经验首页 前端设计 程序设计 Java相关 移动开发 数据库/运维 软件/图像 大数据/云计算 其他经验
当前位置:技术经验 » 程序设计 » ASP.net » 查看文章
Windows Community Toolkit 3.0 - UniformGrid
来源:cnblogs  作者:shaomeng  时间:2018/9/25 20:40:31  对本文有异议

概述

UniformGrid 控件是一个响应式的布局控件,允许把 items 排列在一组均匀分布的行或列中,以填充整体的可用显示空间,形成均匀的多个网格。默认情况下,网格中的每个单元格大小相同。

这是一个非常实用的控件,比如相册应用中多行多列均匀排列图片,比如新闻类应用中排列新闻,再比如我们在来画视频中展示用户作品封面和简要信息等,因为它支持响应布局,所以在应用尺寸变化时显示会很友好。

下面是 Windows Community Toolkit Sample App 的示例截图和 code/doc 地址:

Windows Community Toolkit Doc - UniformGrid

Windows Community Toolkit Source Code - UniformGrid

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

 

开发过程

代码结构分析

首先来看 UniformGrid 控件的代码结构:

  • TakenSpotsReferenceHolder.cs - 获取和设置点数组,标识布局中的 item 是否固定;
  • UniformGrid.Helpers.cs - UniformGrid 控件帮助类,主要处理控件的行列布局和排列逻辑;
  • UniformGrid.Properties.cs - UniformGrid 控件的依赖属性类;
  • UniformGrid.cs - UniformGrid 控件的主要处理逻辑类;

UniformGrid 控件的代码实现比较简单,我们来看几个类中重要的方法:

1. UniformGrid.Helpers.cs

1). GetFreeSpot()

获取目前 UniformGrid 控件中可用的点,分为上下和左右两个方向,分别处理行和列的数据;以行为例,遍历每列的所有行,返回是否可用于放置元素的标识;

  1. internal static IEnumerable<(int row, int column)> GetFreeSpot(TakenSpotsReferenceHolder arrayref, int firstcolumn, bool topdown)
  2. {
  3. if (topdown)
  4. {
  5. var rows = arrayref.SpotsTaken.GetLength(0);
  6. // Layout spots from Top-Bottom, Left-Right (right-left handled automatically by Grid with Flow-Direction).
  7. // Effectively transpose the Grid Layout.
  8. for (int c = 0; c < arrayref.SpotsTaken.GetLength(1); c++)
  9. {
  10. int start = (c == 0 && firstcolumn > 0 && firstcolumn < rows) ? firstcolumn : 0;
  11. for (int r = start; r < rows; r++)
  12. {
  13. if (!arrayref.SpotsTaken[r, c])
  14. {
  15. yield return (r, c);
  16. }
  17. }
  18. }
  19. }
  20. else
  21. {
  22. // 省略列处理代码
        ...
  23. }
  24. }

2). GetDimensions()

获取 UniformGrid 控件在行和列的数值;先计算目前所有 item 所需的格数,分为 row = 0,column = 0 和两个值都为 0 处理,分别计算 row column 的值;如果两个值有一个为 0,则根据不为 0 的值和 item 数量来判断另一个值;如果两个值都为 0,则定义为方形;

  1. internal static (int rows, int columns) GetDimensions(FrameworkElement[] visible, int rows, int cols, int firstColumn)
  2. {
  3. // If a dimension isn't specified, we need to figure out the other one (or both).
  4. if (rows == 0 || cols == 0)
  5. {
  6. // Calculate the size & area of all objects in the grid to know how much space we need.
  7. var count = Math.Max(1, visible.Sum(item => GetRowSpan(item) * GetColumnSpan(item)));
  8. if (rows == 0)
  9. {
  10. if (cols > 0)
  11. {
  12. // Bound check
  13. var first = (firstColumn >= cols || firstColumn < 0) ? 0 : firstColumn;
  14. // If we have columns but no rows, calculate rows based on column offset and number of children.
  15. rows = (count + first + (cols - 1)) / cols;
  16. return (rows, cols);
  17. }
  18. else
  19. {
  20. // Otherwise, determine square layout if both are zero.
  21. var size = (int)Math.Ceiling(Math.Sqrt(count));
  22. // Figure out if firstColumn is in bounds
  23. var first = (firstColumn >= size || firstColumn < 0) ? 0 : firstColumn;
  24. rows = (int)Math.Ceiling(Math.Sqrt(count + first));
  25. return (rows, rows);
  26. }
  27. }
  28. else if (cols == 0)
  29. {
  30.        ...
  31. }
  32. }
  33. return (rows, cols);
  34. }

3). SetupRowDefinitions()

SetupRowDefinitions() 和 SetupColumnDefinitions() 实现类似,我们看其中一个;先初始化行定义,遍历行列表,如果有行的布局方式不为自动布局,先把这些布局删掉,再重新以自动布局的方式加入到行定义中;这样实现的目标,是保证行布局能对 item 自适应,缩放时可以自动响应;

  1. internal void SetupRowDefinitions(int rows)
  2. {
  3. // Mark initial definitions so we don't erase them.
  4. foreach (var rd in RowDefinitions)
  5. {
  6. if (GetAutoLayout(rd) == null)
  7. {
  8. SetAutoLayout(rd, false);
  9. }
  10. }
  11. // Remove non-autolayout rows we've added and then add them in the right spots.
  12. if (rows != RowDefinitions.Count)
  13. {
  14. for (int r = RowDefinitions.Count - 1; r >= 0; r--)
  15. {
  16. if (GetAutoLayout(RowDefinitions[r]) == true)
  17. {
  18. RowDefinitions.RemoveAt(r);
  19. }
  20. }
  21. for (int r = this.RowDefinitions.Count; r < rows; r++)
  22. {
  23. var rd = new RowDefinition();
  24. SetAutoLayout(rd, true);
  25. this.RowDefinitions.Insert(r, rd);
  26. }
  27. }
  28. }

 

2. UniformGrid.Properties.cs

该类定义了 UniformGrid 控件所需的依赖属性,主要有:

  • AutoLayout - 获取和设置自动布局属性,包括对行和列的操作;
  • Columns - UniformGrid 的列属性;
  • FirstColumn - UniformGrid 的首列属性,获取的是首行元素距离第一列的偏移量;
  • Orientation - UniformGrid 的排列方式,包括横向和纵向两种;
  • Rows - UniformGrid 的行属性;

 

3. UniformGrid.cs 

该类主要是 UnifromGrid 在 Grid 类基础上的处理,主要处理测量和排列的方法,我们来看一下功能比较复杂的 MeasureOverride() 方法,ArrangeOverride() 方法实现很简单,这里不做分析。

1). MeasureOverride()

  • 首先根据可见元素集合,获取控件的行列数量,设置行列定义;
  • 遍历所有可见元素,根据每个元素的行列和行列跨度属性,设置自动布局,填充 spotsTaken;
  • 计算行和列的空白空间总数值,再根据总空间数值和行列数,计算出一个元素的尺寸;
  • 遍历所有可见元素,找出元素中最大的宽度和高度;再用这个最大尺寸,乘上行列数,加上空白空间数值,得到控件所需尺寸;
  1. protected override Size MeasureOverride(Size availableSize)
  2. {
  3. // Get all Visible FrameworkElement Children
  4. var visible = Children.Where(item => item.Visibility != Visibility.Collapsed && item is FrameworkElement).Select(item => item as FrameworkElement).ToArray();
  5. var (rows, columns) = GetDimensions(visible, Rows, Columns, FirstColumn);
  6. SetupRowDefinitions(rows);
  7. SetupColumnDefinitions(columns);
  8. var spotref = new TakenSpotsReferenceHolder(rows, columns);
  9. foreach (var child in visible)
  10. {
  11. var row = GetRow(child);
  12. var col = GetColumn(child);
  13. var rowspan = GetRowSpan(child);
  14. var colspan = GetColumnSpan(child);
  15. if ((row == 0 && col == 0 && GetAutoLayout(child) == null) || GetAutoLayout(child) == true)
  16. {
  17. SetAutoLayout(child, true);
  18. }
  19. else
  20. {
  21. SetAutoLayout(child, false);
  22. spotref.SpotsTaken.Fill(true, row, col, colspan, rowspan); // row, col, width, height
  23. }
  24. }
  25. double columnSpacingSize = 0;
  26. double rowSpacingSize = 0;
  27. if (_hasGridSpacing)
  28. {
  29. columnSpacingSize = ColumnSpacing * (columns - 1);
  30. rowSpacingSize = RowSpacing * (rows - 1);
  31. }
  32. Size childSize = new Size(
  33. (availableSize.Width - columnSpacingSize) / columns,
  34. (availableSize.Height - rowSpacingSize) / rows);
  35. double maxWidth = 0.0;
  36. double maxHeight = 0.0;
  37. var freespots = GetFreeSpot(spotref, FirstColumn, Orientation == Orientation.Vertical).GetEnumerator();
  38. foreach (var child in visible)
  39. {
  40. if (GetAutoLayout(child) == true)
  41. {
  42. if (freespots.MoveNext())
  43. {
  44. var (row, column) = freespots.Current;
  45. SetRow(child, row);
  46. SetColumn(child, column);
  47. var rowspan = GetRowSpan(child);
  48. var colspan = GetColumnSpan(child);
  49. if (rowspan > 1 || colspan > 1)
  50. {
  51. spotref.SpotsTaken.Fill(true, row, column, GetColumnSpan(child), GetRowSpan(child)); // row, col, width, height
  52. }
  53. }
  54. else
  55. {
  56. child.Measure(Size.Empty);
  57. _overflow.Add(child);
  58. continue;
  59. }
  60. }
  61. else if (GetRow(child) < 0 || GetRow(child) >= rows ||
  62. GetColumn(child) < 0 || GetColumn(child) >= columns)
  63. {
  64. child.Measure(Size.Empty);
  65. _overflow.Add(child);
  66. continue;
  67. }
  68. child.Measure(childSize);
  69. maxWidth = Math.Max(child.DesiredSize.Width, maxWidth);
  70. maxHeight = Math.Max(child.DesiredSize.Height, maxHeight);
  71. }
  72. var desiredSize = new Size((maxWidth * (double)columns) + columnSpacingSize, (maxHeight * (double)rows) + rowSpacingSize);
  73. base.MeasureOverride(desiredSize);
  74. return desiredSize;
  75. }

 

调用示例

UniformGrid 控件的调用非常简单,下面看看 XAML 中的调用:

  1. <Page
  2. xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
  3. xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
  4. xmlns:controls="using:Microsoft.Toolkit.Uwp.UI.Controls"
  5. xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
  6. xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
  7. mc:Ignorable="d">
  8.  
  9. <controls:UniformGrid
  10. FirstColumn="1"
  11. Orientation="Horizontal"
  12. Rows="0"
  13. Columns="0">
  14. <Border Background="AliceBlue"
  15. Grid.Row="1" Grid.Column="1"
  16. Grid.RowSpan="2"
  17. Grid.ColumnSpan="2"><TextBlock Text="1"/></Border>
  18. <Border Background="Cornsilk"><TextBlock Text="2"/></Border>
  19. <Border Background="DarkSalmon"><TextBlock Text="3"/></Border>
  20. <Border Background="Gainsboro"><TextBlock Text="4"/></Border>
  21. <Border Background="LightBlue"><TextBlock Text="5"/></Border>
  22. <Border Background="MediumAquamarine"><TextBlock Text="6"/></Border>
  23. <Border Background="MistyRose"><TextBlock Text="7"/></Border>
  24. <Border Background="LightCyan"><TextBlock Text="8"/></Border>
  25. <Border Background="Salmon"><TextBlock Text="9"/></Border>
  26. <Border Background="Goldenrod"><TextBlock Text="10"/></Border>
  27. <Border Background="Pink"><TextBlock Text="11"/></Border>
  28. </controls:UniformGrid>
  29. </Page>

 

总结

到这里我们就把 Windows Community Toolkit 3.0 中的 UniformGrid 的源代码实现过程讲解完成了,希望能对大家更好的理解和使用这个功能有所帮助。

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

衷心感谢 WindowsCommunityToolkit 的作者们杰出的工作,感谢每一位贡献者,Thank you so much, ALL WindowsCommunityToolkit AUTHORS !!!

 友情链接:直通硅谷  点职佳  北美留学生论坛

本站QQ群:前端 618073944 | Java 606181507 | Python 626812652 | C/C++ 612253063 | 微信 634508462 | 苹果 692586424 | C#/.net 182808419 | PHP 305140648 | 运维 608723728

W3xue 的所有内容仅供测试,对任何法律问题及风险不承担任何责任。通过使用本站内容随之而来的风险与本站无关。
关于我们  |  意见建议  |  捐助我们  |  报错有奖  |  广告合作、友情链接(目前9元/月)请联系QQ:27243702 沸活量
皖ICP备17017327号-2 皖公网安备34020702000426号