Windows Community Toolkit 3.0 - UniformGrid

来源:https://www.cnblogs.com/shaomeng/archive/2018/08/13/9281068.html
-Advertisement-
Play Games

概述 UniformGrid 控制項是一個響應式的佈局控制項,允許把 items 排列在一組均勻分佈的行或列中,以填充整體的可用顯示空間,形成均勻的多個網格。預設情況下,網格中的每個單元格大小相同。 這是一個非常實用的控制項,比如相冊應用中多行多列均勻排列圖片,比如新聞類應用中排列新聞,再比如我們在來畫視 ...


概述

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)
    {
        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 控制項所需的依賴屬性,主要有:

  • 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);

        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 !!!


您的分享是我們最大的動力!

-Advertisement-
Play Games
更多相關文章
  • 爬: 爬一個網站需要幾步? 確定用戶的需求 根據需求,尋找網址 讀取網頁 urllib requests 定位並提取數據 存儲數據 mysql redis 文件存儲 爬取百度首頁:(確定用戶需求) cookie和session之間的愛情故事: 啥是cookie: 當你在瀏覽網站的時候,WEB 伺服器 ...
  • 0.簡介 承接上篇文章我們會在這篇文章詳細解說一下 Abp 是如何結合 與 來實現一個完整的多租戶系統的許可權校驗的。 1.多租戶的概念 多租戶系統又被稱之為 Saas ,比如阿裡雲就是一個典型的多租戶系統,用戶本身就是一個租戶,可以在上面購買自己的 ECS 實例,並且自己的數據與其他使用者(租戶)所 ...
  • 1、下載和安裝註意每次安裝只是安裝到本項目中,換了另一個項目需要再次安裝和配置。我使用的是Visual Studio 2013 社區版,在tools中找到NuGet包管理。搜索log4net並點擊安裝。首先進行下載點擊確定安裝安裝完成當然,也可以用NuGet 的控制台來安裝百度搜索 net4log ... ...
  • 相信有些朋友喜歡直接把項目放在移動硬碟上進行工作,為了方便來回在多臺電腦或不同的操作系統平臺上來回碼磚,磁碟的格式基本都是exFAT的(喜歡在macOS上用NTFS或者FAT的都是大佬),在這裡我們不討論exFat的格式優缺點、反正他免費就行,只記一次在MAC上出現的奇怪問題,希望有遇到該問題的朋友 ...
  • NET Core的跨平臺大家已經有目共睹,而在MAC平臺上做開發已經成為目前的主流,無論哪種語言。 在一次微服務移植的過程中,客戶端需要發送Http自定義混合驗證,在MonoNET上沒有任何問題,而移植到NET Core 2.0並運行,就出現了錯誤:The handler does not supp ...
  • 使用Task,await,async,非同步執行事件(event),不阻塞UI線程和不跨線程執行UI更新 使用Task,await,async 的非同步模式 去執行事件(event) 解決不阻塞UI線程和不誇跨線程執行UI更新報錯的最佳實踐,附加幾種其他方式比較 由於是Winform代碼和其他原因,本文 ...
  • 1、 問題描述 最近使用ABP .Net Core框架做一個微信開發,同時採用了一個微信開發框架集成到ABP,在微信用戶關註的推送事件里調用了一個async 方法,由於沒有返回值,也沒做任何處理,本地調試也OK,但一發佈到線上就有問題,微信公眾號關註成功,也有推送消息過來,但微信用戶一直保存不上,查 ...
  • TransactionScope只要一個操作失敗,它會自動回滾,Complete表示事務完成 實事上,一個錯誤的理解就是Complete()方法是提交事務的,這是錯誤的,事實上,它的作用的表示本事務完成,它一般放在try{}的結尾處,不用判斷前臺操作是否成功,如果不成功,它會自己回滾。 在.net ...
一周排行
    -Advertisement-
    Play Games
  • 前言 本文介紹一款使用 C# 與 WPF 開發的音頻播放器,其界面簡潔大方,操作體驗流暢。該播放器支持多種音頻格式(如 MP4、WMA、OGG、FLAC 等),並具備標記、實時歌詞顯示等功能。 另外,還支持換膚及多語言(中英文)切換。核心音頻處理採用 FFmpeg 組件,獲得了廣泛認可,目前 Git ...
  • OAuth2.0授權驗證-gitee授權碼模式 本文主要介紹如何筆者自己是如何使用gitee提供的OAuth2.0協議完成授權驗證並登錄到自己的系統,完整模式如圖 1、創建應用 打開gitee個人中心->第三方應用->創建應用 創建應用後在我的應用界面,查看已創建應用的Client ID和Clien ...
  • 解決了這個問題:《winForm下,fastReport.net 從.net framework 升級到.net5遇到的錯誤“Operation is not supported on this platform.”》 本文內容轉載自:https://www.fcnsoft.com/Home/Sho ...
  • 國內文章 WPF 從裸 Win 32 的 WM_Pointer 消息獲取觸摸點繪製筆跡 https://www.cnblogs.com/lindexi/p/18390983 本文將告訴大家如何在 WPF 裡面,接收裸 Win 32 的 WM_Pointer 消息,從消息裡面獲取觸摸點信息,使用觸摸點 ...
  • 前言 給大家推薦一個專為新零售快消行業打造了一套高效的進銷存管理系統。 系統不僅具備強大的庫存管理功能,還集成了高性能的輕量級 POS 解決方案,確保頁面載入速度極快,提供良好的用戶體驗。 項目介紹 Dorisoy.POS 是一款基於 .NET 7 和 Angular 4 開發的新零售快消進銷存管理 ...
  • ABP CLI常用的代碼分享 一、確保環境配置正確 安裝.NET CLI: ABP CLI是基於.NET Core或.NET 5/6/7等更高版本構建的,因此首先需要在你的開發環境中安裝.NET CLI。這可以通過訪問Microsoft官網下載並安裝相應版本的.NET SDK來實現。 安裝ABP ...
  • 問題 問題是這樣的:第三方的webapi,需要先調用登陸介面獲取Cookie,訪問其它介面時攜帶Cookie信息。 但使用HttpClient類調用登陸介面,返回的Headers中沒有找到Cookie信息。 分析 首先,使用Postman測試該登陸介面,正常返回Cookie信息,說明是HttpCli ...
  • 國內文章 關於.NET在中國為什麼工資低的分析 https://www.cnblogs.com/thinkingmore/p/18406244 .NET在中國開發者的薪資偏低,主要因市場需求、技術棧選擇和企業文化等因素所致。歷史上,.NET曾因微軟的閉源策略發展受限,儘管後來推出了跨平臺的.NET ...
  • 在WPF開發應用中,動畫不僅可以引起用戶的註意與興趣,而且還使軟體更加便於使用。前面幾篇文章講解了畫筆(Brush),形狀(Shape),幾何圖形(Geometry),變換(Transform)等相關內容,今天繼續講解動畫相關內容和知識點,僅供學習分享使用,如有不足之處,還請指正。 ...
  • 什麼是委托? 委托可以說是把一個方法代入另一個方法執行,相當於指向函數的指針;事件就相當於保存委托的數組; 1.實例化委托的方式: 方式1:通過new創建實例: public delegate void ShowDelegate(); 或者 public delegate string ShowDe ...