按照上一节所讲,我已经对布局系统又所了解。接下来我就实现一个布局控件FixedColumnGrid。
1.基础版
布局控件机制如下,FixedColumnGrid将子控件按照水平排列,每行满两列后换行。每个控件大小相同,高度固定为50。
第一步,先重载测量和排列方法
protected override Size MeasureOverride(Size constraint) { //base.MeasureOverride(constraint); return constraint; } protected override Size ArrangeOverride(Size arrangeBounds) { //base.ArrangeOverride(arrangeBounds); return arrangeBounds; }
根据机制,我们需要自己决定子控件尺寸,也就是需要自己测量和排列子控件。所以我们就不需要祖先的递归了,将由我们自己手动递归,所有注释掉base调用。
第二步,测量子控件
虽然我们可以直接把constraint传过去,但我们根据布局机制,尽可能的少传递可用空间给子控件。所以我们接下来在MeasureOverride添加如下测量代码。
//base.MeasureOverride(constraint); for (int i = 0; i < this.VisualChildrenCount; i++) { UIElement child = (UIElement)this.GetVisualChild(i); if (child!=null) { child.Measure(new Size(constraint.Width / 2, 50)); } } return constraint;
第三步,测量子控件后,根据其期望尺寸,排列子控件,因此,接下来在ArrangeOverride中添加排列代码。
//base.ArrangeOverride(arrangeBounds); for (int i = 0; i < this.VisualChildrenCount; i++) { UIElement child = (UIElement)this.GetVisualChild(i); if (child!=null) { if (i % 2 == 0) { child.Arrange(new Rect(new Point(0, Math.Floor(i / 2d) * 50), child.DesiredSize)); } else { child.Arrange(new Rect(new Point(arrangeBounds.Width / 2, i / 2 * 50), child.DesiredSize)); } } } return arrangeBounds;
现在,我们已经可以试着看看效果了。先生成一下项目,再到mainWindow.xaml中添加一个FixedColumnGrid控件
<Border Background="Blue"> <local:FixedColumnGrid> <Button Content="btn" Width="500" Height="100" HorizontalAlignment="Center" VerticalAlignment="Center"/> <Button Content="btn" Width="500" Height="100"/> <Button Content="btn" Width="500" Height="100"/> <Button Content="btn"/> </local:FixedColumnGrid> </Border>
可以看到一些问题。第一个button是手动居中的,第二个就没有居中了。这时因为第二个button期望的控件大于FixedColumnGrid理应给他的控件,所以尽管他的期望尺寸被限制在了FixedColumnGrid所给的尺寸(400,50),但其真是大小却要大些,所以看起来第二个button的内容就没有居中了。
第4个button没有设置尺寸,又太小了。这时因为我们排列button时,使用的时其期望大小,而button没有手动指定width和height时,其期望大小是根据内容定的。
因此我改进了下排列,不根据子控件期望尺寸排列,而是根据我们根据布局机制规定的尺寸排列,现在得到了想要的效果。
if (i % 2 == 0) { child.Arrange(new Rect(new Point(0, i/2 * 50), new Size(arrangeBounds.Width / 2, 50))); } else { child.Arrange(new Rect(new Point(arrangeBounds.Width / 2, Math.Floor(i / 2d) * 50), new Size(arrangeBounds.Width / 2, 50))); }
对MeasureOverride和ArrangeOverride稍作修改
protected override Size MeasureOverride(Size constraint) { //base.MeasureOverride(constraint); for (int i = 0; i < this.VisualChildrenCount; i++) { UIElement child = (UIElement)this.GetVisualChild(i); if (child!=null) { child.Measure(new Size(constraint.Width / 2, 50)); } } return constraint; } protected override Size ArrangeOverride(Size arrangeBounds) { //base.ArrangeOverride(arrangeBounds); for (int i = 0; i < this.VisualChildrenCount; i++) { UIElement child = (UIElement)this.GetVisualChild(i); if (child!=null) { if (this.Columns == default(int)) { if (i % 2 == 0) { child.Arrange(new Rect(new Point(0, i / 2 * 50), new Size(arrangeBounds.Width / 2, 50))); } else { child.Arrange(new Rect(new Point(arrangeBounds.Width / 2, Math.Floor(i / 2d) * 50), new Size(arrangeBounds.Width / 2, 50))); } } else { double columnWidth = arrangeBounds.Width / this.Columns;//列宽 int offsetColumn = i % this.Columns;//当前单元格处于哪一列 child.Arrange(new Rect(new Point(offsetColumn * columnWidth, Math.Floor((double)i / this.Columns) * 50), new Size(columnWidth, 50))); } } } return arrangeBounds; }
2.可扩展列数版
既然我们可以排两列,哪能不能排1列,2列,3列呢。我决定继续进行增强。
首先,我们要能定义列数,于是我增加了一个依赖属性Columns。
public int Columns { get { return (int)GetValue(ColumnsProperty); } set { SetValue(ColumnsProperty, value); } } public static readonly DependencyProperty ColumnsProperty = DependencyProperty.Register("Columns", typeof(int), typeof(FixedColumnGrid), new FrameworkPropertyMetadata(default(int),FrameworkPropertyMetadataOptions.AffectsArrange));
为了在更改列数时能重新排列,所以还增加了AffectsArrange
然后就可以在xaml中定义列数
<Border Background="Blue"> <local:FixedColumnGrid Columns="1"> <Button Content="btn" Width="500" Height="100" HorizontalAlignment="Center" VerticalAlignment="Center"/> <Button Content="btn" Width="500" Height="100"/> <Button Content="btn" Width="500" Height="100" HorizontalAlignment="Left"/> <Button Content="btn"/> </local:FixedColumnGrid> </Border>
这里没有居中的原因button自身HorizontalAlignment属性会影响到排列
3.可自定义高度版
列数都能调整了,哪每行高度也能否调整呢,这倒是简单,只是增加一个RowHeight依赖属性罢了。
public int ColumnHeight { get { return (int)GetValue(ColumnHeightProperty); } set { SetValue(ColumnHeightProperty, value); } } // Using a DependencyProperty as the backing store for ColumnHeight. This enables animation, styling, binding, etc... public static readonly DependencyProperty ColumnHeightProperty = DependencyProperty.Register("ColumnHeight", typeof(int), typeof(FixedColumnGrid), new FrameworkPropertyMetadata(50,FrameworkPropertyMetadataOptions.AffectsMeasure));
同时载将测量和排列重载中的50换成高度
child.Measure(new Size(constraint.Width / 2, this.ColumnHeight)); ... child.Arrange(new Rect(new Point(0, i / 2 * this.ColumnHeight), new Size(arrangeBounds.Width / 2, this.ColumnHeight))); ... child.Arrange(new Rect(new Point(arrangeBounds.Width / 2, Math.Floor(i / 2d) * this.ColumnHeight), new Size(arrangeBounds.Width / 2, this.ColumnHeight))); ... child.Arrange(new Rect(new Point(offsetColumn * columnWidth, Math.Floor((double)i / this.Columns) * this.ColumnHeight), new Size(columnWidth, this.ColumnHeight)));
现在就可以在xaml中使用自定义高度了
<Border Background="Blue"> <local:FixedColumnGrid Columns="2" ColumnHeight="120"> <Button Content="btn" Width="500" Height="100" HorizontalAlignment="Center" VerticalAlignment="Center"/> <Button Content="btn" Width="500" Height="100"/> <Button Content="btn"/> <Button Content="btn"/> <Button Content="btn"/> <Button Content="btn"/> </local:FixedColumnGrid> </Border>
FixedColumnGrid完整代码
1 public partial class FixedColumnGrid : Panel 2 { 3 public FixedColumnGrid() 4 { 5 InitializeComponent(); 6 } 7 8 protected override Size MeasureOverride(Size constraint) 9 { 10 //base.MeasureOverride(constraint); 11 for (int i = 0; i < this.VisualChildrenCount; i++) 12 { 13 UIElement child = (UIElement)this.GetVisualChild(i); 14 if (child!=null) 15 { 16 child.Measure(new Size(constraint.Width / 2, this.ColumnHeight)); 17 } 18 } 19 return constraint; 20 } 21 22 protected override Size ArrangeOverride(Size arrangeBounds) 23 { 24 //base.ArrangeOverride(arrangeBounds); 25 for (int i = 0; i < this.VisualChildrenCount; i++) 26 { 27 UIElement child = (UIElement)this.GetVisualChild(i); 28 if (child!=null) 29 { 30 if (this.Columns == default(int)) 31 { 32 if (i % 2 == 0) 33 { 34 child.Arrange(new Rect(new Point(0, i / 2 * this.ColumnHeight), new Size(arrangeBounds.Width / 2, this.ColumnHeight))); 35 } 36 else 37 { 38 child.Arrange(new Rect(new Point(arrangeBounds.Width / 2, Math.Floor(i / 2d) * this.ColumnHeight), new Size(arrangeBounds.Width / 2, this.ColumnHeight))); 39 } 40 } 41 else 42 { 43 double columnWidth = arrangeBounds.Width / this.Columns;//列宽 44 int offsetColumn = i % this.Columns;//当前单元格处于哪一列 45 child.Arrange(new Rect(new Point(offsetColumn * columnWidth, Math.Floor((double)i / this.Columns) * this.ColumnHeight), new Size(columnWidth, this.ColumnHeight))); 46 } 47 } 48 } 49 return arrangeBounds; 50 } 51 52 53 54 public int Columns 55 { 56 get { return (int)GetValue(ColumnsProperty); } 57 set { SetValue(ColumnsProperty, value); } 58 } 59 60 public static readonly DependencyProperty ColumnsProperty = 61 DependencyProperty.Register("Columns", typeof(int), typeof(FixedColumnGrid), new FrameworkPropertyMetadata(default(int),FrameworkPropertyMetadataOptions.AffectsArrange)); 62 63 64 65 public int ColumnHeight 66 { 67 get { return (int)GetValue(ColumnHeightProperty); } 68 set { SetValue(ColumnHeightProperty, value); } 69 } 70 71 // Using a DependencyProperty as the backing store for ColumnHeight. This enables animation, styling, binding, etc... 72 public static readonly DependencyProperty ColumnHeightProperty = 73 DependencyProperty.Register("ColumnHeight", typeof(int), typeof(FixedColumnGrid), new FrameworkPropertyMetadata(50,FrameworkPropertyMetadataOptions.AffectsMeasure)); 74 75 76 }View Code
标签:控件,自定义,int,50,arrangeBounds,child,new,WPF,Size From: https://www.cnblogs.com/ggtc/p/18153855