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>

图示

相关推荐
Java Fans5 小时前
在WPF项目中集成Python:Python.NET深度实战指南
python·.net·wpf
布伦鸽12 小时前
C# WPF 左右布局实现学习笔记(1)
笔记·学习·c#·wpf
code bean2 天前
【WPF】WPF 项目实战:构建一个可增删、排序的光源类型管理界面(含源码)
wpf
沉到海底去吧Go2 天前
【图片识别改名】如何批量将图片按图片上文字重命名?自动批量识别图片文字并命名,基于图片文字内容改名,WPF和京东ocr识别的解决方案
ocr·wpf·图片识别改名·图片识别重命名·图片内容改名
lph19722 天前
自定义事件wpf
wpf
code bean2 天前
【WPF】从普通 ItemsControl 到支持筛选的 ItemsControl:深入掌握 CollectionViewSource 用法
wpf
碎碎念的安静2 天前
WPF可拖拽ListView
c#·wpf
界面开发小八哥3 天前
界面组件DevExpress WPF中文教程:Grid - 如何识别行和卡片?
.net·wpf·界面控件·devexpress·ui开发
TwilightLemon4 天前
WPF 使用CompositionTarget.Rendering实现平滑流畅滚动的ScrollViewer,支持滚轮、触控板、触摸屏和笔
wpf
Vae_Mars5 天前
WPF中自定义消息弹窗
wpf