wpf - 具有覆盖的 OnVisualChildrenChanged 的​​自定义 WPF WrapPanel 将无法按预期工作

标签 wpf panel wrappanel rotatetransform

我正在 Windows 上开发一个应用程序,我想要一个像这样布局其子项的面板:

This is my desired layout

我在 WPF 面板中找到的最接近的是垂直方向的 WrapPanel,其布局如下:

This is WrapPanel (with vertical orientation) layout

到目前为止,我的解决方案是从 WrapPanel 创建派生类并将其及其每个子级旋转 180 度,以便实现我的目标:

public class NotificationWrapPanel : WrapPanel
{
    public NotificationWrapPanel()
    {
        this.Orientation = Orientation.Vertical;
        this.RenderTransformOrigin = new Point(.5, .5);
        this.RenderTransform = new RotateTransform(180);
    }
    protected override void OnVisualChildrenChanged(DependencyObject visualAdded, DependencyObject visualRemoved)
    {
        var addedChild = visualAdded as UIElement;
        if (addedChild != null)
        {
            addedChild.RenderTransformOrigin = new Point(.5, .5);
            addedChild.RenderTransform = new RotateTransform(180);
        }

        base.OnVisualChildrenChanged(addedChild, visualRemoved);
    }
}

问题是被覆盖的 OnVisualChildrenChanged 中的 addedChild.RenderTransform = new RotateTransform(180) 部分似乎不起作用。欢迎任何解决方案(即使与我的方法无关)。

更新:

我重新检查了我的代码并意识到我在其他地方更改了 RenderTransform,从而阻止了它的工作。所以我的问题解决了。但是,如果您提供任何可能更优雅的解决方案,我将不胜感激,例如使用 MeasureOverride/ArrangeOverride

最佳答案

根据您的要求,下面是一个自定义面板,我认为它应该可以满足您的需求。它以从下到上然后从右到左的方式排列子元素,在一列中堆叠最多 MaxRows 个元素(或者所有元素,如果它是 null)。所有插槽的大小都相同。它还确实考虑了元素的 Visibility 值,即如果一个项目是 Hidden,它会留下一个空槽,如果它是 Collapsed ,它被跳过,下一个元素“跳”到它的位置。

public class NotificationWrapPanel : Panel
{
    public static readonly DependencyProperty MaxRowsProperty =
        DependencyProperty.Register(
            name: nameof(MaxRows),
            propertyType: typeof(int?),
            ownerType: typeof(NotificationWrapPanel),
            typeMetadata: new FrameworkPropertyMetadata
            {
                AffectsArrange = true,
                AffectsMeasure = true,
            },
            validateValueCallback: o => o == null || (int)o >= 1);

    public int? MaxRows
    {
        get { return (int?)GetValue(MaxRowsProperty); }
        set { SetValue(MaxRowsProperty, value); }
    }

    protected override Size MeasureOverride(Size constraint)
    {
        var children = InternalChildren
            .Cast<UIElement>()
            .Where(e => e.Visibility != Visibility.Collapsed)
            .ToList();
        if (children.Count == 0) return new Size();
        var rows = MaxRows.HasValue ?
            Math.Min(MaxRows.Value, children.Count) :
            children.Count;
        var columns = children.Count / rows +
            Math.Sign(children.Count % rows);
        var childConstraint = new Size
        {
            Width = constraint.Width / columns,
            Height = constraint.Height / rows,
        };
        foreach (UIElement child in children)
            child.Measure(childConstraint);
        return new Size
        {
            Height = rows * children
                .Cast<UIElement>()
                .Max(e => e.DesiredSize.Height),
            Width = columns * children
                .Cast<UIElement>()
                .Max(e => e.DesiredSize.Width),
        };
    }

    protected override Size ArrangeOverride(Size finalSize)
    {
        var children = InternalChildren
            .Cast<UIElement>()
            .Where(e => e.Visibility != Visibility.Collapsed)
            .ToList();
        if (children.Count == 0) return finalSize;
        var rows = MaxRows.HasValue ?
            Math.Min(MaxRows.Value, children.Count) : 
            children.Count;
        var columns = children.Count / rows +
            Math.Sign(children.Count % rows);
        var childSize = new Size
        {
            Width = finalSize.Width / columns,
            Height = finalSize.Height / rows
        };
        for (int i = 0; i < children.Count; i++)
        {
            var row = i % rows; //rows are numbered bottom-to-top
            var col = i / rows; //columns are numbered right-to-left
            var location = new Point
            {
                X = finalSize.Width - (col + 1) * childSize.Width,
                Y = finalSize.Height - (row + 1) * childSize.Height,
            };
            children[i].Arrange(new Rect(location, childSize));
        }
        return finalSize;
    }
}

关于wpf - 具有覆盖的 OnVisualChildrenChanged 的​​自定义 WPF WrapPanel 将无法按预期工作,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/45659206/

相关文章:

.net - 对两个 ControlTemplate 使用相同的定义

r - ggplot2在使用facet时控制每行面板的数量?

javascript - Laravel 第二个 js 文件

WPF 自定义 ItemsControl - 包装问题

c# - 用户控件中的 VisualStateManager

c# - 如何将文本放在 RadioButton 的顶部

wpf - 使用 FlowDocumentScrollViewer 打印所有页面

java - Eclipse项目无法运行

c# - 在 WPF for WINdows 8 中包装面板设计问题

c# - 在运行时重新排列 WrapPanel 中的控件