按照上一节所讲,我已经对布局系统又所了解。接下来我就实现一个布局控件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布局控件的更多相关文章

  1. WPF自定义选择年月控件详解

    本文实例为大家分享了WPF自定义选择年月控件的具体代码,供大家参考,具体内容如下 封装了一个选择年月的控件,XAML代码: 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 ...

  2. WPF中的布局控件(转)

    WPF中使用Panel进行页面布局,Panel是一个抽象类,它作为所有Panel面板控件的基类.Panel并不是继承自Control类,而是直接从FrameworkElement继承.看Panel的继 ...

  3. WPF 自定义DateControl DateTime控件

    自定义日期控件,月份选择.如下是日期的一些效果图. 具体的样式.颜色可以根据下面的代码,自己调节即可    1.日期控件的界面 <UserControl x:Class="WpfApp ...

  4. WPF 自定义DateControl DateTime控件(转)

    自定义日期控件,月份选择.如下是日期的一些效果图. 具体的样式.颜色可以根据下面的代码,自己调节即可    1.日期控件的界面 <UserControl x:Class="WpfApp ...

  5. WPF自定义DataGrid分页控件

    新建Custom Control,名:PagingDataGrid 打开工程下面的Themes\Generic.xaml xaml里面代码替换如下 <Style x:Key="{x:T ...

  6. WPF自定义数字输入框控件

    要求:只能输入数字和小数点,可以设置最大值,最小值,小数点前长度,小数点后长度(支持绑定设置): 代码如下: using System; using System.Collections.Generi ...

  7. WPF自定义轮播控件

     闲得蛋疼做了一个WPF制作轮播动画(随机动画),勉强可以看,写个随笔留个脚印.  效果图:

  8. WPF自定义下拉控件

    可以搜索的下拉条 using System; using System.Collections; using System.Collections.Generic; using System.Coll ...

  9. wpf布局控件总结

    首先要认识到wpf所有的布局控件都继承自Panel类,Panel类又继承自其他类.继承关系如下: 一.StackPanel布局面板 1.该面板在单行或者单列中以堆栈的形式放置其子元素. 默认情况下,S ...

  10. [WPF自定义控件库]简单的表单布局控件

    1. WPF布局一个表单 <Grid Width="400" HorizontalAlignment="Center" VerticalAlignment ...

随机推荐

  1. 深入理解 Spring IoC 和 DI:掌握控制反转和依赖注入的精髓

    在本文中,我们将介绍 IoC(控制反转)和 DI(依赖注入)的概念,以及如何在 Spring 框架中实现它们. 什么是控制反转? 控制反转是软件工程中的一个原则,它将对象或程序的某些部分的控制权转移给 ...

  2. 用户触达难?流失率高?HMS Core预测服务和智能运营,助你提前掌握营销时机,解决此难题。

    用户流失了,触达难? 活动做了那么多,转化仍然很低? 运营也需要提前思考,预测用户动向,提前精准触达,才能事半功倍.结合HMS Core分析服务的预测服务和智能运营,洞察营销时机,实时落地营销策略,提 ...

  3. MyBatis 应用的组成

    王有志,一个分享硬核Java技术的互金摸鱼侠 加入Java人的提桶跑路群:共同富裕的Java人 大家好,我是王有志.在上一篇文章的最后,我们写了一个简单的例子,今天我们就通过这个例子来看一看一个标准的 ...

  4. HarmonyOS Connect FAQ第三期

    原文:https://mp.weixin.qq.com/s/YpI9-k4yQvNhaMfg7Li82g,点击链接查看更多技术内容.   在开发HarmonyOS Connect生态产品时,你是否对设 ...

  5. 高云1N1开发板高云gowin软件使用教程

    国产FPGA是最近几年起来的产品,具有性价比高特点.高云FPGA,很多用户都用在LED,电机控制,PLC设备上. 开发板子采用GW1N-LV1QN48C6/I5 FPGA器件.具有低功耗,瞬时启动,高 ...

  6. 力扣15(Java)-三数之和(中等)

    题目: 给你一个整数数组 nums ,判断是否存在三元组 [nums[i], nums[j], nums[k]] 满足 i != j.i != k 且 j != k ,同时还满足 nums[i] + ...

  7. 01_Vue技术-Hello案例分析

    初始vue:       1.想让vue工作,就必须创建一个Vue实例,且要传入一个配置对象:       2.root容器里的代码依然符合html规范,只不过混入了一些特殊的vue语法:       ...

  8. EasyNLP带你玩转CLIP图文检索

    简介: 本文简要介绍CLIP的技术解读,以及如何在EasyNLP框架中玩转CLIP模型. 作者:熊兮.章捷.岑鸣.临在 导读 随着自媒体的不断发展,多种模态数据例如图像.文本.语音.视频等不断增长,创 ...

  9. 基于 RocketMQ Prometheus Exporter 打造定制化 DevOps 平台

    简介: 本文将对 RocketMQ-Exporter 的设计实现做一个简单的介绍,读者可通过本文了解到 RocketMQ-Exporter 的实现过程,以及通过 RocketMQ-Exporter 来 ...

  10. Fluid给数据弹性一双隐形的翅膀 (1) -- 自定义弹性伸缩

    简介: 弹性伸缩作为Kubernetes的核心能力之一,但它一直是围绕这无状态的应用负载展开.而Fluid提供了分布式缓存的弹性伸缩能力,可以灵活扩充和收缩数据缓存. 它基于Runtime提供了缓存空 ...