WPF-Grid自动排列(一个附加属性)
目录
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Windows;
using System.Windows.Controls;
namespace WpfApp7
{
static class GridAssist
{
#region AutoRowColumn
public static string GetAutoRowColumn(DependencyObject obj)
{
return (string)obj.GetValue(AutoRowColumnProperty);
}
public static void SetAutoRowColumn(DependencyObject obj, string value)
{
obj.SetValue(AutoRowColumnProperty, value);
}
/// <summary>
/// 自动排列 Grid 容器中的所有控件
/// </summary>
/// <remarks>
/// 值为一个逗号隔开的字符串,形如:<br/>
/// - 2,3(2 行 3 列,列宽默认为 1*,行高为 Auto)<br/>
/// - _,3(3 列,行数根据子控件而定)<br/>
/// - 2,3,Auto(列的宽度为 Auto)<br/>
/// Grid 中的所有控件将自动按照从左到右、从上到下的顺序进行排列<br/>
/// 控件各自的 RowSpan 以及 ColumnSpan 也会被考虑
/// </remarks>
/// <example>
/// <code>
/// <![CDATA[
/// <Window xmlns:ap="clr-namespace:NemoDemo.AttachedProperties">
/// <Grid ap:GridHelper.AutoRowColumn="2,3">
/// <Button /> // 1,1
/// <Label /> // 1,2
/// <TextBox /> // 1,3
/// <Button /> // 2,1
/// <Label Grid.ColumnSpan="2" /> // 2,2-3
/// </Grid>
/// </Window>
/// ]]>
/// </code>
/// </example>
public static readonly DependencyProperty AutoRowColumnProperty = DependencyProperty.RegisterAttached(
"AutoRowColumn",
typeof(string),
typeof(GridAssist),
new FrameworkPropertyMetadata("", FrameworkPropertyMetadataOptions.AffectsMeasure, OnAutoRowColumnChanged)
);
private static void OnAutoRowColumnChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
{
if (!(d is Grid grid))
{
return;
}
var value = e.NewValue as string;
if (string.IsNullOrEmpty(value))
{
grid.RowDefinitions.Clear();
grid.ColumnDefinitions.Clear();
grid.Loaded -= OnGridLoaded;
return;
}
grid.Loaded += OnGridLoaded;
if (grid.IsLoaded)
{
OnGridLoaded(grid, null);
}
}
private static void OnGridLoaded(object sender, RoutedEventArgs e)
{
var grid = sender as Grid;
// 列宽,默认为 Star,即平均分布
var width = new GridLength(1.0, GridUnitType.Star);
var split = GridAssist.GetAutoRowColumn(grid).Split(',');
var r = split[0] != "_" ? int.Parse(split[0]) : 1;
var c = int.Parse(split[1]);
// 如果有第三个参数且值为 auto,则宽度为 Auto
if (split.Length == 3 && split[2].Equals("auto", StringComparison.OrdinalIgnoreCase))
{
width = GridLength.Auto;
}
grid.RowDefinitions.Clear();
grid.ColumnDefinitions.Clear();
for (int i = 0; i < r; i++)
{
grid.RowDefinitions.Add(new RowDefinition { Height = GridLength.Auto });
}
for (int i = 0; i < c; i++)
{
grid.ColumnDefinitions.Add(new ColumnDefinition { Width = width });
}
var cols = grid.ColumnDefinitions.Count;
var map = new List<bool[]>();
int x = 0,
y = 0;
foreach (UIElement item in grid.Children)
{
var rowSpan = Grid.GetRowSpan(item);
var colSpan = Grid.GetColumnSpan(item);
// 默认从上到下,从左到右,即任何时候控件的下方和右方都是空的
// 可能会出现中途有一个 RowSpan > 1 导致其左下方的右侧不为空的情况,暂不处理
// 同时默认当前的 (x, y) 位置是一个可用位置
// 当前控件占据的格子
for (int i = 0; i < rowSpan; i++)
{
// 如果 RowDefinition 不够用,则自动添加
if (map.Count <= y + i)
{
while (map.Count <= y + i)
{
grid.RowDefinitions.Add(new RowDefinition { Height = GridLength.Auto });
map.Add(new bool[cols]);
}
}
map[y + i][x] = true;
}
for (int i = 0; i < colSpan; i++)
{
if (x + i >= cols)
{
break;
}
map[y][x + i] = true;
}
Grid.SetRow(item, y);
Grid.SetColumn(item, x);
// 寻找下一个可用的格子
while (map[y][x])
{
x++;
if (x >= cols)
{
x = 0;
y++;
if (y >= map.Count)
{
grid.RowDefinitions.Add(new RowDefinition { Height = GridLength.Auto });
map.Add(new bool[cols]);
}
}
}
}
}
#endregion
}
}<Window
x:Class="WpfApp7.MainWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:local="clr-namespace:WpfApp7"
Title="MainWindow"
Width="500"
Height="350">
<Grid Margin="20" local:GridAssist.AutoRowColumn="2,3">
<Button Margin="5" Content="Button 1" />
<Label
Margin="5"
Background="LightBlue"
Content="Label 1" />
<TextBox Margin="5" Text="TextBox 1" />
<Button Margin="5" Content="Button 2" />
<Label
Grid.ColumnSpan="2"
Margin="5"
Background="LightGreen"
Content="Span 2 Columns" />
</Grid>
</Window>自动行数
AutoRowColumn="_,3"含义:
3 columns
rows auto expandAuto 列宽
AutoRowColumn="2,3,Auto"列宽:
ColumnWidth = Auto不是 *。