概述

前面 New UWP Community Toolkit 文章中,我们对 2.2.0 版本的重要更新做了简单回顾,其中简单介绍了 Staggered panel,本篇我们结合代码详细讲解  Staggered panel 的实现。

Staggered panel 是一种交错排列的面板控件,允许面板中的 item 以非整齐排列的方式排列,每个 item 会被添加到当前占用空间最小的列。这种排列方式,非常适用于图片类,新闻资讯类的应用,官方示例展示如下图:

Source: https://github.com/Microsoft/UWPCommunityToolkit/blob/master/Microsoft.Toolkit.Uwp.UI.Controls/StaggeredPanel/StaggeredPanel.cs

Doc: https://docs.microsoft.com/zh-cn/windows/uwpcommunitytoolkit/controls/staggeredpanel

Namespace: Microsoft.Toolkit.Uwp.UI.Controls; Nuget: Microsoft.Toolkit.Uwp.UI.Controls;

开发过程

代码分析

StaggeredPanel 类继承自 Panel类,我们先来看看它的构成:

  • public static 依赖属性:PaddingProperty, DesiredColumnWidthProperty
  • public 变量:Padding, DesiredColumnWidth
  • private 变量:_columnWidth
  • public 方法:StaggeredPanel()
  • protected override 方法:MeasureOverride(availableSize), ArrangeOverride(finalSize)
  • private 方法:GetColumnIndex(columnHeights), OnHorizontalAlignmentChanged(sender, dp)
  • private static 方法:OnDesiredColumnWidthChanged(d, e), OnPaddingChanged(d, e)

我们先来看一下 StaggeredPanel 中可在调用类中获取、设置和绑定的两个依赖属性:

  • DesiredColumnWidth - 获取和设置 StaggeredPanel 内 Item 期望列宽度的属性,默认值宽度是 250d;
  • Padding - 获取和设置 StaggeredPanel 内 Item padding 属性,默认值是 Thickness 的默认值 (0,0,0,0),它也是本次 V2.2.0 更新加入的内容
public static readonly DependencyProperty DesiredColumnWidthProperty = DependencyProperty.Register(nameof(DesiredColumnWidth),typeof(double),typeof(StaggeredPanel),new PropertyMetadata(250d, OnDesiredColumnWidthChanged));public static readonly DependencyProperty PaddingProperty = DependencyProperty.Register(nameof(Padding),typeof(Thickness),typeof(StaggeredPanel),new PropertyMetadata(default(Thickness), OnPaddingChanged));

而这两个依赖属性注册的 On***Changed 如下,获取当前 StaggeredPanel 后,强制触发一次 Measure 的重新计算:

private static void OnDesiredColumnWidthChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
{var panel = (StaggeredPanel)d;panel.InvalidateMeasure();
}private static void OnPaddingChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
{var panel = (StaggeredPanel)d;panel.InvalidateMeasure();
}

接下来看一下 StaggeredPanel 的类构造方法:

可以看到,构造方法中注册了一个属性变化后的回调事件,针对 Panel.HorizontalAlignmentProperty 的变化,注册了 OnHorizontalAlignmentChanged 方法,这个方法的功能也很简单,就是强制触发一次 Measure 计算。

public StaggeredPanel()
{RegisterPropertyChangedCallback(Panel.HorizontalAlignmentProperty, OnHorizontalAlignmentChanged);
}

private void OnHorizontalAlignmentChanged(DependencyObject sender, DependencyProperty dp)
{InvalidateMeasure();
}

然后来看两个 override 方法:MeasureOverride(availableSize) 和 ArrangeOverride(finalSize)

MeasureOverride(availableSize) :

该方法作用是传入可用的尺寸,基于其对子元素大小的计算确定它在布局期间所需要的尺寸,我们来看一下具体实现过程:

1. 根据 availableSize,去掉 Padding 对应方向的值,获得新的 availableSize,也就是子元素可用的尺寸;

2. 在期望列宽和可用宽度间获得正确的列宽,根据列宽计算当前布局中可用的列数;如果当前控件的横向对齐方式对拉伸,重新设置列宽,这时列宽实际就是期望列宽度;

3. 遍历 panel 中的 children,根据 GetColumnIndex(columnHeights) 方法传回指定 child 的列索引,计算原则是找到 columnHeights 数组中最小值,返回索引;根据返回的索引,把对应 child 的高度加到 columnHeights 对应索引中,更新  columnHeights 数组中每列的总高度值;

4. 在 columnHeights 数组中 ,找到最大值,返回新的尺寸:宽度为可用尺寸的宽度,高度为列数组的最大值;可以看出,这个尺寸就是根据子元素计算出的 panel 需要的空间大小;

protected override Size MeasureOverride(Size availableSize)
{availableSize.Width = availableSize.Width - Padding.Left - Padding.Right;availableSize.Height = availableSize.Height - Padding.Top - Padding.Bottom;_columnWidth = Math.Min(DesiredColumnWidth, availableSize.Width);int numColumns = (int)Math.Floor(availableSize.Width / _columnWidth);if (HorizontalAlignment == HorizontalAlignment.Stretch){_columnWidth = availableSize.Width / numColumns;}var columnHeights = new double[numColumns];for (int i = 0; i < Children.Count; i++){var columnIndex = GetColumnIndex(columnHeights);var child = Children[i];child.Measure(new Size(_columnWidth, availableSize.Height));var elementSize = child.DesiredSize;columnHeights[columnIndex] += elementSize.Height;}double desiredHeight = columnHeights.Max();return new Size(availableSize.Width, desiredHeight);
}

ArrangeOverride(finalSize):

该方法作用是根据 Measure 方法计算的最终尺寸,实际去排列 Item,排列完成后给出元素实际占用的尺寸,来看一下具体实现过程:

1. 计算列数,根据 panel 横向对齐方式,在居中和靠右时,重新设置横向偏移值,考虑最终宽度和实际元素宽度的偏差;

2. 遍历 panel 的 children,在排列时对 child 宽度做矫正,如果 child 宽度大于列宽,则把宽度调整到列宽,根据宽高比调整高度;

3. 排列后,重新计算当前占用空间的 bounds,调整列数组中对应列的高度;

protected override Size ArrangeOverride(Size finalSize)
{double horizontalOffset = Padding.Left;double verticalOffset = Padding.Top;int numColumns = (int)Math.Floor(finalSize.Width / _columnWidth);if (HorizontalAlignment == HorizontalAlignment.Right){horizontalOffset += finalSize.Width - (numColumns * _columnWidth);}else if (HorizontalAlignment == HorizontalAlignment.Center){horizontalOffset += (finalSize.Width - (numColumns * _columnWidth)) / 2;}var columnHeights = new double[numColumns];for (int i = 0; i < Children.Count; i++){var columnIndex = GetColumnIndex(columnHeights);var child = Children[i];var elementSize = child.DesiredSize;double elementWidth = elementSize.Width;double elementHeight = elementSize.Height;if (elementWidth > _columnWidth){double differencePercentage = _columnWidth / elementWidth;elementHeight = elementHeight * differencePercentage;elementWidth = _columnWidth;}Rect bounds = new Rect(horizontalOffset + (_columnWidth * columnIndex), columnHeights[columnIndex]                                                 + verticalOffset, elementWidth, elementHeight);child.Arrange(bounds);columnHeights[columnIndex] += elementSize.Height;}return base.ArrangeOverride(finalSize);
}

最后来看一下前面 MeasureOverride 和 ArrangeOverride 方法中都用到的 GetColumnIndex(columnHeights) 方法:

这个方法的作用是根据传入的列高度数组,计算当前高度最小的列索引;这也是 StaggeredPanel 可以实现每次添加到最小高度列的关键方法;

private int GetColumnIndex(double[] columnHeights)
{int columnIndex = 0;double height = columnHeights[0];for (int j = 1; j < columnHeights.Length; j++){if (columnHeights[j] < height){columnIndex = j;height = columnHeights[j];}}return columnIndex;
}

调用示例

下面示例中,我们使用了 GridView 控件,用 StaggeredPanel 作为 ItemsPanelTemplate;上面说到了两个依赖属性,我们分别作了设置,从下面的运行图中也可以体现出来。大家也可以看到,StaggeredPanel 中 child 的排列规则,确实是按照每个列高度最小的列来排列;而在 panel 宽度变化时,也对应作了重新的计算和排列。

<GridView.ItemTemplate><DataTemplate><Grid><Grid.Background><SolidColorBrush Color="{Binding Color}"/></Grid.Background><Image Source="{Binding Thumbnail}" Stretch="Uniform"/><Border Background="#44000000" VerticalAlignment="Top"><TextBlock Foreground="White" Margin="5,3"><Run Text="{Binding Title}"/></TextBlock></Border></Grid></DataTemplate>
</GridView.ItemTemplate>
<GridView.ItemsPanel><ItemsPanelTemplate><controls:StaggeredPanel DesiredColumnWidth="135" Padding="25,25,25,25"HorizontalAlignment="Stretch"/></ItemsPanelTemplate>
</GridView.ItemsPanel>

 

总结

到这里我们就把 UWP Community Toolkit 中的 StaggeredPanel 功能的源代码实现过程和简单的调用示例讲解完成了,希望能对大家更好的理解和使用这个控件有所帮助,也希望能启发大家去做出更丰富排列规则的 Panel 控件。欢迎大家多多交流,谢谢!

最后,再跟大家安利一下 UWPCommunityToolkit 的官方微博:https://weibo.com/u/6506046490, 大家可以通过微博关注最新动态。

衷心感谢 UWPCommunityToolkit 的作者们杰出的工作,Thank you so much, UWPCommunityToolkit authors!!!

转载于:https://www.cnblogs.com/shaomeng/p/8676696.html

New UWP Community Toolkit - Staggered panel相关推荐

  1. New UWP Community Toolkit - ImageEx

    概述 UWP Community Toolkit  中有一个图片的扩展控件 - ImageEx,本篇我们结合代码详细讲解  ImageEx 的实现. ImageEx 是一个图片的扩展控件,包括 Ima ...

  2. Extensions in UWP Community Toolkit - ViewExtensions

    概述 UWP Community Toolkit Extensions 中有一个为 View 提供的扩展 - View Extensions,本篇我们结合代码详细讲解 View Extensions  ...

  3. New UWP Community Toolkit - DeveloperTools

    概述 UWP Community Toolkit  中有一个开发者工具集 DeveloperTools,可以帮助开发者在开发过程中进行 UI 和功能的调试,本篇我们结合代码详细讲解  Develope ...

  4. Extensions in UWP Community Toolkit - ListViewExtensions

    概述 UWP Community Toolkit Extensions 中有一个为 ListView 提供的扩展 - ListViewExtensions,本篇我们结合代码详细讲解 ListView  ...

  5. Coolite Toolkit学习笔记四:容器控件之FiledSet、Panel和Window

    一.FieldSet控件       FieldSet控件在开发中使用率还是很高的,毕竟Web中微软没有提供Group控件,通常都是使用FieldSet来进行表单等界面布局分组.在Coolite To ...

  6. 用于Fluent Design的UWP社区工具包蓄势待发

    Microsoft继续为即将推出Windows 10"秋季创作者更新"(FCU,Fall Creators Update)在做着准备工作,其中部分工作是目前正在开展的开发者工具和资 ...

  7. [UWP]了解IValueConverter

    [UWP]了解IValueConverter 原文:[UWP]了解IValueConverter 1. 前言 IValueConverter是用于数据绑定的强大的武器,它用于Value在Binding ...

  8. UWP 使用OneDrive云存储2.x api(二)【全网首发】

    上一篇提到为了给用户打造一个完全无缝衔接的最佳体验,UWP开发者最好也要实现App设置和数据的跨平台 分析了数据漫游和OneDrive的优缺点,结合自己App实际需要,我选择了OneDrive. 毕竟 ...

  9. [UWP]创建一个ProgressControl

    原文:[UWP]创建一个ProgressControl 1. 前言 博客园终于新增了UWP的分类,我来为这个分类贡献第一篇博客吧. UWP有很多问题,先不说生态的事情,表单.验证.输入.设计等等一堆基 ...

最新文章

  1. 根据IP查找在交换机上的端口
  2. 智能计算升级背后:华为“不想再搬砖”
  3. python django 表单_Django-表单处理
  4. django models索引_Django(生命周期、每部分详解、路由层)
  5. docker 容器启动顺序_Docker容器启动时初始化Mysql数据库
  6. 使用Cygwin实现vlc 1.0.5的wince移植
  7. 引擎设计跟踪(九.6) 地形最近更新
  8. C#编程之委托与事件(一)
  9. Extjs window autoload
  10. idea全局搜索快捷键总结
  11. window sserver 2008 r2安装教程
  12. 驱动概念(uboot命令)
  13. 执行以下代码后,可以看到小猫在舞台上右转了4次正好一圈。
  14. 接口文档要写在概要设计里吗_概要设计报告怎么写
  15. 关于DEP(数据执行保护)的分析
  16. ReactNative连接android模拟器
  17. 对SE_ResNet的理解
  18. mac 重装 mysql
  19. 大华摄像头录像视频,可以使用PotPlayer剪切
  20. JDK9-17开发常用的新特性

热门文章

  1. linux下mnt目录作用
  2. 学习笔记 - CFA 权益 2
  3. 开源应用中心|如何快速部署你的绝佳Markdown写作平台——CodiMD
  4. 什么是量化投资——QUANT-zhang
  5. 桌面路径不小心改成磁盘根目录改不回去解决方案
  6. docker修练之windows与linux下实践记录
  7. Java 统计输入字符串中’e’字符出现的频数
  8. 关于防御式编程 (Defensive programming )和安全编码
  9. 数组字符操作--输出乘法竖式
  10. 运动健身食谱APP--学习大作业设计指导性项目