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 ...
随机推荐
- C++ 条件与 If 语句:掌握逻辑判断与流程控制精髓
C++ 条件和 If 语句 您已经知道 C++ 支持数学中的常见逻辑条件: 小于:a < b 小于或等于:a <= b 大于:a > b 大于或等于:a >= b 等于:a = ...
- Go 语言基础:包、函数、语句和注释解析
一个 Go 文件包含以下几个部分: 包声明 导入包 函数 语句和表达式 看下面的代码,更好地理解它: 例子 package main import "fmt" func main( ...
- 【直播预告】HarmonyOS 极客松赋能直播第六期:产品创新从哪里来?
- 直播预告丨Hello HarmonyOS进阶课程第三课——游戏开发实践
为了帮助初识HarmonyOS的开发者快速入门,我们曾推出Hello HarmonyOS系列一共5期课程,从最基础的配置IDE和创建Hello World开始,详细介绍HarmonyOS基础.开发环境 ...
- Prometheus之node_exporter安装
一.简介 node_exporter用来安装到被监控的主机上,暴露被监控主机的指标数据,服务器端基于http协议调用的端口9100(默认)来获取被监控服务器信息. 二.安装部署 下载地址 https: ...
- centos7或者centos8下安装google-chrome谷歌浏览器 亲测成功 20220302
第一步: wget https://dl.google.com/linux/direct/google-chrome-stable_current_x86_64.rpm 第二步: 安装 Google ...
- docker 应用篇————具名挂载和匿名挂载[十三]
前言 简单整理一下具名挂载和匿名挂载. 正文 来看一下匿名挂载. 这里-v指定了容器内部的路径,但是没有指定容器外部的路径,那么挂载到了什么地方. 用inspect 查看一下. 挂载到这个位置了. 然 ...
- Node 中的 Stream ?应用场景?
一.是什么 流(Stream),是一种数据传输手段,是端到端信息交换的一种方式,是有顺序的,是逐块读取数据.处理内容,用于顺序读取输入或写入输出 在很多时候,流(Stream)是字节流(Byte St ...
- vscode使用npm安装依赖报错
1.报错信息 npm ERR! code EPERM npm ERR! syscall open npm ERR! path C:\Node\node_cache_cacache\index-v5\4 ...
- 这10款VS Code神仙插件,嵌入式程序员必备
大家好,我是知微! 嵌入式软件开发工程师平时可能更多的是使用Source Insight.Keil.IAR来阅读代码,写代码. VSCode大家都听说过,功能十分强大,而且免费! 或许是因为这款软件上 ...