Files
Shrlalgo.RvKits/WPFluent/Layout/WrapPanel.cs

369 lines
13 KiB
C#
Raw Normal View History

using System;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Markup;
namespace WPFluent.Layout;
[ContentProperty(nameof(Children))]
public class WrapPanel : Panel
{
public static readonly DependencyProperty OrientationProperty =
DependencyProperty.Register(nameof(Orientation), typeof(Orientation), typeof(WrapPanel), new FrameworkPropertyMetadata(Orientation.Horizontal, FrameworkPropertyMetadataOptions.AffectsMeasure));
public Orientation Orientation
{
get => (Orientation)GetValue(OrientationProperty);
set => SetValue(OrientationProperty, value);
}
public static readonly DependencyProperty ItemWidthProperty =
DependencyProperty.Register(nameof(ItemWidth), typeof(double), typeof(WrapPanel), new FrameworkPropertyMetadata(double.NaN, FrameworkPropertyMetadataOptions.AffectsMeasure));
public double ItemWidth
{
get => (double)GetValue(ItemWidthProperty);
set => SetValue(ItemWidthProperty, value);
}
public static readonly DependencyProperty ItemHeightProperty =
DependencyProperty.Register(nameof(ItemHeight), typeof(double), typeof(WrapPanel), new FrameworkPropertyMetadata(double.NaN, FrameworkPropertyMetadataOptions.AffectsMeasure));
public double ItemHeight
{
get => (double)GetValue(ItemHeightProperty);
set => SetValue(ItemHeightProperty, value);
}
public static readonly DependencyProperty VerticalSpacingProperty =
DependencyProperty.Register(nameof(VerticalSpacing), typeof(double), typeof(WrapPanel), new FrameworkPropertyMetadata(0d, FrameworkPropertyMetadataOptions.AffectsMeasure));
public double VerticalSpacing
{
get => (double)GetValue(VerticalSpacingProperty);
set => SetValue(VerticalSpacingProperty, value);
}
public static readonly DependencyProperty HorizontalSpacingProperty =
DependencyProperty.Register(nameof(HorizontalSpacing), typeof(double), typeof(WrapPanel), new FrameworkPropertyMetadata(0d, FrameworkPropertyMetadataOptions.AffectsMeasure));
public double HorizontalSpacing
{
get => (double)GetValue(HorizontalSpacingProperty);
set => SetValue(HorizontalSpacingProperty, value);
}
protected override Size MeasureOverride(Size availableSize)
{
var itemWidth = ItemWidth;
var itemHeight = ItemHeight;
var verticalSpacing = VerticalSpacing;
var horizontalSpacing = HorizontalSpacing;
var offsetX = 0d;
var offsetY = 0d;
var maxLineLength = 0d;
var currentLineSize = 0d;
var currentLineLength = 0d;
Func<Size, double> childWidthGetter = double.IsNaN(itemWidth) ? size => size.Width : size => itemWidth;
Func<Size, double> childHeightGetter = double.IsNaN(itemHeight) ? size => size.Height : size => itemHeight;
var internalChildren = InternalChildren;
if (Orientation == Orientation.Horizontal)
{
availableSize.Height = double.PositiveInfinity;
for (int i = 0; i < internalChildren.Count; i++)
{
var child = internalChildren[i];
child.Measure(availableSize);
var childDesiredSize = child.DesiredSize;
var childWidth = childWidthGetter.Invoke(childDesiredSize);
var childHeight = childHeightGetter.Invoke(childDesiredSize);
offsetX += childWidth;
if (child.Visibility != Visibility.Collapsed)
{
offsetX += horizontalSpacing;
}
if (offsetX - horizontalSpacing > availableSize.Width)
{
currentLineLength = offsetX - horizontalSpacing - childWidth - horizontalSpacing;
if (currentLineLength > maxLineLength)
{
maxLineLength = currentLineLength;
}
offsetX = childWidth + horizontalSpacing;
offsetY += currentLineSize;
offsetY += verticalSpacing;
currentLineSize = 0;
}
if (childHeight > currentLineSize)
{
currentLineSize = childHeight;
}
}
currentLineLength = offsetX - horizontalSpacing;
if (currentLineLength > maxLineLength)
{
maxLineLength = currentLineLength;
}
offsetX = 0;
offsetY += currentLineSize;
offsetY += verticalSpacing;
currentLineSize = 0;
return new Size(maxLineLength, offsetY - verticalSpacing);
}
else
{
availableSize.Width = double.PositiveInfinity;
for (int i = 0; i < internalChildren.Count; i++)
{
var child = internalChildren[i];
child.Measure(availableSize);
var childDesiredSize = child.DesiredSize;
var childWidth = childWidthGetter.Invoke(childDesiredSize);
var childHeight = childHeightGetter.Invoke(childDesiredSize);
offsetY += childHeight;
if (child.Visibility != Visibility.Collapsed)
{
offsetY += verticalSpacing;
}
if (offsetY - verticalSpacing > availableSize.Height)
{
currentLineLength = offsetY - horizontalSpacing - childHeight - verticalSpacing;
if (currentLineLength > maxLineLength)
{
maxLineLength = currentLineLength;
}
offsetY = childHeight + verticalSpacing;
offsetX += currentLineSize;
offsetX += horizontalSpacing;
currentLineSize = 0;
}
if (childWidth > currentLineSize)
currentLineSize = childWidth;
}
currentLineLength = offsetY - verticalSpacing;
if (currentLineLength > maxLineLength)
{
maxLineLength = currentLineLength;
}
offsetY = 0;
offsetX += currentLineSize;
offsetX += horizontalSpacing;
currentLineSize = 0;
return new Size(offsetX - horizontalSpacing, maxLineLength);
}
}
protected override Size ArrangeOverride(Size finalSize)
{
var itemWidth = ItemWidth;
var itemHeight = ItemHeight;
var verticalSpacing = VerticalSpacing;
var horizontalSpacing = HorizontalSpacing;
var tempOffset = 0d;
Func<Size, double> childWidthGetter = double.IsNaN(itemWidth) ? size => size.Width : size => itemWidth;
Func<Size, double> childHeightGetter = double.IsNaN(itemHeight) ? size => size.Height : size => itemHeight;
var internalChildren = InternalChildren;
var currentLineSize = 0d;
var currentLineOffsetX = 0d;
var currentLineOffsetY = 0d;
var currentLineIndexStart = 0;
if (Orientation == Orientation.Horizontal)
{
for (int i = 0; i < internalChildren.Count; i++)
{
var child = internalChildren[i];
var childDesiredSize = child.DesiredSize;
var childWidth = childWidthGetter.Invoke(childDesiredSize);
var childHeight = childHeightGetter.Invoke(childDesiredSize);
tempOffset += childWidth;
if (child.Visibility != Visibility.Collapsed)
{
tempOffset += horizontalSpacing;
}
if (tempOffset - horizontalSpacing > finalSize.Width)
{
ArrangeLineHorizontal(
internalChildren,
currentLineIndexStart,
i,
currentLineOffsetX,
currentLineOffsetY,
currentLineSize,
horizontalSpacing,
childWidthGetter,
childHeightGetter);
currentLineOffsetX = 0;
currentLineOffsetY += currentLineSize;
currentLineOffsetY += verticalSpacing;
currentLineIndexStart = i;
currentLineSize = 0;
tempOffset = childWidth + horizontalSpacing;
}
if (childHeight > currentLineSize)
{
currentLineSize = childHeight;
}
}
ArrangeLineHorizontal(
internalChildren,
currentLineIndexStart,
internalChildren.Count,
currentLineOffsetX,
currentLineOffsetY,
currentLineSize,
horizontalSpacing,
childWidthGetter,
childHeightGetter);
}
else
{
for (int i = 0; i < internalChildren.Count; i++)
{
var child = internalChildren[i];
var childDesiredSize = child.DesiredSize;
var childWidth = childWidthGetter.Invoke(childDesiredSize);
var childHeight = childHeightGetter.Invoke(childDesiredSize);
tempOffset += childHeight;
if (child.Visibility != Visibility.Collapsed)
{
tempOffset += verticalSpacing;
}
if (tempOffset - verticalSpacing > finalSize.Height)
{
ArrangeLineVertical(
internalChildren,
currentLineIndexStart,
i,
currentLineOffsetX,
currentLineOffsetY,
currentLineSize,
verticalSpacing,
childWidthGetter,
childHeightGetter);
currentLineOffsetY = 0;
currentLineOffsetX += currentLineSize;
currentLineOffsetX += horizontalSpacing;
currentLineIndexStart = i;
currentLineSize = 0;
tempOffset = childHeight + verticalSpacing;
}
if (childWidth > currentLineSize)
{
currentLineSize = childWidth;
}
}
ArrangeLineVertical(
internalChildren,
currentLineIndexStart,
internalChildren.Count,
currentLineOffsetX,
currentLineOffsetY,
currentLineSize,
verticalSpacing,
childWidthGetter,
childHeightGetter);
}
return finalSize;
static void ArrangeLineHorizontal(
UIElementCollection children,
int childIndexStart,
int childIndexEnd,
double currentLineOffsetX,
double currentLineOffsetY,
double currentLineSize,
double spacing,
Func<Size, double> widthGetter,
Func<Size, double> heightGetter)
{
var lineChildOffset = 0d;
for (int j = childIndexStart; j < childIndexEnd; j++)
{
var lineChild = children[j];
var lineChildDesiredSize = lineChild.DesiredSize;
var lineChildWidth = widthGetter.Invoke(lineChildDesiredSize);
var lineChildHeight = heightGetter.Invoke(lineChildDesiredSize);
lineChild.Arrange(new Rect(currentLineOffsetX + lineChildOffset, currentLineOffsetY, lineChildWidth, currentLineSize));
lineChildOffset += lineChildWidth;
if (lineChild.Visibility != Visibility.Collapsed)
{
lineChildOffset += spacing;
}
}
}
static void ArrangeLineVertical(
UIElementCollection children,
int childIndexStart,
int childIndexEnd,
double currentLineOffsetX,
double currentLineOffsetY,
double currentLineSize,
double spacing,
Func<Size, double> widthGetter,
Func<Size, double> heightGetter)
{
var lineChildOffset = 0d;
for (int j = childIndexStart; j < childIndexEnd; j++)
{
var lineChild = children[j];
var lineChildDesiredSize = lineChild.DesiredSize;
var lineChildWidth = widthGetter.Invoke(lineChildDesiredSize);
var lineChildHeight = heightGetter.Invoke(lineChildDesiredSize);
lineChild.Arrange(new Rect(currentLineOffsetX, currentLineOffsetY + lineChildOffset, currentLineSize, lineChildHeight));
lineChildOffset += lineChildHeight;
if (lineChild.Visibility != Visibility.Collapsed)
{
lineChildOffset += spacing;
}
}
}
}
}