/* This file is generated by DeepSeek */ using System; using System.Windows; using System.Windows.Controls; namespace PCL.Core.UI.Controls; /// /// 瀑布流布局模式 /// public enum WaterfallLayoutMode { /// /// 固定列数模式,使用 ColumnCount 属性指定列数 /// FixedColumns, /// /// 自适应模式,根据可用宽度和 MaxItemWidth 自动计算列数 /// AutoFit } public class WaterfallPanel : Panel { // 私有字段,存储 Measure 阶段计算出的实际列数(用于 Arrange 阶段) private int _computedColumnCount; #region 依赖属性 /// /// 布局模式(固定列数 / 自适应) /// public WaterfallLayoutMode LayoutMode { get { return (WaterfallLayoutMode)GetValue(LayoutModeProperty); } set { SetValue(LayoutModeProperty, value); } } public static readonly DependencyProperty LayoutModeProperty = DependencyProperty.Register(nameof(LayoutMode), typeof(WaterfallLayoutMode), typeof(WaterfallPanel), new FrameworkPropertyMetadata(WaterfallLayoutMode.FixedColumns, FrameworkPropertyMetadataOptions.AffectsMeasure)); /// /// 固定列数(仅在 LayoutMode = FixedColumns 时生效) /// public int ColumnCount { get { return (int)GetValue(ColumnCountProperty); } set { SetValue(ColumnCountProperty, value); } } public static readonly DependencyProperty ColumnCountProperty = DependencyProperty.Register(nameof(ColumnCount), typeof(int), typeof(WaterfallPanel), new FrameworkPropertyMetadata(2, FrameworkPropertyMetadataOptions.AffectsMeasure)); /// /// 列间距 /// public double ColumnSpacing { get { return (double)GetValue(ColumnSpacingProperty); } set { SetValue(ColumnSpacingProperty, value); } } public static readonly DependencyProperty ColumnSpacingProperty = DependencyProperty.Register(nameof(ColumnSpacing), typeof(double), typeof(WaterfallPanel), new FrameworkPropertyMetadata(5.0, FrameworkPropertyMetadataOptions.AffectsMeasure)); /// /// 行间距 /// public double RowSpacing { get { return (double)GetValue(RowSpacingProperty); } set { SetValue(RowSpacingProperty, value); } } public static readonly DependencyProperty RowSpacingProperty = DependencyProperty.Register(nameof(RowSpacing), typeof(double), typeof(WaterfallPanel), new FrameworkPropertyMetadata(0.0, FrameworkPropertyMetadataOptions.AffectsMeasure)); /// /// 项的最大宽度(仅在 LayoutMode = AutoFit 时生效) /// public double MaxItemWidth { get { return (double)GetValue(MaxItemWidthProperty); } set { SetValue(MaxItemWidthProperty, value); } } public static readonly DependencyProperty MaxItemWidthProperty = DependencyProperty.Register(nameof(MaxItemWidth), typeof(double), typeof(WaterfallPanel), new FrameworkPropertyMetadata(100.0, FrameworkPropertyMetadataOptions.AffectsMeasure)); #endregion protected override Size MeasureOverride(Size availableSize) { // 1. 确定列数 int columnCount; if (LayoutMode == WaterfallLayoutMode.FixedColumns) { columnCount = ColumnCount; } else // AutoFit { if (double.IsInfinity(availableSize.Width) || double.IsNaN(availableSize.Width)) { // 宽度无限时,回退到固定列数模式(使用 ColumnCount 作为后备) columnCount = ColumnCount; } else { double effectiveMaxWidth = MaxItemWidth > 0 ? MaxItemWidth : 100; // 防御性处理 double slotWidth = effectiveMaxWidth + ColumnSpacing; columnCount = Math.Max(1, (int)Math.Floor((availableSize.Width + ColumnSpacing) / slotWidth)); } } _computedColumnCount = columnCount; // 2. 计算每列的实际宽度 double colWidth = (availableSize.Width - (columnCount - 1) * ColumnSpacing) / columnCount; colWidth = Math.Max(0, colWidth); // 避免负宽度 // 3. 记录每列的当前高度(用于测量时决定元素放入哪一列) double[] colHeights = new double[columnCount]; // 4. 测量所有子元素 foreach (UIElement child in InternalChildren) { // 约束子元素宽度为列宽,高度不限 child.Measure(new Size(colWidth, double.PositiveInfinity)); Size desiredSize = child.DesiredSize; // 找到当前高度最小的列 int minColIndex = _GetMinHeightColumnIndex(colHeights); double y = colHeights[minColIndex]; // 当前列已占高度,即新元素的 Y 坐标 // 更新该列高度:加上元素高度和行间距(为下一个元素预留) colHeights[minColIndex] += desiredSize.Height + RowSpacing; } // 5. 计算面板所需总高度(取最高列的实际内容高度,需减去最后一个多余的行间距) double totalHeight = 0; for (int i = 0; i < colHeights.Length; i++) { if (colHeights[i] > 0) totalHeight = Math.Max(totalHeight, colHeights[i] - RowSpacing); else totalHeight = Math.Max(totalHeight, colHeights[i]); } return new Size(availableSize.Width, totalHeight); } protected override Size ArrangeOverride(Size finalSize) { if (_computedColumnCount <= 0) return finalSize; // 使用 Measure 阶段确定的列数,但根据 finalSize 重新计算列宽(保证填满可用宽度) double colWidth = (finalSize.Width - (_computedColumnCount - 1) * ColumnSpacing) / _computedColumnCount; colWidth = Math.Max(0, colWidth); double[] colHeights = new double[_computedColumnCount]; foreach (UIElement child in InternalChildren) { int colIndex = _GetMinHeightColumnIndex(colHeights); double x = colIndex * (colWidth + ColumnSpacing); double y = colHeights[colIndex]; child.Arrange(new Rect(new Point(x, y), new Size(colWidth, child.DesiredSize.Height))); // 更新该列高度:加上元素高度和行间距(为下一个元素定位) colHeights[colIndex] += child.DesiredSize.Height + RowSpacing; } return finalSize; } /// /// 获取当前高度最小的列索引 /// private int _GetMinHeightColumnIndex(double[] colHeights) { int minIndex = 0; double minHeight = colHeights[0]; for (int i = 1; i < colHeights.Length; i++) { if (colHeights[i] < minHeight) { minHeight = colHeights[i]; minIndex = i; } } return minIndex; } }