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>

图示

相关推荐
Macbethad6 小时前
工业设备数据采集主站程序技术方案
wpf
关关长语15 小时前
HandyControl 3.5.x 版本 ListViewItem不显示问题
windows·wpf
Macbethad15 小时前
工业设备维护程序技术方案
wpf
Macbethad16 小时前
工业设备配方管理系统技术方案
wpf
喵叔哟17 小时前
7.日志系统深入
wpf
清风徐来Groot18 小时前
WPF布局之Grid
wpf
清风徐来Groot19 小时前
WPF布局之WrapPanel
wpf
Macbethad19 小时前
WPF工业设备工艺配方流程程序技术方案
wpf
清风徐来Groot19 小时前
WPF布局之UniformGrid
wpf
清风徐来Groot19 小时前
WPF布局之StackPanel
wpf