using System.Windows.Markup; namespace Melskin.Layout; /// /// WaterfallPanel 类继承自 VirtualizingPanel,用于实现瀑布流布局。这种布局方式中,元素按照指定的列数排列,并且在每列之间保持一定的间距。当一列的高度不足以容纳下一个元素时,该元素会自动放置到下一列中,从而形成类似瀑布流动的效果。 /// [ContentProperty(nameof(Children))] public class WaterfallPanel : VirtualizingPanel { /// /// 用于获取或设置 WaterfallPanel 中的列数。此属性定义了布局中元素将被分配到多少列中。 /// public static readonly DependencyProperty ColumnsProperty = DependencyProperty.Register(nameof(Columns), typeof(int), typeof(WaterfallPanel), new PropertyMetadata(3)); /// /// 用于获取或设置 WaterfallPanel 中列与列之间的间距。此属性定义了布局中每列元素之间的距离,以确保元素之间有足够的空间间隔。 /// public static readonly DependencyProperty SpacingProperty = DependencyProperty.Register(nameof(Spacing), typeof(double), typeof(WaterfallPanel), new PropertyMetadata(5.0)); private List columnHeights = []; /// /// 重写基类的 MeasureOverride 方法,以实现瀑布流布局特有的测量逻辑。此方法根据可用空间和当前设置的列数及间距来计算每个子元素的位置,并返回整个面板期望的大小。 /// /// 一个 Size 结构,表示 WaterfallPanel 可用的空间。 /// 一个 Size 结构,代表经过测量后 WaterfallPanel 的期望尺寸。 protected override Size MeasureOverride(Size availableSize) { columnHeights.Clear(); var panelDesiredSize = new Size(0, 0); columnHeights = new double[Columns].ToList(); double currentX = 0; var width = availableSize.Width / Columns - (Columns * Spacing); for(var i = 0; i < InternalChildren.Count; i++) { if(InternalChildren[i] is not FrameworkElement child) continue; child.Measure(availableSize); child.Width = width; var columnIndex = i % Columns; var x = columnIndex != 0 ? currentX + Spacing : 0; var y = columnHeights[columnIndex]; if(i >= Columns) y = y + Spacing; var size = new Size(width, child.DesiredSize.Height); child.Arrange(new Rect(new Point(x, y), size)); panelDesiredSize.Width = Math.Max(panelDesiredSize.Width, x + child.DesiredSize.Width); panelDesiredSize.Height = Math.Max(panelDesiredSize.Height, y + child.DesiredSize.Height); currentX = x + size.Width; if(currentX >= Width) currentX = 0; columnHeights[columnIndex] += child.DesiredSize.Height + (i >= Columns ? Spacing : 0); } return panelDesiredSize; } /// /// 向 WaterfallPanel 中添加一个新的子元素。 /// /// 要添加到 WaterfallPanel 中的 UIElement。 public void AddChild(UIElement element) { Children.Add(element); } /// /// 获取或设置 WaterfallPanel 中的列数。此属性决定了布局中元素将被分配到多少列中,从而影响瀑布流布局的整体外观。 /// public int Columns { get => (int)GetValue(ColumnsProperty); set => SetValue(ColumnsProperty, value); } /// /// 用于获取或设置 WaterfallPanel 中列与列之间的间距。此属性定义了布局中每列元素之间的距离,以确保元素之间有足够的空间间隔。 /// public double Spacing { get => (double)GetValue(SpacingProperty); set => SetValue(SpacingProperty, value); } }