using System.Linq; using System.Windows; using System.Windows.Controls; using HandyControl.Data; using HandyControl.Expression.Drawing; using HandyControl.Tools; namespace HandyControl.Controls; public class WaterfallPanel : Panel { public static readonly DependencyProperty GroupsProperty = DependencyProperty.Register( nameof(Groups), typeof(int), typeof(WaterfallPanel), new FrameworkPropertyMetadata( ValueBoxes.Int2Box, FrameworkPropertyMetadataOptions.AffectsMeasure), IsGroupsValid); public int Groups { get => (int) GetValue(GroupsProperty); set => SetValue(GroupsProperty, value); } private static bool IsGroupsValid(object value) => (int) value >= 1; public static readonly DependencyProperty AutoGroupProperty = DependencyProperty.Register( nameof(AutoGroup), typeof(bool), typeof(WaterfallPanel), new FrameworkPropertyMetadata( ValueBoxes.FalseBox, FrameworkPropertyMetadataOptions.AffectsMeasure)); public bool AutoGroup { get => (bool) GetValue(AutoGroupProperty); set => SetValue(AutoGroupProperty, ValueBoxes.BooleanBox(value)); } public static readonly DependencyProperty DesiredLengthProperty = DependencyProperty.Register( nameof(DesiredLength), typeof(double), typeof(WaterfallPanel), new FrameworkPropertyMetadata(ValueBoxes.Double0Box, FrameworkPropertyMetadataOptions.AffectsMeasure), ValidateHelper.IsInRangeOfPosDoubleIncludeZero); public double DesiredLength { get => (double) GetValue(DesiredLengthProperty); set => SetValue(DesiredLengthProperty, value); } public static readonly DependencyProperty OrientationProperty = StackPanel.OrientationProperty.AddOwner(typeof(WaterfallPanel), new FrameworkPropertyMetadata(Orientation.Horizontal, FrameworkPropertyMetadataOptions.AffectsMeasure)); public Orientation Orientation { get => (Orientation) GetValue(OrientationProperty); set => SetValue(OrientationProperty, value); } private int CaculateGroupCount(Orientation orientation, PanelUvSize size) { if (!AutoGroup) { return Groups; } var itemLength = DesiredLength; if (MathHelper.IsVerySmall(itemLength)) { return Groups; } return (int) (size.U / itemLength); } protected override Size ArrangeOverride(Size finalSize) { var orientation = Orientation; var uvConstraint = new PanelUvSize(orientation, finalSize); var groups = CaculateGroupCount(orientation, uvConstraint); if (groups < 1) { return finalSize; } var vArr = new double[groups].ToList(); var itemU = uvConstraint.U / groups; var children = InternalChildren; for (int i = 0, count = children.Count; i < count; i++) { var child = children[i]; if (child == null) { continue; } var minIndex = vArr.IndexOf(vArr.Min()); var minV = vArr[minIndex]; var childUvSize = new PanelUvSize(orientation, child.DesiredSize); var childSize = new PanelUvSize(orientation, itemU, childUvSize.V); var childRectSize = new PanelUvSize(orientation, minIndex * itemU, minV); child.Arrange(new Rect(new Point(childRectSize.U, childRectSize.V), childSize.ScreenSize)); vArr[minIndex] = minV + childUvSize.V; } return finalSize; } protected override Size MeasureOverride(Size constraint) { var orientation = Orientation; var uvConstraint = new PanelUvSize(orientation, constraint); var groups = CaculateGroupCount(orientation, uvConstraint); if (groups < 1) { return constraint; } var vArr = new double[groups].ToList(); var itemU = uvConstraint.U / groups; if (double.IsNaN(itemU) || double.IsInfinity(itemU)) { return constraint; } var children = InternalChildren; for (int i = 0, count = children.Count; i < count; i++) { var child = children[i]; if (child == null) { continue; } child.Measure(constraint); var sz = new PanelUvSize(orientation, child.DesiredSize); var minIndex = vArr.IndexOf(vArr.Min()); var minV = vArr[minIndex]; vArr[minIndex] = minV + sz.V; } uvConstraint = new PanelUvSize(orientation, new Size(uvConstraint.ScreenSize.Width, vArr.Max())); return uvConstraint.ScreenSize; } }