控件功能
根据自定义行和列,快速进行排列,能够进行自定义控件间距离,减少元素进行定义间距,同时能更好的维护界面排序。
代码部分
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>
图示
