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; } }