WPF中Grid自动布局

控件功能

根据自定义行和列,快速进行排列,能够进行自定义控件间距离,减少元素进行定义间距,同时能更好的维护界面排序。

代码部分

csharp 复制代码
using System.ComponentModel;
using System.Windows;
using System.Windows.Controls;

namespace WPFApp
{
    public class AutoGrid : Grid
    {
        /// <summary>
        /// 列定义 例如: "100,*,100"  "100 * 100"
        /// </summary>
        [Category("Layout")]
        public string Columns
        {
            get { return (string)GetValue(ColumnsProperty); }
            set { SetValue(ColumnsProperty, value); }
        }

        public static readonly DependencyProperty ColumnsProperty =
            DependencyProperty.Register(nameof(Columns), typeof(string), typeof(AutoGrid), new PropertyMetadata("", ColumnsChanged));

        /// <summary>
        /// 行定义 例如: "100,*,100"  "100 * 100"    
        /// </summary>
        [Category("Layout")]
        public string Rows
        {
            get { return (string)GetValue(RowsProperty); }
            set { SetValue(RowsProperty, value); }
        }

        public static readonly DependencyProperty RowsProperty =
            DependencyProperty.Register(nameof(Rows), typeof(string), typeof(AutoGrid), new PropertyMetadata("", RowsChange));

        /// <summary>
        /// 排列方式
        /// </summary>
        [Category("Layout")]
        public Orientation Orientation
        {
            get { return (Orientation)GetValue(OrientationProperty); }
            set { SetValue(OrientationProperty, value); }
        }
        public static readonly DependencyProperty OrientationProperty =
    DependencyProperty.Register(nameof(Orientation), typeof(Orientation), typeof(AutoGrid), new PropertyMetadata(Orientation.Horizontal));

        /// <summary>
        /// 元素之间Margin
        /// </summary>
        [Category("Layout")]
        public Thickness? ChildMargin
        {
            get { return (Thickness?)GetValue(ChildMarginProperty); }
            set { SetValue(ChildMarginProperty, value); }
        }

        public static readonly DependencyProperty ChildMarginProperty =
DependencyProperty.Register(nameof(ChildMargin), typeof(Thickness?), typeof(AutoGrid), new PropertyMetadata((Thickness?)null, OnChildMarginChanged));

        /// <summary>
        /// 移除多余的元素
        /// </summary>
        public bool IsRemoveEleme { get; set; } = true;

        private static void OnChildMarginChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
        {
            var grid = d as AutoGrid;
            foreach (UIElement child in grid.Children)
            {
                if (grid.ChildMargin.HasValue)
                    child.SetValue(FrameworkElement.MarginProperty, grid.ChildMargin);
                else
                    child.SetValue(FrameworkElement.MarginProperty, DependencyProperty.UnsetValue);
            }
        }

        protected override Size MeasureOverride(Size constraint)
        {
            PerformLayout();
            return base.MeasureOverride(constraint);
        }

        private void PerformLayout()
        {
            int colCount = this.ColumnDefinitions.Count;
            int rowCount = this.RowDefinitions.Count;
            if (colCount == 0 || rowCount == 0) return;

            var position = 0;

            foreach (UIElement child in Children)
            {
                var childIsCollapsed = child.Visibility == Visibility.Collapsed;
                if (childIsCollapsed) continue;

                if (this.Orientation == Orientation.Horizontal)
                {
                    //从左往右,再从上往下
                    var row = Clamp(position / colCount, rowCount - 1);
                    var col = Clamp(position % colCount, colCount - 1);
                    Grid.SetRow(child, row);
                    Grid.SetColumn(child, col);
                    Grid.SetRowSpan(child, 1);
                    position += Grid.GetColumnSpan(child);
                }
                else
                {
                    //从上往下,再从左往右
                    var row = Clamp(position % rowCount, rowCount - 1);
                    var col = Clamp(position / rowCount, colCount - 1);
                    Grid.SetRow(child, row);
                    Grid.SetColumn(child, col);
                    Grid.SetColumnSpan(child, 1);
                    position += Grid.GetRowSpan(child);
                }

                //设置Margin
                if (ChildMargin.HasValue)
                {
                    child.SetValue(FrameworkElement.MarginProperty, ChildMargin);
                }
                else
                {
                    child.SetValue(FrameworkElement.MarginProperty, DependencyProperty.UnsetValue);
                }
            }

            //移除无法分配的元素
            if (IsRemoveEleme && Children.Count > colCount * rowCount)
            {
                Children.RemoveRange(colCount * rowCount, Children.Count - colCount * rowCount);
            }
        }

        private int Clamp(int value, int max)
        {
            return (value > max) ? max : value;
        }

        private static void RowsChange(DependencyObject d, DependencyPropertyChangedEventArgs e)
        {
            if (string.IsNullOrEmpty((string)e.NewValue)) return;

            var grid = (AutoGrid)d;
            grid.RowDefinitions.Clear();
            GridLength[] defs = GridLengthParse((string)e.NewValue);
            foreach (var def in defs)
            {
                grid.RowDefinitions.Add(new RowDefinition()
                {
                    Height = def
                });
            }
        }

        private static void ColumnsChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
        {
            if (string.IsNullOrEmpty((string)e.NewValue)) return;

            var grid = (AutoGrid)d;
            grid.ColumnDefinitions.Clear();
            GridLength[] defs = GridLengthParse((string)e.NewValue);
            foreach (var def in defs)
            {
                grid.ColumnDefinitions.Add(new ColumnDefinition()
                {
                    Width = def
                });
            }
        }

        public static GridLength[] GridLengthParse(string text)
        {
            string[] tokens = text.Split([' ', ','], StringSplitOptions.RemoveEmptyEntries);
            GridLength[] defineResult = new GridLength[tokens.Length];

            for (int i = 0; i < tokens.Count(); i++)
            {
                string curStr = tokens[i];
                double value = 0.0;

                //Star
                if (curStr.Contains('*'))
                {
                    if (!double.TryParse(curStr.Replace("*", ""), out value))
                    {
                        value = 1.0;
                    }
                    defineResult[i] = new GridLength(value, GridUnitType.Star);
                }

                //Pixel
                if (double.TryParse(curStr, out value))
                {
                    defineResult[i] = new GridLength(value, GridUnitType.Pixel);
                    continue;
                }

                //Auto
                defineResult[i] = GridLength.Auto;
            }

            return defineResult;
        }
    }
}

使用示例

csharp 复制代码
<Window
    x:Class="WPFApp.MainWindow"
    xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
    xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
    xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
    xmlns:local="clr-namespace:WPFApp"
    xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
    Title="MainWindow"
    Width="800"
    Height="450"
    mc:Ignorable="d">
    <Window.DataContext>
        <local:MainWindowViewModel />
    </Window.DataContext>
    <local:AutoGrid
        ChildMargin="10,10,0,0"
        Columns="100 200 300 400"
        Orientation="Horizontal"
        Rows="200 *">
        <TextBlock Text="1" />
        <TextBlock Text="2" />
        <TextBlock Text="3" />
        <TextBlock Text="4" />
        <TextBlock Text="5" />
        <TextBlock Text="6" />
        <TextBlock Text="7" />
        <TextBlock Text="8" />
        <TextBlock Text="9" />
        <TextBlock Text="10" />
        <TextBlock Text="11" />
        <TextBlock Text="12" />
    </local:AutoGrid>
</Window>

图示

相关推荐
不知名君1 天前
WPF轮播图动画交互 动画缩放展示图片
wpf
Sitarrrr2 天前
【WPF】IOC控制反转的应用:弹窗但不互相调用ViewModel
设计模式·c#·wpf
"孙小浩4 天前
HarmonyOS应用开发者高级-编程题-001
华为·wpf·harmonyos
yngsqq4 天前
003集——《利用 C# 与 AutoCAD API 开发 WPF 随机圆生成插件》(侧栏菜单+WPF窗体和控件+MVVM)
wpf
baivfhpwxf20235 天前
WPF 免费UI 控件HandyControl
ui·wpf
qq_196055875 天前
WPF插入背景图
wpf
baivfhpwxf20235 天前
prism WPF 对话框
c#·wpf
baivfhpwxf20235 天前
WPF 登录页面
ui·wpf
baivfhpwxf20236 天前
prism WPF 模块
wpf
baivfhpwxf20236 天前
prism WPF 导航
wpf