概述c++
UniformGrid 控件是一個響應式的佈局控件,容許把 items 排列在一組均勻分佈的行或列中,以填充總體的可用顯示空間,造成均勻的多個網格。默認狀況下,網格中的每一個單元格大小相同。git
這是一個很是實用的控件,好比相冊應用中多行多列均勻排列圖片,好比新聞類應用中排列新聞,再好比咱們在來畫視頻中展現用戶做品封面和簡要信息等,由於它支持響應佈局,因此在應用尺寸變化時顯示會很友好。github
下面是 Windows Community Toolkit Sample App 的示例截圖和 code/doc 地址:express
Windows Community Toolkit Doc - UniformGridcanvas
Windows Community Toolkit Source Code - UniformGridwindows
Namespace: Microsoft.Toolkit.Uwp.UI.Controls; Nuget: Microsoft.Toolkit.Uwp.UI.Controls;數組
開發過程ide
代碼結構分析佈局
首先來看 UniformGrid 控件的代碼結構:this
UniformGrid 控件的代碼實現比較簡單,咱們來看幾個類中重要的方法:
1. UniformGrid.Helpers.cs
1). GetFreeSpot()
獲取目前 UniformGrid 控件中可用的點,分爲上下和左右兩個方向,分別處理行和列的數據;以行爲例,遍歷每列的全部行,返回是否可用於放置元素的標識;
internal static IEnumerable<(int row, int column)> GetFreeSpot(TakenSpotsReferenceHolder arrayref, int firstcolumn, bool topdown) { if (topdown) { var rows = arrayref.SpotsTaken.GetLength(0); // Layout spots from Top-Bottom, Left-Right (right-left handled automatically by Grid with Flow-Direction). // Effectively transpose the Grid Layout. for (int c = 0; c < arrayref.SpotsTaken.GetLength(1); c++) { int start = (c == 0 && firstcolumn > 0 && firstcolumn < rows) ? firstcolumn : 0; 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). if (rows == 0 || cols == 0) { // Calculate the size & area of all objects in the grid to know how much space we need. var count = Math.Max(1, visible.Sum(item => GetRowSpan(item) * GetColumnSpan(item))); if (rows == 0) { if (cols > 0) { // Bound check var first = (firstColumn >= cols || firstColumn < 0) ? 0 : firstColumn; // If we have columns but no rows, calculate rows based on column offset and number of children. rows = (count + first + (cols - 1)) / 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 var first = (firstColumn >= size || firstColumn < 0) ? 0 : firstColumn; rows = (int)Math.Ceiling(Math.Sqrt(count + first)); return (rows, rows); } } else if (cols == 0) { ... } } 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) { for (int r = RowDefinitions.Count - 1; r >= 0; 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 控件所需的依賴屬性,主要有:
3. UniformGrid.cs
該類主要是 UnifromGrid 在 Grid 類基礎上的處理,主要處理測量和排列的方法,咱們來看一下功能比較複雜的 MeasureOverride() 方法,ArrangeOverride() 方法實現很簡單,這裏不作分析。
1). MeasureOverride()
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); if ((row == 0 && col == 0 && 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 } } double columnSpacingSize = 0; double rowSpacingSize = 0; if (_hasGridSpacing) { columnSpacingSize = ColumnSpacing * (columns - 1); rowSpacingSize = RowSpacing * (rows - 1); } 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); if (rowspan > 1 || colspan > 1) { spotref.SpotsTaken.Fill(true, row, column, GetColumnSpan(child), GetRowSpan(child)); // row, col, width, height } } else { child.Measure(Size.Empty); _overflow.Add(child); continue; } } else if (GetRow(child) < 0 || GetRow(child) >= rows || GetColumn(child) < 0 || 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 !!!