概述

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 控件中可用的点,分为上下和左右两个方向,分别处理行和列的数据;以行为例,遍历每列的所有行,返回是否可用于放置元素的标识;

internal static IEnumerable<(int row, int column)> GetFreeSpot(TakenSpotsReferenceHolder arrayref, int firstcolumn, bool topdown)
{
    if (topdown)
    {
        );

        // Layout spots from Top-Bottom, Left-Right (right-left handled automatically by Grid with Flow-Direction).
        // Effectively transpose the Grid Layout.
        ; c < arrayref.SpotsTaken.GetLength(); c++)
        {
             && firstcolumn >  && firstcolumn < rows) ? firstcolumn : ;
            for (int r = start; r < rows; r++)
            {
                if (!arrayref.SpotsTaken[r, c])
                {
                    yield return (r, c);
                }
            }
        }
    }
    else
    {
       // 省略列处理代码     ...
    }
}

2). GetDimensions()

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

internal static (int rows, int columns) GetDimensions(FrameworkElement[] visible, int rows, int cols, int firstColumn)
{
    // If a dimension isn't specified, we need to figure out the other one (or both).
     || cols == )
    {
        // Calculate the size & area of all objects in the grid to know how much space we need.
        , visible.Sum(item => GetRowSpan(item) * GetColumnSpan(item)));

        )
        {
            )
            {
                // Bound check
                ) ?  : firstColumn;

                // If we have columns but no rows, calculate rows based on column offset and number of children.
                rows = (count + first + (cols - )) / cols;
                return (rows, cols);
            }
            else
            {
                // Otherwise, determine square layout if both are zero.
                var size = (int)Math.Ceiling(Math.Sqrt(count));

                // Figure out if firstColumn is in bounds
                ) ?  : firstColumn;

                rows = (int)Math.Ceiling(Math.Sqrt(count + first));
                return (rows, rows);
            }
        }
        )
        {
       ...
        }
    }

    return (rows, cols);
}

3). SetupRowDefinitions()

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

internal void SetupRowDefinitions(int rows)
{
    // Mark initial definitions so we don't erase them.
    foreach (var rd in RowDefinitions)
    {
        if (GetAutoLayout(rd) == null)
        {
            SetAutoLayout(rd, false);
        }
    }

    // Remove non-autolayout rows we've added and then add them in the right spots.
    if (rows != RowDefinitions.Count)
    {
        ; r >= ; r--)
        {
            if (GetAutoLayout(RowDefinitions[r]) == true)
            {
                RowDefinitions.RemoveAt(r);
            }
        }

        for (int r = this.RowDefinitions.Count; r < rows; r++)
        {
            var rd = new RowDefinition();
            SetAutoLayout(rd, true);
            this.RowDefinitions.Insert(r, rd);
        }
    }
}

2. UniformGrid.Properties.cs

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

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

3. UniformGrid.cs

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

1). MeasureOverride()

  • 首先根据可见元素集合,获取控件的行列数量,设置行列定义;
  • 遍历所有可见元素,根据每个元素的行列和行列跨度属性,设置自动布局,填充 spotsTaken;
  • 计算行和列的空白空间总数值,再根据总空间数值和行列数,计算出一个元素的尺寸;
  • 遍历所有可见元素,找出元素中最大的宽度和高度;再用这个最大尺寸,乘上行列数,加上空白空间数值,得到控件所需尺寸;
protected override Size MeasureOverride(Size availableSize)
{
    // Get all Visible FrameworkElement Children
    var visible = Children.Where(item => item.Visibility != Visibility.Collapsed && item is FrameworkElement).Select(item => item as FrameworkElement).ToArray();
    var (rows, columns) = GetDimensions(visible, Rows, Columns, FirstColumn);

    SetupRowDefinitions(rows);
    SetupColumnDefinitions(columns);

    var spotref = new TakenSpotsReferenceHolder(rows, columns);
    foreach (var child in visible)
    {
        var row = GetRow(child);
        var col = GetColumn(child);
        var rowspan = GetRowSpan(child);
        var colspan = GetColumnSpan(child);

         && col ==  && GetAutoLayout(child) == null) || GetAutoLayout(child) == true)
        {
            SetAutoLayout(child, true);
        }
        else
        {
            SetAutoLayout(child, false);
            spotref.SpotsTaken.Fill(true, row, col, colspan, rowspan); // row, col, width, height
        }
    }

    ;
    ;

    if (_hasGridSpacing)
    {
        columnSpacingSize = ColumnSpacing * (columns - );
        rowSpacingSize = RowSpacing * (rows - );
    }

    Size childSize = new Size(
        (availableSize.Width - columnSpacingSize) / columns,
        (availableSize.Height - rowSpacingSize) / rows);

    double maxWidth = 0.0;
    double maxHeight = 0.0;

    var freespots = GetFreeSpot(spotref, FirstColumn, Orientation == Orientation.Vertical).GetEnumerator();
    foreach (var child in visible)
    {
        if (GetAutoLayout(child) == true)
        {
            if (freespots.MoveNext())
            {
                var (row, column) = freespots.Current;

                SetRow(child, row);
                SetColumn(child, column);

                var rowspan = GetRowSpan(child);
                var colspan = GetColumnSpan(child);

                 || colspan > )
                {
                    spotref.SpotsTaken.Fill(true, row, column, GetColumnSpan(child), GetRowSpan(child)); // row, col, width, height
                }
            }
            else
            {
                child.Measure(Size.Empty);
                _overflow.Add(child);
                continue;
            }
        }
         || GetRow(child) >= rows ||
                    GetColumn(child) <  || GetColumn(child) >= columns)
        {
            child.Measure(Size.Empty);

            _overflow.Add(child);

            continue;
        }

        child.Measure(childSize);

        maxWidth = Math.Max(child.DesiredSize.Width, maxWidth);
        maxHeight = Math.Max(child.DesiredSize.Height, maxHeight);
    }

    var desiredSize = new Size((maxWidth * (double)columns) + columnSpacingSize, (maxHeight * (double)rows) + rowSpacingSize);
    base.MeasureOverride(desiredSize);

    return desiredSize;
}

调用示例

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

<Page
    xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
    xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
    xmlns:controls="using:Microsoft.Toolkit.Uwp.UI.Controls"
    xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
    xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
    mc:Ignorable="d">

  <controls:UniformGrid
        FirstColumn="1"
        Orientation="Horizontal"
        Rows="0"
        Columns="0">
    <Border Background="AliceBlue"
            Grid.Row="1" Grid.Column="1"
            Grid.RowSpan="2"
            Grid.ColumnSpan="2"><TextBlock Text="1"/></Border>
    <Border Background="Cornsilk"><TextBlock Text="2"/></Border>
    <Border Background="DarkSalmon"><TextBlock Text="3"/></Border>
    <Border Background="Gainsboro"><TextBlock Text="4"/></Border>
    <Border Background="LightBlue"><TextBlock Text="5"/></Border>
    <Border Background="MediumAquamarine"><TextBlock Text="6"/></Border>
    <Border Background="MistyRose"><TextBlock Text="7"/></Border>
    <Border Background="LightCyan"><TextBlock Text="8"/></Border>
    <Border Background="Salmon"><TextBlock Text="9"/></Border>
    <Border Background="Goldenrod"><TextBlock Text="10"/></Border>
    <Border Background="Pink"><TextBlock Text="11"/></Border>
  </controls:UniformGrid>
</Page>

总结

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

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

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

Windows Community Toolkit 3.0 - UniformGrid的更多相关文章

  1. Windows Community Toolkit 3.0 新功能 在WinForms 和 WPF 使用 UWP 控件

    本文告诉大家一个令人震惊的消息,Windows Community Toolkit 有一个大更新,现在的版本是 3.0 .最大的提升就是 WinForm 和 WPF 程序可以使用部分 UWP 控件. ...

  2. Windows Community Toolkit 4.0 - DataGrid - Part03

    概述 在上面一篇 Windows Community Toolkit 4.0 - DataGrid - Part02 中,我们针对 DataGrid 控件的 Utilities 部分做了详细分享.而在 ...

  3. Windows Community Toolkit 4.0 - DataGrid - Part02

    概述 在上面一篇 Windows Community Toolkit 4.0 - DataGrid - Part01 中,我们针对 DataGrid 控件的 CollectionView 部分做了详细 ...

  4. Windows Community Toolkit 4.0 - DataGrid - Part01

    概述 在上面一篇 Windows Community Toolkit 4.0 - DataGrid - Overview 中,我们对 DataGrid 控件做了一个概览的介绍,今天开始我们会做进一步的 ...

  5. Windows Community Toolkit 4.0 - DataGrid - Overview

    概述 Windows Community Toolkit 4.0 于 2018 月 8 月初发布:Windows Community Toolkit 4.0 Release Note. 4.0 版本相 ...

  6. Windows Community Toolkit 3.0 - InfiniteCanvas

    概述 InfiniteCanvas 是一个 Canvas 控件,它支持无限画布的滚动,支持 Ink,文本,格式文本,画布缩放操作,撤销重做操作,导入和导出数据. 这是一个非常实用的控件,在“来画视频” ...

  7. Windows Community Toolkit 3.0 - Gaze Interaction

    概述 Gaze Input & Tracking - 也就是视觉输入和跟踪,是一种和鼠标/触摸屏输入非常不一样的交互方式,利用人类眼球的识别和眼球方向角度的跟踪,来判断人眼的目标和意图,从而非 ...

  8. Windows Community Toolkit 3.0 - CameraPreview

    概述 Windows Community Toolkit 3.0 于 2018 年 6 月 2 日 Release,同时正式更名为 Windows Community Toolkit,原名为 UWP ...

  9. 与众不同 windows phone (44) - 8.0 位置和地图

    [源码下载] 与众不同 windows phone (44) - 8.0 位置和地图 作者:webabcd 介绍与众不同 windows phone 8.0 之 位置和地图 位置(GPS) - Loc ...

随机推荐

  1. 虚拟机中安装Virtualbox,嵌套的虚拟机不能运行64位系统

    https://www.quora.com/Can-I-install-Virtualbox-in-a-virtual-machine Here is a previous question on Q ...

  2. mssql sqlserver 从指定字符串中获取数字的方法

    转自:http://www.maomao365.com/?p=6410 摘要: 下文主要分享从指定字符串或列中获取数字信息,如下所示: 实验环境:sql server 2000 ----编写sql函数 ...

  3. css把容器级别(div...)标签固定在一个位置(在页面最右边)

    .process{ border:1px solid #B7B7B8; background:#F8F8F8; width:80px; height:250px; <!--固定定位; text- ...

  4. 【转载】Python中的垃圾回收机制

    GC作为现代编程语言的自动内存管理机制,专注于两件事:1. 找到内存中无用的垃圾资源 2. 清除这些垃圾并把内存让出来给其他对象使用.GC彻底把程序员从资源管理的重担中解放出来,让他们有更多的时间放在 ...

  5. Linux之系统优化

    查看系统版本 [root@luffy- /]# cat /etc/redhat-release CentOS release 6.9 (Final) [root@luffy- /]# uname -m ...

  6. OLTP与OLAP的区别

    OLTP和OLAP的区别 联机事务处理OLTP(on-line transaction processing) 主要是执行基本日常的事务处理,比如数据库记录的增删查改.比如在银行的一笔交易记录,就是一 ...

  7. 再谈:为什么开源C/C++开源框架极昂贵?

        今天读了一篇文章:<腾讯前员工创业笔记:那些跟钱有关的事儿>(http://tech.163.com/14/0515/08/9S9975C5000915BF.html),摘录两段: ...

  8. 如何用chrome扩展将网页变成黑底白字,用以保护视力

    不知道有没有科学依据,自己感觉黑底白字对视力好些,于是动手加个chrome扩展: 第一步:建个文件夹,名称比如叫changeColor; 第二步:在changeColor文件夹中建三个文件:manif ...

  9. [MCM] 多目标优化 MOP(multi-objective programming)

    生活中许多问题都是由相互冲突和影响的多个目标组成.人们会经常遇到使多个目标在给定区域同时尽可能最佳的优化问题,也就是多目标优化问题.优化问题存在的优化目标超过一个并需要同时处理就成为多目标优化问题. ...

  10. [HEOI2016/TJOI2016]排序

    嘟嘟嘟 首先这题的暴力是十分好写的,而且据说能得不少分. 正解写起来不难,就是不太好想. 根据做题经验,我想到了给这个序列转化成01序列,但是接下来我就不会了.还是看了题解. 因为查询只有一个数,所以 ...