首页 > 其他分享 >WPF自定义FixedColumnGrid布局控件

WPF自定义FixedColumnGrid布局控件

时间:2024-04-24 20:59:16浏览次数:26  
标签:控件 自定义 int 50 arrangeBounds child new WPF Size

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

相关文章

  • 利用自定义流程表单开发的优势,实现流程化发展!
    要想实现流程化发展,通过低代码技术平台以及自定义流程表单开发的力量,可以将效率大大提升,便于企业进行数字化管理。拥有够灵活、可维护、易操作等优势特点的低代码技术平台拥有强劲的市场竞争力,逐渐在市场中脱颖而出,如果将自定义流程表单开发的优势特点发挥极致,就能快速实现流程化......
  • vis.js自定义标签3d图形
    代码案例<!DOCTYPEhtml><html><head><title>Graph3Ddemo</title><style>body{font:10ptarial;}</style><scripttype="text/javascript"src=......
  • 【Java注解】自定义注解的简单demo
    需求场景对于特定字段进行脱敏实现步骤首先创建注解@interface1importjava.lang.annotation.ElementType;2importjava.lang.annotation.Retention;3importjava.lang.annotation.RetentionPolicy;4importjava.lang.annotation.Target;56@Retention(Reten......
  • 界面控件DevExpress VCL v24.1预览 - 支持RAD Studio 12.1、图表新功能
    DevExpressVCL Controls是Devexpress公司旗下最老牌的用户界面套包,所包含的控件有:数据录入、图表、数据分析、导航、布局等。该控件能帮助您创建优异的用户体验,提供高影响力的业务解决方案,并利用您现有的VCL技能为未来构建下一代应用程序。我们距离下一个主要更新(v24.1)还有几......
  • 【转】[C#][WPF] ContentControl 和 ContentPresenter 的区别
    转自:阿里的通义灵码在 https://blog.csdn.net/yao_hou/article/details/134431366需要付费阅读,所以从阿里的通义灵码来寻找答案:WPF中的 ContentControl 和 ContentPresenter 虽然都涉及内容展示,但它们扮演着不同的角色,具有各自的特性和用途。以下是两者之间的主要区......
  • Django 自定义创建密码重置确认页面
    要实现上述功能,你需要修改模板文件以添加“忘记密码”链接,并创建新的视图函数来处理密码丢失修改页面、验证和密码修改。下面是你可以进行的步骤:1.修改模板文件在登录页面的表单下方添加一个“忘记密码”链接:<divclass="form-grouptext-center"><buttontype="submit"......
  • 5款开源、美观、强大的WPF UI组件库
    前言经常看到有小伙伴在DotNetGuide技术社区交流群里提问:WPF有什么好用或者好看的UI组件库?,今天大姚给大家分享5款开源、美观、强大、简单易用的WPFUI组件库。WPF介绍WPF是一个强大的桌面应用程序框架,用于构建具有丰富用户界面的Windows应用。它提供了灵活的布局、数据绑定......
  • 自定义Mini-Tomcat之动态资源返回实现
    在自定义Mini-Tomcat之基本架构V3需求中实现了访问动态资源,该实现案例中的动态资源通过两个配置文件获取:server.xml:web.xml:下面是对这俩配置文件的解析保存数据:......
  • 24-自定义持久层框架
    1.JDBC的使用问题代码示例:publicclassJDBCTest{publicstaticvoidmain(String[]args){Connectionconnection=null;PreparedStatementpreparedStatement=null;ResultSetresultSet=null;try{//加载数......
  • 自定义Mini-Tomcat(一)
    在Spring+ / SpringMVC中使用到了Tomcat。那么Tomcat提供什么功能呢?访问使用HTTP协议,所以它首先是一个HTTP服务器(关于HTTP协议资料较多自行查找);其次MVC提供servlet部署在tomcat中进行实际的业务操作,所以它还是一个Servlet容器。下面定义一个Mini-Tomcat提供以上两个......