根据控件的数量在一个或多个水平行中显示WPF元素?[副本]

ivqmmu1c  于 2023-05-13  发布在  其他
关注(0)|答案(1)|浏览(94)

此问题已在此处有答案

How to display WPF elements in one or more horizontal lines dependent on number of controls? [closed](1个答案)
14小时前关闭
我的用例-我有一个固定高度(比如40 px)的按钮水平布局(目前是StackPanel)。按钮排成一行,每个按钮高度为40。按钮可以有不同的宽度(取决于内容-文本)。用户可以编辑按钮文本,因此宽度可以在运行时更改。
我需要一种方法来处理的情况下,按钮溢出可用的水平空间没有滚动。在这种情况下,用户希望将布局更改为2行。按钮高度将相应修改(更改为20)。
我希望它尽可能简单。
有什么实际的解决办法吗?

4dc9hkyq

4dc9hkyq1#

最直接的解决方案是子类化WrapPanel并覆盖ArrangeOverride。您可以添加一个依赖项属性来跟踪面板是否溢出到第二行,然后可以将其用作触发器的源,并在排列过程中设置该属性。

EnhancedWrapPanel

public class EnhancedWrapPanel : WrapPanel
{
    public static readonly DependencyProperty HasOverflowProperty = DependencyProperty.Register(
        "HasOverflow",
        typeof(bool),
        typeof(EnhancedWrapPanel),
        new PropertyMetadata((bool)false));
    public bool HasOverflow
    {
        get
        {
            return (bool)GetValue(HasOverflowProperty);
        }
        set
        {
            SetValue(HasOverflowProperty, value);
        }
    }

    // Unfortunately the bulk of this is just copied from
    // the original WPF source since there's no way to 
    // inject into the arrange pass in a more subtle way.
    // For implementation of other referenced internal/private
    // structs and methods see
    // https://github.com/dotnet/wpf/blob/0162cb1b509bafe8716ebff344d1170f663fdfee/src/Microsoft.DotNet.Wpf/src/PresentationFramework/System/Windows/Controls/WrapPanel.cs#L6
    protected override Size ArrangeOverride(Size finalSize)
    {
        int firstInLine = 0;
        double itemWidth = ItemWidth;
        double itemHeight = ItemHeight;
        double accumulatedV = 0;
        double itemU = (Orientation == Orientation.Horizontal ? itemWidth : itemHeight);
        UVSize curLineSize = new UVSize(Orientation);
        UVSize uvFinalSize = new UVSize(Orientation, finalSize.Width, finalSize.Height);
        bool itemWidthSet = !double.IsNaN(itemWidth);
        bool itemHeightSet = !double.IsNaN(itemHeight);
        bool useItemU = (Orientation == Orientation.Horizontal ? itemWidthSet : itemHeightSet);

        UIElementCollection children = InternalChildren;
        bool hasOverflow = false;

        for (int i = 0, count = children.Count; i < count; i++)
        {
            UIElement child = children[i] as UIElement;
            if (child == null) continue;

            UVSize sz = new UVSize(
                Orientation,
                (itemWidthSet ? itemWidth : child.DesiredSize.Width),
                (itemHeightSet ? itemHeight : child.DesiredSize.Height));

            if (curLineSize.U + sz.U > uvFinalSize.U) //need to switch to another line
            {
                arrangeLine(accumulatedV, curLineSize.V, firstInLine, i, useItemU, itemU);

                accumulatedV += curLineSize.V;
                curLineSize = sz;

                if (sz.U > uvFinalSize.U) //the element is wider then the constraint - give it a separate line                    
                {
                    //switch to next line which only contain one element
                    arrangeLine(accumulatedV, sz.V, i, ++i, useItemU, itemU);

                    accumulatedV += sz.V;
                    curLineSize = new UVSize(Orientation);
                }
                firstInLine = i;

                /** New **/
                hasOverflow = true;  
            }
            else //continue to accumulate a line
            {
                curLineSize.U += sz.U;
                curLineSize.V = Math.Max(sz.V, curLineSize.V);
            }
        }

        //arrange the last line, if any
        if (firstInLine < children.Count)
        {
            arrangeLine(accumulatedV, curLineSize.V, firstInLine, children.Count, useItemU, itemU);
        }

        /** New **/
        if (hasOverflow != this.HasOverflow)
            this.SetCurrentValue(HasOverflowProperty, hasOverflow);

        return finalSize;
    }        
}

XAML示例

<local:EnhancedWrapPanel>
    <local:EnhancedWrapPanel.Style>
        <Style TargetType="local:EnhancedWrapPanel">
            <Style.Triggers>
                <Trigger Property="HasOverflow"
                         Value="true">
                    <Setter Property="ItemHeight"
                            Value="20" />
                </Trigger>
                <Trigger Property="HasOverflow" Value="False">
                    <Setter Property="ItemHeight"
                            Value="40" />
                </Trigger>
            </Style.Triggers>
        </Style>
    </local:EnhancedWrapPanel.Style>            
    <Button Width="200" />
    <Button Width="200" />
    <Button Width="200" />
    <Button Width="200" />
    <Button Width="200" />
    <Button Width="200" />
</local:EnhancedWrapPanel>

其他注意事项要警惕一种潜在的情况,即对行计数更改的响应本身可能导致行计数更改,从而陷入无限循环。只要你只改变子对象的高度而不是宽度,你就没问题,但是如果你曾经在不同的场景中应用过这个,你可能需要在设置HasOverflow之前检查一下,以确保排列过程本身不是由HasOverflow的更改触发的。

还要感谢Denis Schaf在另一个问题中提出了XAML部分的答案,这个问题也已经结束。

相关问题