2

我有一个自定义画布,它应该用最大数量的 Tile 对象填充自己。

如果它被调整为更大,它会用额外的瓷砖填充空白空间。如果它被调整为更小,它会删除不再可见的瓷砖。

这仅在我非常缓慢地调整大小时才有效,否则一切都会开始混乱。

正确图片 http://imageshack.us/a/img585/105/50186779.png 正确图片 http://imageshack.us/a/img267/3179/76648216.png

我肯定做错了什么,下面的代码是画布:

using System;
using System.Windows;
using System.Windows.Controls;

namespace WpfApplication1.View
{
    public class TileCanvas : Canvas
    {
        #region Fields

        private Boolean _firstCreation;
        private Double _heightDifference;
        private Double _widthDifference;

        #endregion // Fields

        #region Properties

        public static readonly DependencyProperty TileSizeProperty =
            DependencyProperty.Register("TileSize", typeof (Int32), typeof (TileCanvas),
                                        new PropertyMetadata(30));

        public Int32 TileSize
        {
            get { return (Int32) GetValue(TileSizeProperty); }
            set { SetValue(TileSizeProperty, value); }
        }

        public Int32 Columns { get; private set; }
        public Int32 Rows { get; private set; }

        #endregion // Properties

        #region Constructors

        public TileCanvas()
        {
            _firstCreation = true;
        }

        #endregion // Constructors

        #region Methods

        #region Public

        #endregion // Methods - Public

        #region Protected

        /// <summary>
        /// Gets called when the rendering size of this control changes.
        /// </summary>
        /// <param name="sizeInfo">Information on the size change.</param>
        protected override void OnRenderSizeChanged(SizeChangedInfo sizeInfo)
        {
            Console.WriteLine("{0}x{1}", sizeInfo.NewSize.Width, sizeInfo.NewSize.Height);

            _widthDifference += sizeInfo.NewSize.Width - sizeInfo.PreviousSize.Width;
            _heightDifference += sizeInfo.NewSize.Height - sizeInfo.PreviousSize.Height;

            int columnDifference = 0;
            int rowDifference = 0;

            if (_widthDifference > TileSize || _widthDifference < TileSize*-1)
            {
                columnDifference = ((Int32) _widthDifference)/TileSize;
                Columns += columnDifference;
                _widthDifference = 0;
            }

            if (_heightDifference > TileSize || _heightDifference < TileSize*-1)
            {
                rowDifference = ((Int32) _heightDifference)/TileSize;
                Rows += rowDifference;
                _heightDifference = 0;
            }

            UpdateTileSet(columnDifference, rowDifference);
        }

        #endregion // Methods - Protected

        #region Private

        /// <summary>
        /// Updates the number of tiles if the control changed in size.
        /// </summary>
        private void UpdateTileSet(Int32 columnChange, Int32 rowChange)
        {
            // Exit if there's no practical change
            if (columnChange == 0 && rowChange == 0)
            {
                return;
            }

            // Delete all tiles that fall out on vertical resize
            if (rowChange < 0)
            {
                for (var row = Rows; row > Rows + rowChange; row--)
                {
                    for (var column = 0; column < Columns; column++)
                    {
                        Children.RemoveRange(GetListIndex(0, Rows), Columns);
                    }
                }
            }

            // Delete all tiles that fall out on horizontal resize
            if (columnChange < 0)
            {
                for (var column = Columns; column > Columns + columnChange; column--)
                {
                    for (var row = 0; row < Rows; row++)
                    {
                        Children.RemoveAt(GetListIndex(column, row));
                    }
                }
            }

            // Fill new rows with tiles on vertical resize
            if (rowChange > 0)
            {
                for (var row = Rows - rowChange; row < Rows; row++)
                {
                    for (var column = 0; column < Columns; column++)
                    {
                        var tile = new Tile(column, row);

                        Point position = GetCanvasPosition(column, row);
                        var index = GetListIndex(column, row);

                        SetLeft(tile, position.X);
                        SetTop(tile, position.Y);

                        Children.Insert(index, tile);
                    }
                }
            }

            // The first population is a special case that can be handled
            // by filling rows only.
            if (_firstCreation)
            {
                _firstCreation = false;
                return;
            }

            // Fill new columns with tiles on horizontal resize
            if (columnChange > 0)
            {
                for (var column = Columns - columnChange; column < Columns; column++)
                {
                    for (var row = 0; row < Rows; row++)
                    {
                        var tile = new Tile(column, row);

                        Point position = GetCanvasPosition(column, row);
                        var index = GetListIndex(column, row);

                        SetLeft(tile, position.X);
                        SetTop(tile, position.Y);

                        Children.Insert(index, tile);
                    }
                }
            }
        }

        /// <summary>
        /// Returns the index a tile should occupy based on its column and row.
        /// </summary>
        /// <param name="column">The column in which this tile resides.</param>
        /// <param name="row">The row in which this tile resides.</param>
        /// <returns></returns>
        private Int32 GetListIndex(Int32 column, Int32 row)
        {
            return row*Columns + column;
        }

        /// <summary>
        /// Returns the coordinates of a specific position.
        /// </summary>
        /// <param name="column">The column the position is in.</param>
        /// <param name="row">The row the position is in.</param>
        /// <returns></returns>
        private Point GetCanvasPosition(Int32 column, Int32 row)
        {
            var positionX = column*TileSize;
            var positionY = row*TileSize;

            return new Point(positionX, positionY);
        }

        #endregion // Methods - Private

        #endregion // Methods
    }
}

这是 Tile.cs

using System;
using System.Windows.Controls;

namespace WpfApplication1.View
{
    /// <summary>
    /// Interaction logic for Tile.xaml
    /// </summary>
    public partial class Tile : Border
    {
        public Tile(Int32 column, Int32 row)
        {
            InitializeComponent();


            Textfield.Text = String.Format("{0}x{1}", column, row);
        }
    }
}

和 Tile.xaml

<Border x:Class="WpfApplication1.View.Tile"
             xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
             xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
             xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006" 
             xmlns:d="http://schemas.microsoft.com/expression/blend/2008" 
             mc:Ignorable="d" 
             Width="40" Height="40">
    <TextBlock x:Name="Textfield" FontSize="8" HorizontalAlignment="Center" VerticalAlignment="Top"/>
</Border>

问题出在哪里?

4

1 回答 1

1

这是有关如何在 WPF 控件中的自定义位置放置元素的良好信息来源:http: //codeblitz.wordpress.com/2009/03/20/wpf-auto-arrange-animated-panel/

我不知道它是否适用于画布,但将代码放在 Measure/Arrange Override 方法中可能会产生更好的结果。

于 2013-01-04T00:29:07.637 回答