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 ...
随机推荐
- SQL 的 AND、OR 和 NOT 运算符:条件筛选的高级用法
AND 运算符 SQL的AND运算符用于根据多个条件筛选记录,确保所有条件都为TRUE才返回记录.下面是AND运算符的基本语法: SELECT column1, column2, ... FROM t ...
- 并发编程面试必备之ConcurrentHashMap源码解析
ConcurrentHashMap在我的面试生涯中,10次有8次是会被问到的,记得刚毕业那会,被问到ConcurrentHashMap源码的无助与苦涩,无奈只能网上找了一些教程,背一背,才算是蒙混过关 ...
- C# Winform Socket点对点通信
前言 Socket的英文原义是"孔"或"插座",其实在网络编程中Socket就是这个意思,就像我们打电话,要首先知道对方的手机号一样,这个手机号就相当于一个So ...
- HarmonyOS SDK开放能力,服务鸿蒙生态建设,打造优质应用体验
华为开发者大会2023(HDC.Together)于8月4日至6日在东莞松山湖举行,在HarmonyOS端云开放能力技术分论坛上,华为为广大开发者们介绍了HarmonyOS SDK开放能力在基础开发架 ...
- Python设计模式----2.工厂模式
工厂方法模式是简单工厂模式的衍生,解决了许多简单工厂模式的问题 首先完全实现'开-闭 原则',实现了可扩展.其次更复杂的层次结构,可以应用于产品结果复杂的场合. 工厂方法模式的对简单工厂模式进行了抽象 ...
- c# countDownEvent类
前言 把异步先总结完吧. countDownEvent 这东西是干什么的呢? 比如说我们比赛跑步,我们需要得出的是第一二三名得出后就可以先统计出来,因为比较重要,后面没有获得获奖名次的可以后续统计出来 ...
- opensips使用drouting进行路由
操作系统 :CentOS 7.6_x64 opensips版本:2.4.9 drouting是Dynamic Routing(动态路由)的缩写,该模块可为特定呼叫选择(基于多个条件)最佳网关.今天整理 ...
- 【Oracle】Oracle数据库,第N大数据取值
Oracle数据库,第N大数据取值 没想到力扣还有数据库的练习,正好本菜鸡跑过来练手 要显示第二大的数据可以使用order by进行排序,然后用limit对显示的数据进行限制,limit1,1,以此来 ...
- java应用提速(速度与激情)
简介: 本文将阐述通过基础设施与工具的改进,实现从构建到启动全方面大幅提速的实践和理论. 作者 | 阿里巴巴CTO技术来源 | 阿里开发者公众号 联合作者:道延 微波 沈陵 梁希 大熊 断岭 北纬 未 ...
- Schedulerx2.0支持应用级别资源管理和任务优先级
1. 前言 Schedulerx2.0是一套分布式的任务调度+计算框架.作为一套分布式计算引擎,用户经常需要资源管理的需求,当前schedulerx仅仅支持单个任务实例的管控(比如单机子任务并发数.拉 ...