WPF中的DataGrid自动行列显示
新建wpf页面DataGridAutoView
引用空间: xmlns:ga="clr-namespace:WPFDemoMVVM.Helpers"
<Window x:Class="WPFDemoMVVM.View.DataGridAutoView"
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:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
xmlns:local="clr-namespace:WPFDemoMVVM.View"
xmlns:ga="clr-namespace:WPFDemoMVVM.Helpers"
mc:Ignorable="d"
Title="DataGridAutoView" Height="450" Width="800">
<Grid>
<Grid ga:GridAssist.AutoRowColumn="_,2">
<Grid.Resources>
<Style TargetType="Label" BasedOn="{StaticResource {x:Type Label}}">
<Setter Property="VerticalContentAlignment" Value="Center"></Setter>
<Setter Property="Width" Value="120"></Setter>
<Setter Property="Height" Value="40"></Setter>
</Style>
<Style TargetType="TextBox" BasedOn="{StaticResource {x:Type TextBox}}">
<Setter Property="VerticalAlignment" Value="Center"></Setter>
<Setter Property="Width" Value="180"></Setter>
</Style>
</Grid.Resources>
<Label Content="IP Address"></Label>
<TextBox Text="127.0.0.1"></TextBox>
<Label Content="Port"></Label>
<TextBox Text="8080"></TextBox>
<Label Content="TimeOut"></Label>
<TextBox Text="10000"></TextBox>
</Grid>
</Grid>
</Window>
新建一个类GridAssist
在命名空间clr-namespace:WPFDemoMVVM.Helpers下,新建GridAssist类
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
}