123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382 |
- using System;
- using System.ComponentModel;
- using System.Windows;
- using System.Windows.Controls;
- using HandyControl.Data;
- using HandyControl.Expression.Drawing;
- namespace HandyControl.Controls;
- public class UniformSpacingPanel : Panel
- {
- private Orientation _orientation;
- public UniformSpacingPanel()
- {
- _orientation = Orientation.Horizontal;
- }
- public static readonly DependencyProperty OrientationProperty =
- StackPanel.OrientationProperty.AddOwner(typeof(UniformSpacingPanel),
- new FrameworkPropertyMetadata(Orientation.Horizontal, FrameworkPropertyMetadataOptions.AffectsMeasure, OnOrientationChanged));
- private static void OnOrientationChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
- {
- var p = (UniformSpacingPanel) d;
- p._orientation = (Orientation) e.NewValue;
- }
- public Orientation Orientation
- {
- get => (Orientation) GetValue(OrientationProperty);
- set => SetValue(OrientationProperty, value);
- }
- public static readonly DependencyProperty ChildWrappingProperty = DependencyProperty.Register(
- nameof(ChildWrapping), typeof(VisualWrapping), typeof(UniformSpacingPanel),
- new FrameworkPropertyMetadata(default(VisualWrapping), FrameworkPropertyMetadataOptions.AffectsMeasure));
- public VisualWrapping ChildWrapping
- {
- get => (VisualWrapping) GetValue(ChildWrappingProperty);
- set => SetValue(ChildWrappingProperty, value);
- }
- public static readonly DependencyProperty SpacingProperty = DependencyProperty.Register(
- nameof(Spacing), typeof(double), typeof(UniformSpacingPanel),
- new FrameworkPropertyMetadata(double.NaN, FrameworkPropertyMetadataOptions.AffectsMeasure), IsSpacingValid);
- public double Spacing
- {
- get => (double) GetValue(SpacingProperty);
- set => SetValue(SpacingProperty, value);
- }
- public static readonly DependencyProperty HorizontalSpacingProperty = DependencyProperty.Register(
- nameof(HorizontalSpacing), typeof(double), typeof(UniformSpacingPanel),
- new FrameworkPropertyMetadata(double.NaN, FrameworkPropertyMetadataOptions.AffectsMeasure), IsSpacingValid);
- public double HorizontalSpacing
- {
- get => (double) GetValue(HorizontalSpacingProperty);
- set => SetValue(HorizontalSpacingProperty, value);
- }
- public static readonly DependencyProperty VerticalSpacingProperty = DependencyProperty.Register(
- nameof(VerticalSpacing), typeof(double), typeof(UniformSpacingPanel),
- new FrameworkPropertyMetadata(double.NaN, FrameworkPropertyMetadataOptions.AffectsMeasure), IsSpacingValid);
- public double VerticalSpacing
- {
- get => (double) GetValue(VerticalSpacingProperty);
- set => SetValue(VerticalSpacingProperty, value);
- }
- public static readonly DependencyProperty ItemWidthProperty = DependencyProperty.Register(
- nameof(ItemWidth), typeof(double), typeof(UniformSpacingPanel),
- new FrameworkPropertyMetadata(double.NaN, FrameworkPropertyMetadataOptions.AffectsMeasure),
- IsWidthHeightValid);
- [TypeConverter(typeof(LengthConverter))]
- public double ItemWidth
- {
- get => (double) GetValue(ItemWidthProperty);
- set => SetValue(ItemWidthProperty, value);
- }
- public static readonly DependencyProperty ItemHeightProperty = DependencyProperty.Register(
- nameof(ItemHeight), typeof(double), typeof(UniformSpacingPanel),
- new FrameworkPropertyMetadata(double.NaN, FrameworkPropertyMetadataOptions.AffectsMeasure),
- IsWidthHeightValid);
- [TypeConverter(typeof(LengthConverter))]
- public double ItemHeight
- {
- get => (double) GetValue(ItemHeightProperty);
- set => SetValue(ItemHeightProperty, value);
- }
- public static readonly DependencyProperty ItemHorizontalAlignmentProperty = DependencyProperty.Register(
- nameof(ItemHorizontalAlignment), typeof(HorizontalAlignment?), typeof(UniformSpacingPanel),
- new FrameworkPropertyMetadata(HorizontalAlignment.Stretch, FrameworkPropertyMetadataOptions.AffectsMeasure));
- public HorizontalAlignment? ItemHorizontalAlignment
- {
- get => (HorizontalAlignment?) GetValue(ItemHorizontalAlignmentProperty);
- set => SetValue(ItemHorizontalAlignmentProperty, value);
- }
- public static readonly DependencyProperty ItemVerticalAlignmentProperty = DependencyProperty.Register(
- nameof(ItemVerticalAlignment), typeof(VerticalAlignment?), typeof(UniformSpacingPanel),
- new FrameworkPropertyMetadata(VerticalAlignment.Stretch, FrameworkPropertyMetadataOptions.AffectsMeasure));
- public VerticalAlignment? ItemVerticalAlignment
- {
- get => (VerticalAlignment?) GetValue(ItemVerticalAlignmentProperty);
- set => SetValue(ItemVerticalAlignmentProperty, value);
- }
- private static bool IsWidthHeightValid(object value)
- {
- var v = (double) value;
- return double.IsNaN(v) || v >= 0.0d && !double.IsPositiveInfinity(v);
- }
- private static bool IsSpacingValid(object value)
- {
- if (value is double spacing)
- {
- return double.IsNaN(spacing) || spacing > 0;
- }
- return false;
- }
- private void ArrangeWrapLine(double v, double lineV, int start, int end, bool useItemU, double itemU, double spacing)
- {
- double u = 0;
- var isHorizontal = _orientation == Orientation.Horizontal;
- var children = InternalChildren;
- for (var i = start; i < end; i++)
- {
- var child = children[i];
- if (child == null) continue;
- var childSize = new PanelUvSize(_orientation, child.DesiredSize);
- var layoutSlotU = useItemU ? itemU : childSize.U;
- child.Arrange(isHorizontal ? new Rect(u, v, layoutSlotU, lineV) : new Rect(v, u, lineV, layoutSlotU));
- if (layoutSlotU > 0)
- {
- u += layoutSlotU + spacing;
- }
- }
- }
- private void ArrangeLine(double lineV, bool useItemU, double itemU, double spacing)
- {
- double u = 0;
- var isHorizontal = _orientation == Orientation.Horizontal;
- var children = InternalChildren;
- for (var i = 0; i < children.Count; i++)
- {
- var child = children[i];
- if (child == null) continue;
- var childSize = new PanelUvSize(_orientation, child.DesiredSize);
- var layoutSlotU = useItemU ? itemU : childSize.U;
- child.Arrange(isHorizontal ? new Rect(u, 0, layoutSlotU, lineV) : new Rect(0, u, lineV, layoutSlotU));
- if (layoutSlotU > 0)
- {
- u += layoutSlotU + spacing;
- }
- }
- }
- protected override Size MeasureOverride(Size constraint)
- {
- var curLineSize = new PanelUvSize(_orientation);
- var panelSize = new PanelUvSize(_orientation);
- var uvConstraint = new PanelUvSize(_orientation, constraint);
- var itemWidth = ItemWidth;
- var itemHeight = ItemHeight;
- var itemWidthSet = !double.IsNaN(itemWidth);
- var itemHeightSet = !double.IsNaN(itemHeight);
- var childWrapping = ChildWrapping;
- var itemHorizontalAlignment = ItemHorizontalAlignment;
- var itemVerticalAlignment = ItemVerticalAlignment;
- var itemHorizontalAlignmentSet = itemHorizontalAlignment != null;
- var itemVerticalAlignmentSet = ItemVerticalAlignment != null;
- var spacingSize = GetSpacingSize();
- var childConstraint = new Size(
- itemWidthSet ? itemWidth : constraint.Width,
- itemHeightSet ? itemHeight : constraint.Height);
- var children = InternalChildren;
- var isFirst = true;
- if (childWrapping == VisualWrapping.Wrap)
- {
- for (int i = 0, count = children.Count; i < count; i++)
- {
- var child = children[i];
- if (child == null) continue;
- if (itemHorizontalAlignmentSet)
- {
- child.SetCurrentValue(HorizontalAlignmentProperty, itemHorizontalAlignment);
- }
- if (itemVerticalAlignmentSet)
- {
- child.SetCurrentValue(VerticalAlignmentProperty, itemVerticalAlignment);
- }
- child.Measure(childConstraint);
- var sz = new PanelUvSize(
- _orientation,
- itemWidthSet ? itemWidth : child.DesiredSize.Width,
- itemHeightSet ? itemHeight : child.DesiredSize.Height);
- if (MathHelper.GreaterThan(curLineSize.U + sz.U + spacingSize.U, uvConstraint.U))
- {
- panelSize.U = Math.Max(curLineSize.U, panelSize.U);
- panelSize.V += curLineSize.V + spacingSize.V;
- curLineSize = sz;
- if (MathHelper.GreaterThan(sz.U, uvConstraint.U))
- {
- panelSize.U = Math.Max(sz.U, panelSize.U);
- panelSize.V += sz.V + spacingSize.V;
- curLineSize = new PanelUvSize(_orientation);
- }
- }
- else
- {
- curLineSize.U += isFirst ? sz.U : sz.U + spacingSize.U;
- curLineSize.V = Math.Max(sz.V, curLineSize.V);
- isFirst = false;
- }
- }
- }
- else
- {
- var layoutSlotSize = constraint;
- if (_orientation == Orientation.Horizontal)
- {
- layoutSlotSize.Width = double.PositiveInfinity;
- }
- else
- {
- layoutSlotSize.Height = double.PositiveInfinity;
- }
- for (int i = 0, count = children.Count; i < count; ++i)
- {
- var child = children[i];
- if (child == null) continue;
- if (itemHorizontalAlignmentSet)
- {
- child.SetCurrentValue(HorizontalAlignmentProperty, itemHorizontalAlignment);
- }
- if (itemVerticalAlignmentSet)
- {
- child.SetCurrentValue(VerticalAlignmentProperty, itemVerticalAlignment);
- }
- child.Measure(layoutSlotSize);
- var sz = new PanelUvSize(
- _orientation,
- itemWidthSet ? itemWidth : child.DesiredSize.Width,
- itemHeightSet ? itemHeight : child.DesiredSize.Height);
- curLineSize.U += isFirst ? sz.U : sz.U + spacingSize.U;
- curLineSize.V = Math.Max(sz.V, curLineSize.V);
- isFirst = false;
- }
- }
- panelSize.U = Math.Max(curLineSize.U, panelSize.U);
- panelSize.V += curLineSize.V;
- return new Size(panelSize.Width, panelSize.Height);
- }
- private PanelUvSize GetSpacingSize()
- {
- var spacing = Spacing;
- if (!double.IsNaN(spacing))
- {
- return new PanelUvSize(_orientation, spacing, spacing);
- }
- var horizontalSpacing = HorizontalSpacing;
- if (double.IsNaN(horizontalSpacing))
- {
- horizontalSpacing = 0;
- }
- var verticalSpacing = VerticalSpacing;
- if (double.IsNaN(verticalSpacing))
- {
- verticalSpacing = 0;
- }
- return new PanelUvSize(_orientation, horizontalSpacing, verticalSpacing);
- }
- protected override Size ArrangeOverride(Size finalSize)
- {
- var firstInLine = 0;
- var itemWidth = ItemWidth;
- var itemHeight = ItemHeight;
- double accumulatedV = 0;
- var itemU = _orientation == Orientation.Horizontal ? itemWidth : itemHeight;
- var curLineSize = new PanelUvSize(_orientation);
- var uvFinalSize = new PanelUvSize(_orientation, finalSize);
- var itemWidthSet = !double.IsNaN(itemWidth);
- var itemHeightSet = !double.IsNaN(itemHeight);
- var useItemU = _orientation == Orientation.Horizontal ? itemWidthSet : itemHeightSet;
- var childWrapping = ChildWrapping;
- var spacingSize = GetSpacingSize();
- var children = InternalChildren;
- var isFirst = true;
- if (childWrapping == VisualWrapping.Wrap)
- {
- for (int i = 0, count = children.Count; i < count; i++)
- {
- var child = children[i];
- if (child == null) continue;
- var sz = new PanelUvSize(
- _orientation,
- itemWidthSet ? itemWidth : child.DesiredSize.Width,
- itemHeightSet ? itemHeight : child.DesiredSize.Height);
- if (MathHelper.GreaterThan(curLineSize.U + (isFirst ? sz.U : sz.U + spacingSize.U), uvFinalSize.U))
- {
- ArrangeWrapLine(accumulatedV, curLineSize.V, firstInLine, i, useItemU, itemU, spacingSize.U);
- accumulatedV += curLineSize.V + spacingSize.V;
- curLineSize = sz;
- firstInLine = i;
- }
- else
- {
- curLineSize.U += isFirst ? sz.U : sz.U + spacingSize.U;
- curLineSize.V = Math.Max(sz.V, curLineSize.V);
- }
- isFirst = false;
- }
- if (firstInLine < children.Count)
- {
- ArrangeWrapLine(accumulatedV, curLineSize.V, firstInLine, children.Count, useItemU, itemU, spacingSize.U);
- }
- }
- else
- {
- ArrangeLine(uvFinalSize.V, useItemU, itemU, spacingSize.U);
- }
- return finalSize;
- }
- }
|