WPF自定义FixedColumnGrid布局控件
按照上一节所讲,我已经对布局系统又所了解。接下来我就实现一个布局控件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)));
}

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
对MeasureOverride和ArrangeOverride稍作修改
protected override Size MeasureOverride(Size constraint)
{
//base.MeasureOverride(constraint);
double columnWidth = constraint.Width / this.Columns;//列宽
for (int i = 0; i < this.VisualChildrenCount; i++)
{
UIElement child = (UIElement)this.GetVisualChild(i);
if (child!=null)
{
child.Measure(new Size(constraint.Width / columnWidth, 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;
}
然后就可以在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 RowHeight
{
get { return (int)GetValue(RowHeightProperty); }
set { SetValue(RowHeightProperty, value); }
} // Using a DependencyProperty as the backing store for ColumnHeight. This enables animation, styling, binding, etc...
public static readonly DependencyProperty RowHeightProperty =
DependencyProperty.Register("RowHeight", typeof(int), typeof(FixedColumnGrid), new FrameworkPropertyMetadata(50,FrameworkPropertyMetadataOptions.AffectsMeasure));
同时载将测量和排列重载中的50换成高度
child.Measure(new Size(constraint.Width / 2, this.RowHeight));
...
child.Arrange(new Rect(new Point(0, i / 2 * this.RowHeight), new Size(arrangeBounds.Width / 2, this.RowHeight)));
...
child.Arrange(new Rect(new Point(arrangeBounds.Width / 2, Math.Floor(i / 2d) * this.RowHeight), new Size(arrangeBounds.Width / 2, this.RowHeight)));
...
child.Arrange(new Rect(new Point(offsetColumn * columnWidth, Math.Floor((double)i / this.Columns) * this.RowHeight), new Size(columnWidth, this.RowHeight)));
现在就可以在xaml中使用自定义高度了
<Border Background="Blue">
<local:FixedColumnGrid Columns="2" RowHeight="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 double columnWidth = constraint.Width / this.Columns;//列宽
12 for (int i = 0; i < this.VisualChildrenCount; i++)
13 {
14 UIElement child = (UIElement)this.GetVisualChild(i);
15 if (child!=null)
16 {
17 child.Measure(new Size(constraint.Width / columnWidth, this.RowHeight));
18 }
19 }
20 return constraint;
21 }
22
23 protected override Size ArrangeOverride(Size arrangeBounds)
24 {
25 //base.ArrangeOverride(arrangeBounds);
26 for (int i = 0; i < this.VisualChildrenCount; i++)
27 {
28 UIElement child = (UIElement)this.GetVisualChild(i);
29 if (child!=null)
30 {
31 if (this.Columns == default(int))
32 {
33 if (i % 2 == 0)
34 {
35 child.Arrange(new Rect(new Point(0, i / 2 * this.RowHeight), new Size(arrangeBounds.Width / 2, this.RowHeight)));
36 }
37 else
38 {
39 child.Arrange(new Rect(new Point(arrangeBounds.Width / 2, Math.Floor(i / 2d) * this.RowHeight), new Size(arrangeBounds.Width / 2, this.RowHeight)));
40 }
41 }
42 else
43 {
44 double columnWidth = arrangeBounds.Width / this.Columns;//列宽
45 int offsetColumn = i % this.Columns;//当前单元格处于哪一列
46 child.Arrange(new Rect(new Point(offsetColumn * columnWidth, Math.Floor((double)i / this.Columns) * this.RowHeight), new Size(columnWidth, this.RowHeight)));
47 }
48 }
49 }
50 return arrangeBounds;
51 }
52
53
54
55 public int Columns
56 {
57 get { return (int)GetValue(ColumnsProperty); }
58 set { SetValue(ColumnsProperty, value); }
59 }
60
61 public static readonly DependencyProperty ColumnsProperty =
62 DependencyProperty.Register("Columns", typeof(int), typeof(FixedColumnGrid), new FrameworkPropertyMetadata(default(int),FrameworkPropertyMetadataOptions.AffectsArrange));
63
64
65
66 public int RowHeight
67 {
68 get { return (int)GetValue(RowHeightProperty); }
69 set { SetValue(RowHeightProperty, value); }
70 }
71
72 // Using a DependencyProperty as the backing store for ColumnHeight. This enables animation, styling, binding, etc...
73 public static readonly DependencyProperty RowHeightProperty =
74 DependencyProperty.Register("RowHeight", typeof(int), typeof(FixedColumnGrid), new FrameworkPropertyMetadata(50,FrameworkPropertyMetadataOptions.AffectsMeasure));
75
76
77 }
WPF自定义FixedColumnGrid布局控件的更多相关文章
- WPF自定义选择年月控件详解
本文实例为大家分享了WPF自定义选择年月控件的具体代码,供大家参考,具体内容如下 封装了一个选择年月的控件,XAML代码: 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 ...
- WPF中的布局控件(转)
WPF中使用Panel进行页面布局,Panel是一个抽象类,它作为所有Panel面板控件的基类.Panel并不是继承自Control类,而是直接从FrameworkElement继承.看Panel的继 ...
- WPF 自定义DateControl DateTime控件
自定义日期控件,月份选择.如下是日期的一些效果图. 具体的样式.颜色可以根据下面的代码,自己调节即可 1.日期控件的界面 <UserControl x:Class="WpfApp ...
- WPF 自定义DateControl DateTime控件(转)
自定义日期控件,月份选择.如下是日期的一些效果图. 具体的样式.颜色可以根据下面的代码,自己调节即可 1.日期控件的界面 <UserControl x:Class="WpfApp ...
- WPF自定义DataGrid分页控件
新建Custom Control,名:PagingDataGrid 打开工程下面的Themes\Generic.xaml xaml里面代码替换如下 <Style x:Key="{x:T ...
- WPF自定义数字输入框控件
要求:只能输入数字和小数点,可以设置最大值,最小值,小数点前长度,小数点后长度(支持绑定设置): 代码如下: using System; using System.Collections.Generi ...
- WPF自定义轮播控件
闲得蛋疼做了一个WPF制作轮播动画(随机动画),勉强可以看,写个随笔留个脚印. 效果图:
- WPF自定义下拉控件
可以搜索的下拉条 using System; using System.Collections; using System.Collections.Generic; using System.Coll ...
- wpf布局控件总结
首先要认识到wpf所有的布局控件都继承自Panel类,Panel类又继承自其他类.继承关系如下: 一.StackPanel布局面板 1.该面板在单行或者单列中以堆栈的形式放置其子元素. 默认情况下,S ...
- [WPF自定义控件库]简单的表单布局控件
1. WPF布局一个表单 <Grid Width="400" HorizontalAlignment="Center" VerticalAlignment ...
随机推荐
- OpenHarmony Liteos_A内核之iperf3移植心得
一.iperf3工作原理 iperf3主要的功能是测试基于特定路径的带宽,在客户端和服务器端建立连接(三次握手)后,客户端发送一定大小的数据报并记下发送的时间,或者客户端在一定的时间内发送数据并记下发 ...
- 教你构建一个优秀的SD Prompt
构建一个优秀的Prompt 在使用Stable Diffusion AI时,构建一个有效的提示(Prompt)是至关重要的第一步.这个过程涉及到创造性的尝试和对AI行为的理解.这里我会对如何构建一个好 ...
- JMeter接口性能测试工具
博客地址:https://blog.csdn.net/lovesoo/article/details/78579547
- 浅析eTS的起源和演进
原文:https://mp.weixin.qq.com/s/N2RPeboN8Fj0-8wBMZJ-7w,点击链接查看更多技术内容. 引言 Mozilla创造了JS,Microsoft创建了TS,Hu ...
- huggingface vit训练CIFAR10数据集代码 ,可以改dataset训练自己的数据
上代码,使用hugging face fineturn vit模型 自己写的代码 from transformers import ViTImageProcessor, ViTForImageClas ...
- 《Effective C#》系列之(六)——提高多线程的性能
一.综述 <Effective C#>中提高多线程性能的方法主要有以下几点: 避免锁竞争:锁的使用会导致线程阻塞,从而影响程序的性能.为了避免锁竞争,可以采用无锁编程技术,如CAS(Com ...
- 日志架构演进:从集中式到分布式的Kubernetes日志策略
当我们没有使用云原生方案部署应用时采用的日志方案往往是 ELK 技术栈. 这套技术方案比较成熟,稳定性也很高,所以几乎成为了当时的标配. 可是随着我们使用 kubernetes 步入云原生的时代后, ...
- 力扣393(java)-UTF-8编码验证(中等)
题目: 给定一个表示数据的整数数组 data ,返回它是否为有效的 UTF-8 编码. UTF-8 中的一个字符可能的长度为 1 到 4 字节,遵循以下的规则: 对于 1 字节 的字符,字节的第一位设 ...
- RocketMQ 之 IoT 消息解析:物联网需要什么样的消息技术?
前言: 从初代开源消息队列崛起,到 PC 互联网.移动互联网爆发式发展,再到如今 IoT.云计算.云原生引领了新的技术趋势,消息中间件的发展已经走过了 30 多个年头. 目前,消息中间件在国内许多行业 ...
- 一文搞懂 SAE 日志采集架构
简介: 本文将着重介绍了 SAE 提供了多种日志采集方案,以及相关的架构,场景使用特点,点击下文,立即查看吧- 作者:牛通(奇卫) 日志,对于一个程序的重要程度不言而喻.无论是作为排查问题的手段, ...