Создание обратимой StackPanel в WPF - PullRequest
9 голосов
/ 07 августа 2010

Я хотел бы создать пользовательское StackPanel со свойством ReverseOrder, для которого я могу декларативно установить значение true, чтобы элементы в StackPanel отображались в порядке, обратном нормали (например, снизу вверх или справа налево ). Он должен быть обратимым на лету.

Я думаю о выводе нового класса из StackPanel, но мне нужно знать, какие методы переопределить.

Окончательное решение:

protected override System.Windows.Size ArrangeOverride( System.Windows.Size arrangeSize ) {
    double x = 0;
    double y = 0;

    IEnumerable<UIElement> children = ReverseOrder ? InternalChildren.Cast<UIElement>().Reverse<UIElement>() : InternalChildren.Cast<UIElement>();
    foreach ( UIElement child in children ) {
        var size = child.DesiredSize;
        child.Arrange( new Rect( new Point( x, y ), size ) );

        if ( Orientation == Orientation.Horizontal )
            x += size.Width;
        else
            y += size.Height;
    }

    if ( Orientation == Orientation.Horizontal )
        return new Size( x, arrangeSize.Height );
    else
        return new Size( arrangeSize.Width, y );
}

Также определите и зарегистрируйте ReverseOrder и позвоните UpdateLayout, если он изменится.

Ответы [ 3 ]

12 голосов
/ 07 августа 2010

Вы можете переопределить FrameworkElement.ArrangeOverride и вызвать все дочерние элементы. Расположите в обратном порядке, чем обычно, при необходимости.

http://msdn.microsoft.com/en-us/library/system.windows.uielement.arrange.aspx

Примерно так (не проверено):

    double x = 0;
    double y = 0;

    var children = ReverseOrder ? InternalChildren.Reverse() : InternalChildren;
    foreach (UIElement child in children)
    {
        var size = child.DesiredSize;
        child.Arrange(new Rect(new Point(x, y), size));

        if (Orientation == Horizontal)
            x += size.Width;
        else
            y += size.Height;
    }

Убедитесь, что вы вызываете UpdateLayout после изменения свойства ReverseOrder.

3 голосов
/ 07 августа 2010

MeasureOverride и ArrangeOverride

На самом деле, ваша логика измерения может быть такой же, как StackPanel, поэтому вы можете просто переопределить ArrangeOverride.Имейте в виду, что в StackPanel есть некоторая логика для прокрутки, которую вам, возможно, придется дублировать, если вы напишите ее самостоятельно.Вы можете наследовать напрямую от Panel и не пытаться поддерживать прокрутку.

См. Элементы пользовательской панели на странице MSDN на панелях или различные записи в блоге по написанию пользовательских панелей, такие как Учебное пособие по WPF - Создание пользовательского элемента управления панели или Создание пользовательских панелей в WPF .

0 голосов
/ 30 декабря 2017

Вот еще один вариант. Он основан на StackPanel эталонном источнике и способен также обрабатывать выравнивания предметов.

public class ReversibleStackPanel : StackPanel
{
    public bool ReverseOrder
    {
        get => (bool)GetValue(ReverseOrderProperty);
        set => SetValue(ReverseOrderProperty, value);
    }

    public static readonly DependencyProperty ReverseOrderProperty =
        DependencyProperty.Register(nameof(ReverseOrder), typeof(bool), typeof(ReversibleStackPanel), new PropertyMetadata(false));


    protected override Size ArrangeOverride(Size arrangeSize)
    {
        bool fHorizontal = (Orientation == Orientation.Horizontal);
        var  rcChild = new Rect(arrangeSize);
        double previousChildSize = 0.0;

        var children = ReverseOrder ? InternalChildren.Cast<UIElement>().Reverse() : InternalChildren.Cast<UIElement>();
        foreach (UIElement child in children)
        {
            if (child == null)
                continue;

            if (fHorizontal)
            {
                rcChild.X += previousChildSize;
                previousChildSize = child.DesiredSize.Width;
                rcChild.Width = previousChildSize;
                rcChild.Height = Math.Max(arrangeSize.Height, child.DesiredSize.Height);
            }
            else
            {
                rcChild.Y += previousChildSize;
                previousChildSize = child.DesiredSize.Height;
                rcChild.Height = previousChildSize;
                rcChild.Width = Math.Max(arrangeSize.Width, child.DesiredSize.Width);
            }

            child.Arrange(rcChild);
        }

        return arrangeSize;
    }
}
...