256 lines
9.0 KiB
C#
256 lines
9.0 KiB
C#
|
|
using System.Windows;
|
|||
|
|
using System.Windows.Controls;
|
|||
|
|
|
|||
|
|
namespace YY.Admin.Core.Controls;
|
|||
|
|
|
|||
|
|
public class GridPanel : Panel
|
|||
|
|
{
|
|||
|
|
// ------------------------------------------------------------
|
|||
|
|
// 间距属性(行列间距)
|
|||
|
|
// ------------------------------------------------------------
|
|||
|
|
public double Gap
|
|||
|
|
{
|
|||
|
|
get => (double)GetValue(GapProperty);
|
|||
|
|
set => SetValue(GapProperty, value);
|
|||
|
|
}
|
|||
|
|
public static readonly DependencyProperty GapProperty =
|
|||
|
|
DependencyProperty.Register(nameof(Gap), typeof(double), typeof(GridPanel),
|
|||
|
|
new FrameworkPropertyMetadata(20.0, FrameworkPropertyMetadataOptions.AffectsMeasure));
|
|||
|
|
|
|||
|
|
// ------------------------------------------------------------
|
|||
|
|
// 横向/纵向布局(类似 CSS flow)
|
|||
|
|
// ------------------------------------------------------------
|
|||
|
|
public Orientation Orientation
|
|||
|
|
{
|
|||
|
|
get => (Orientation)GetValue(OrientationProperty);
|
|||
|
|
set => SetValue(OrientationProperty, value);
|
|||
|
|
}
|
|||
|
|
public static readonly DependencyProperty OrientationProperty =
|
|||
|
|
DependencyProperty.Register(nameof(Orientation), typeof(Orientation), typeof(GridPanel),
|
|||
|
|
new FrameworkPropertyMetadata(Orientation.Horizontal, FrameworkPropertyMetadataOptions.AffectsMeasure));
|
|||
|
|
|
|||
|
|
// ------------------------------------------------------------
|
|||
|
|
// 测量
|
|||
|
|
// ------------------------------------------------------------
|
|||
|
|
protected override Size MeasureOverride(Size availableSize)
|
|||
|
|
{
|
|||
|
|
return Orientation == Orientation.Horizontal
|
|||
|
|
? MeasureHorizontal(availableSize)
|
|||
|
|
: MeasureVertical(availableSize);
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
// ------------------------------------------------------------
|
|||
|
|
// 布局
|
|||
|
|
// ------------------------------------------------------------
|
|||
|
|
protected override Size ArrangeOverride(Size finalSize)
|
|||
|
|
{
|
|||
|
|
return Orientation == Orientation.Horizontal
|
|||
|
|
? ArrangeHorizontal(finalSize)
|
|||
|
|
: ArrangeVertical(finalSize);
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
// ------------------------------------------------------------
|
|||
|
|
// 横向布局(类似 CSS grid-template-columns)
|
|||
|
|
// ------------------------------------------------------------
|
|||
|
|
private Size MeasureHorizontal(Size availableSize)
|
|||
|
|
{
|
|||
|
|
double panelWidth = double.IsInfinity(availableSize.Width)
|
|||
|
|
? MinWidth * 4 + Gap * 3
|
|||
|
|
: availableSize.Width;
|
|||
|
|
|
|||
|
|
// ---- 修复:不能用 MinWidth+Gap 来估算列数,应使用最小子项宽度 ----
|
|||
|
|
double minChildWidth = MinWidth <= 0 ? 1 : MinWidth;
|
|||
|
|
|
|||
|
|
// ---- 修复:列数 = 能放下多少个最小宽度 ----
|
|||
|
|
int columnCount = Math.Max(1, (int)Math.Floor((panelWidth + Gap) / (minChildWidth + Gap)));
|
|||
|
|
|
|||
|
|
int childCount = InternalChildren.Count;
|
|||
|
|
// ---- 修复2:如果列数超过子项数,裁到子项数(实现 auto-fit 行为) ----
|
|||
|
|
if (childCount > 0 && columnCount > childCount)
|
|||
|
|
{
|
|||
|
|
columnCount = childCount;
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
double columnWidth = (panelWidth - Gap * (columnCount - 1)) / columnCount;
|
|||
|
|
|
|||
|
|
double totalHeight = 0;
|
|||
|
|
double rowHeight = 0;
|
|||
|
|
int col = 0;
|
|||
|
|
|
|||
|
|
for (int i = 0; i < childCount; i++)
|
|||
|
|
{
|
|||
|
|
UIElement child = InternalChildren[i];
|
|||
|
|
child.Measure(new Size(columnWidth, double.PositiveInfinity));
|
|||
|
|
rowHeight = Math.Max(rowHeight, child.DesiredSize.Height);
|
|||
|
|
col++;
|
|||
|
|
|
|||
|
|
if (col >= columnCount || i == childCount - 1)
|
|||
|
|
{
|
|||
|
|
// ------------------------------------------------------------
|
|||
|
|
// 最后一行不加Gap
|
|||
|
|
// ------------------------------------------------------------
|
|||
|
|
totalHeight += rowHeight;
|
|||
|
|
if (i != childCount - 1) totalHeight += Gap;
|
|||
|
|
|
|||
|
|
col = 0;
|
|||
|
|
rowHeight = 0;
|
|||
|
|
}
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
return new Size(panelWidth, totalHeight);
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
private Size ArrangeHorizontal(Size finalSize)
|
|||
|
|
{
|
|||
|
|
double panelWidth = finalSize.Width;
|
|||
|
|
|
|||
|
|
// ---- 修复:与 Measure 保持一致 ----
|
|||
|
|
double minChildWidth = MinWidth <= 0 ? 1 : MinWidth;
|
|||
|
|
int columnCount = Math.Max(1, (int)Math.Floor((panelWidth + Gap) / (minChildWidth + Gap)));
|
|||
|
|
|
|||
|
|
int childCount = InternalChildren.Count;
|
|||
|
|
// ---- 修复2:裁到子项数以实现 auto-fit ----
|
|||
|
|
if (childCount > 0 && columnCount > childCount)
|
|||
|
|
{
|
|||
|
|
columnCount = childCount;
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
double columnWidth = (panelWidth - Gap * (columnCount - 1)) / columnCount;
|
|||
|
|
|
|||
|
|
double x = 0;
|
|||
|
|
double y = 0;
|
|||
|
|
double rowHeight = 0;
|
|||
|
|
int col = 0;
|
|||
|
|
|
|||
|
|
for (int i = 0; i < childCount; i++)
|
|||
|
|
{
|
|||
|
|
UIElement child = InternalChildren[i];
|
|||
|
|
// 为了保证每列宽度一致,传入 columnWidth 并在 Arrange 时使用 columnWidth
|
|||
|
|
child.Arrange(new Rect(x, y, columnWidth, child.DesiredSize.Height));
|
|||
|
|
rowHeight = Math.Max(rowHeight, child.DesiredSize.Height);
|
|||
|
|
col++;
|
|||
|
|
|
|||
|
|
if (col >= columnCount || i == childCount - 1)
|
|||
|
|
{
|
|||
|
|
x = 0;
|
|||
|
|
|
|||
|
|
// ------------------------------------------------------------
|
|||
|
|
// 最后一行不加Gap
|
|||
|
|
// ------------------------------------------------------------
|
|||
|
|
if (i != childCount - 1) y += rowHeight + Gap;
|
|||
|
|
else y += rowHeight;
|
|||
|
|
|
|||
|
|
col = 0;
|
|||
|
|
rowHeight = 0;
|
|||
|
|
}
|
|||
|
|
else
|
|||
|
|
{
|
|||
|
|
x += columnWidth + Gap;
|
|||
|
|
}
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
return finalSize;
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
// ------------------------------------------------------------
|
|||
|
|
// 纵向布局(类似 CSS grid-template-rows)
|
|||
|
|
// ------------------------------------------------------------
|
|||
|
|
private Size MeasureVertical(Size availableSize)
|
|||
|
|
{
|
|||
|
|
double panelHeight = double.IsInfinity(availableSize.Height)
|
|||
|
|
? MinHeight * 4 + Gap * 3
|
|||
|
|
: availableSize.Height;
|
|||
|
|
|
|||
|
|
// ---- 修复:使用最小子项高度,不能用 MinHeight+Gap ----
|
|||
|
|
double minChildHeight = MinHeight <= 0 ? 1 : MinHeight;
|
|||
|
|
|
|||
|
|
// ---- 修复:行数 = 能放下多少个最小高度 ----
|
|||
|
|
int rowCount = Math.Max(1, (int)Math.Floor((panelHeight + Gap) / (minChildHeight + Gap)));
|
|||
|
|
|
|||
|
|
int childCount = InternalChildren.Count;
|
|||
|
|
// ---- 修复2:如果行数超过子项数,裁到子项数(auto-fit) ----
|
|||
|
|
if (childCount > 0 && rowCount > childCount)
|
|||
|
|
{
|
|||
|
|
rowCount = childCount;
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
double rowHeight = (panelHeight - Gap * (rowCount - 1)) / rowCount;
|
|||
|
|
|
|||
|
|
double totalWidth = 0;
|
|||
|
|
double columnWidth = 0;
|
|||
|
|
int row = 0;
|
|||
|
|
|
|||
|
|
for (int i = 0; i < childCount; i++)
|
|||
|
|
{
|
|||
|
|
UIElement child = InternalChildren[i];
|
|||
|
|
child.Measure(new Size(double.PositiveInfinity, rowHeight));
|
|||
|
|
columnWidth = Math.Max(columnWidth, child.DesiredSize.Width);
|
|||
|
|
row++;
|
|||
|
|
|
|||
|
|
if (row >= rowCount || i == childCount - 1)
|
|||
|
|
{
|
|||
|
|
// ------------------------------------------------------------
|
|||
|
|
// 最后一列不加Gap
|
|||
|
|
// ------------------------------------------------------------
|
|||
|
|
totalWidth += columnWidth;
|
|||
|
|
if (i != childCount - 1) totalWidth += Gap;
|
|||
|
|
|
|||
|
|
row = 0;
|
|||
|
|
columnWidth = 0;
|
|||
|
|
}
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
return new Size(totalWidth, panelHeight);
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
private Size ArrangeVertical(Size finalSize)
|
|||
|
|
{
|
|||
|
|
double panelHeight = finalSize.Height;
|
|||
|
|
|
|||
|
|
// ---- 修复:与 MeasureVertical 一致 ----
|
|||
|
|
double minChildHeight = MinHeight <= 0 ? 1 : MinHeight;
|
|||
|
|
int rowCount = Math.Max(1, (int)Math.Floor((panelHeight + Gap) / (minChildHeight + Gap)));
|
|||
|
|
|
|||
|
|
int childCount = InternalChildren.Count;
|
|||
|
|
// ---- 修复2:裁到子项数 ----
|
|||
|
|
if (childCount > 0 && rowCount > childCount)
|
|||
|
|
{
|
|||
|
|
rowCount = childCount;
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
double rowHeight = (panelHeight - Gap * (rowCount - 1)) / rowCount;
|
|||
|
|
|
|||
|
|
double x = 0;
|
|||
|
|
double y = 0;
|
|||
|
|
double columnWidth = 0;
|
|||
|
|
int row = 0;
|
|||
|
|
|
|||
|
|
for (int i = 0; i < childCount; i++)
|
|||
|
|
{
|
|||
|
|
UIElement child = InternalChildren[i];
|
|||
|
|
child.Arrange(new Rect(x, y, child.DesiredSize.Width, rowHeight));
|
|||
|
|
columnWidth = Math.Max(columnWidth, child.DesiredSize.Width);
|
|||
|
|
row++;
|
|||
|
|
|
|||
|
|
if (row >= rowCount || i == childCount - 1)
|
|||
|
|
{
|
|||
|
|
// ------------------------------------------------------------
|
|||
|
|
// 最后一列不加Gap
|
|||
|
|
// ------------------------------------------------------------
|
|||
|
|
x += columnWidth;
|
|||
|
|
if (i != childCount - 1) x += Gap;
|
|||
|
|
|
|||
|
|
y = 0;
|
|||
|
|
row = 0;
|
|||
|
|
columnWidth = 0;
|
|||
|
|
}
|
|||
|
|
else
|
|||
|
|
{
|
|||
|
|
y += rowHeight + Gap;
|
|||
|
|
}
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
return finalSize;
|
|||
|
|
}
|
|||
|
|
}
|