WPF - Comment puis-je centrer tous les articles dans un WrapPanel?

j'utilise un WrapPanelItemsPanel d'un ItemsControl. En ce moment, les articles dans le bandeau de contrôle comme ceci:

|1234567  |
|890      |

je tiens à envelopper comme ceci:

| 1234567 |
|   890   |

conceptuellement, le processus de mise en page devrait aligner chaque ligne d'items de telle sorte qu'elle soit centrée dans le WrapPanel's limites.

quelqu'un Peut m'expliquer comment cela est possible s'il vous plaît?

26
demandé sur Drew Noakes 2009-04-30 17:01:49

6 réponses

Le construit en WrapPanel ne vous permettra pas d'aligner son contenu lui-même. Voici une technique qui vous permet de définir HorizontalContentAlignment:

using System;
using System.Windows;
using System.Windows.Controls;

public class AlignableWrapPanel : Panel
{
    public HorizontalAlignment HorizontalContentAlignment
    {
        get { return (HorizontalAlignment)GetValue(HorizontalContentAlignmentProperty); }
        set { SetValue(HorizontalContentAlignmentProperty, value); }
    }

    public static readonly DependencyProperty HorizontalContentAlignmentProperty =
        DependencyProperty.Register("HorizontalContentAlignment", typeof(HorizontalAlignment), typeof(AlignableWrapPanel), new FrameworkPropertyMetadata(HorizontalAlignment.Left, FrameworkPropertyMetadataOptions.AffectsArrange));

    protected override Size MeasureOverride(Size constraint)
    {
        Size curLineSize = new Size();
        Size panelSize = new Size();

        UIElementCollection children = base.InternalChildren;

        for (int i = 0; i < children.Count; i++)
        {
            UIElement child = children[i] as UIElement;

            // Flow passes its own constraint to children
            child.Measure(constraint);
            Size sz = child.DesiredSize;

            if (curLineSize.Width + sz.Width > constraint.Width) //need to switch to another line
            {
                panelSize.Width = Math.Max(curLineSize.Width, panelSize.Width);
                panelSize.Height += curLineSize.Height;
                curLineSize = sz;

                if (sz.Width > constraint.Width) // if the element is wider then the constraint - give it a separate line                    
                {
                    panelSize.Width = Math.Max(sz.Width, panelSize.Width);
                    panelSize.Height += sz.Height;
                    curLineSize = new Size();
                }
            }
            else //continue to accumulate a line
            {
                curLineSize.Width += sz.Width;
                curLineSize.Height = Math.Max(sz.Height, curLineSize.Height);
            }
        }

        // the last line size, if any need to be added
        panelSize.Width = Math.Max(curLineSize.Width, panelSize.Width);
        panelSize.Height += curLineSize.Height;

        return panelSize;
    }

    protected override Size ArrangeOverride(Size arrangeBounds)
    {
        int firstInLine = 0;
        Size curLineSize = new Size();
        double accumulatedHeight = 0;
        UIElementCollection children = this.InternalChildren;

        for (int i = 0; i < children.Count; i++)
        {
            Size sz = children[i].DesiredSize;

            if (curLineSize.Width + sz.Width > arrangeBounds.Width) //need to switch to another line
            {
                ArrangeLine(accumulatedHeight, curLineSize, arrangeBounds.Width, firstInLine, i);

                accumulatedHeight += curLineSize.Height;
                curLineSize = sz;

                if (sz.Width > arrangeBounds.Width) //the element is wider then the constraint - give it a separate line                    
                {
                    ArrangeLine(accumulatedHeight, sz, arrangeBounds.Width, i, ++i);
                    accumulatedHeight += sz.Height;
                    curLineSize = new Size();
                }
                firstInLine = i;
            }
            else //continue to accumulate a line
            {
                curLineSize.Width += sz.Width;
                curLineSize.Height = Math.Max(sz.Height, curLineSize.Height);
            }
        }

        if (firstInLine < children.Count)
            ArrangeLine(accumulatedHeight, curLineSize, arrangeBounds.Width, firstInLine, children.Count);

        return arrangeBounds;
    }

    private void ArrangeLine(double y, Size lineSize, double boundsWidth, int start, int end)
    {
        double x = 0;
        if (this.HorizontalContentAlignment == HorizontalAlignment.Center)
        {
            x = (boundsWidth - lineSize.Width) / 2;
        }
        else if (this.HorizontalContentAlignment == HorizontalAlignment.Right)
        {
            x = (boundsWidth - lineSize.Width);
        }

        UIElementCollection children = InternalChildren;
        for (int i = start; i < end; i++)
        {
            UIElement child = children[i];
            child.Arrange(new Rect(x, y, child.DesiredSize.Width, lineSize.Height));
            x += child.DesiredSize.Width;
        }
    }
}
61
répondu DTig 2015-07-10 12:17:31

malheureusement, le WrapPanel de stock ne fera pas ce que vous demandez, parce que son intention est de remplir tout l'espace disponible horizontalement (ou verticalement) avant l'emballage. Vous devrez probablement concevoir votre propre WrapPanel pour gérer cette affaire. Vous pourriez commencer par cet exemple montre comment créer votre propre WrapPanel.

8
répondu user7116 2009-04-30 13:31:05

la réponse simple est que vous ne pouvez pas centrer aligner le contenu D'un WrapPanel. Vous pouvez centrer aligner le panneau lui-même, mais la dernière ligne sera encore alignée à l'intérieur du panneau.

Altérer suggestions:

utilisez la grille avec les lignes et les colonnes. Si vous n'ajoutez pas des articles à la collection de façon dynamique, cela pourrait bien fonctionner.

créez votre propre version du WrapPanel qui fonctionne comme vous le souhaitez. ce document MSDN décrit comment fonctionnent les panneaux et inclut une section sur la création de panneaux personnalisés. Il a également un lien vers un exemple de panneau personnalisé.

4
répondu Josh G 2009-04-30 13:25:12

Version avec alignement vertical (pour panneaux verticaux):

public class AlignableWrapPanel : Panel {
    public AlignableWrapPanel() {
        _orientation = Orientation.Horizontal;
    }

    private static bool IsWidthHeightValid(object value) {
        var v = (double)value;
        return (double.IsNaN(v)) || (v >= 0.0d && !double.IsPositiveInfinity(v));
    }

    public static readonly DependencyProperty ItemWidthProperty = DependencyProperty.Register("ItemWidth", typeof(double),
            typeof(AlignableWrapPanel), new FrameworkPropertyMetadata(double.NaN, FrameworkPropertyMetadataOptions.AffectsMeasure),
            IsWidthHeightValid);

    [TypeConverter(typeof(LengthConverter))]
    public double ItemWidth {
        get { return (double)GetValue(ItemWidthProperty); }
        set { SetValue(ItemWidthProperty, value); }
    }

    public static readonly DependencyProperty ItemHeightProperty = DependencyProperty.Register("ItemHeight", typeof(double),
            typeof(AlignableWrapPanel), new FrameworkPropertyMetadata(double.NaN, FrameworkPropertyMetadataOptions.AffectsMeasure),
            IsWidthHeightValid);

    [TypeConverter(typeof(LengthConverter))]
    public double ItemHeight {
        get { return (double)GetValue(ItemHeightProperty); }
        set { SetValue(ItemHeightProperty, value); }
    }

    public static readonly DependencyProperty OrientationProperty = StackPanel.OrientationProperty.AddOwner(typeof(AlignableWrapPanel),
            new FrameworkPropertyMetadata(Orientation.Horizontal, FrameworkPropertyMetadataOptions.AffectsMeasure, OnOrientationChanged));

    public Orientation Orientation {
        get { return _orientation; }
        set { SetValue(OrientationProperty, value); }
    }

    private static void OnOrientationChanged(DependencyObject d, DependencyPropertyChangedEventArgs e) {
        var p = (AlignableWrapPanel)d;
        p._orientation = (Orientation)e.NewValue;
    }

    private Orientation _orientation;

    private struct UvSize {
        internal UvSize(Orientation orientation, double width, double height) {
            U = V = 0d;
            _orientation = orientation;
            Width = width;
            Height = height;
        }

        internal UvSize(Orientation orientation) {
            U = V = 0d;
            _orientation = orientation;
        }

        internal double U;
        internal double V;
        private readonly Orientation _orientation;

        internal double Width {
            get { return (_orientation == Orientation.Horizontal ? U : V); }
            private set { if (_orientation == Orientation.Horizontal) U = value; else V = value; }
        }

        internal double Height {
            get { return (_orientation == Orientation.Horizontal ? V : U); }
            private set { if (_orientation == Orientation.Horizontal) V = value; else U = value; }
        }
    }

    protected override Size MeasureOverride(Size constraint) {
        var curLineSize = new UvSize(Orientation);
        var panelSize = new UvSize(Orientation);
        var uvConstraint = new UvSize(Orientation, constraint.Width, constraint.Height);
        var itemWidth = ItemWidth;
        var itemHeight = ItemHeight;
        var itemWidthSet = !double.IsNaN(itemWidth);
        var itemHeightSet = !double.IsNaN(itemHeight);

        var childConstraint = new Size(
                (itemWidthSet ? itemWidth : constraint.Width),
                (itemHeightSet ? itemHeight : constraint.Height));

        var children = InternalChildren;

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

            //Flow passes its own constrint to children 
            child.Measure(childConstraint);

            //this is the size of the child in UV space 
            var sz = new UvSize(
                    Orientation,
                    (itemWidthSet ? itemWidth : child.DesiredSize.Width),
                    (itemHeightSet ? itemHeight : child.DesiredSize.Height));

            if (curLineSize.U + sz.U > uvConstraint.U) {
                //need to switch to another line 
                panelSize.U = Math.Max(curLineSize.U, panelSize.U);
                panelSize.V += curLineSize.V;
                curLineSize = sz;

                if (!(sz.U > uvConstraint.U)) continue;
                //the element is wider then the constrint - give it a separate line
                panelSize.U = Math.Max(sz.U, panelSize.U);
                panelSize.V += sz.V;
                curLineSize = new UvSize(Orientation);
            } else {
                //continue to accumulate a line
                curLineSize.U += sz.U;
                curLineSize.V = Math.Max(sz.V, curLineSize.V);
            }
        }

        //the last line size, if any should be added 
        panelSize.U = Math.Max(curLineSize.U, panelSize.U);
        panelSize.V += curLineSize.V;

        //go from UV space to W/H space
        return new Size(panelSize.Width, panelSize.Height);
    }

    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 UvSize(Orientation);
        var uvFinalSize = new UvSize(Orientation, finalSize.Width, finalSize.Height);
        var itemWidthSet = !double.IsNaN(itemWidth);
        var itemHeightSet = !double.IsNaN(itemHeight);
        var useItemU = (Orientation == Orientation.Horizontal ? itemWidthSet : itemHeightSet);

        var children = InternalChildren;

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

            var 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(finalSize, accumulatedV, curLineSize, 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(finalSize, accumulatedV, sz, i, ++i, useItemU, itemU);

                    accumulatedV += sz.V;
                    curLineSize = new UvSize(Orientation);
                }

                firstInLine = i;
            } 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(finalSize, accumulatedV, curLineSize, firstInLine, children.Count, useItemU, itemU);
        }

        return finalSize;
    }

    private void ArrangeLine(Size finalSize, double v, UvSize line, int start, int end, bool useItemU, double itemU) {
        double u;
        var isHorizontal = Orientation == Orientation.Horizontal;

        if (_orientation == Orientation.Vertical) {
            switch (VerticalContentAlignment) {
                case VerticalAlignment.Center:
                    u = (finalSize.Height - line.U) / 2;
                    break;
                case VerticalAlignment.Bottom:
                    u = finalSize.Height - line.U;
                    break;
                default:
                    u = 0;
                    break;
            }
        } else {
            switch (HorizontalContentAlignment) {
                case HorizontalAlignment.Center:
                    u = (finalSize.Width - line.U) / 2;
                    break;
                case HorizontalAlignment.Right:
                    u = finalSize.Width - line.U;
                    break;
                default:
                    u = 0;
                    break;
            }
        }

        var children = InternalChildren;
        for (var i = start; i < end; i++) {
            var child = children[i];
            if (child == null) continue;
            var childSize = new UvSize(Orientation, child.DesiredSize.Width, child.DesiredSize.Height);
            var layoutSlotU = (useItemU ? itemU : childSize.U);
            child.Arrange(new Rect(
                    isHorizontal ? u : v,
                    isHorizontal ? v : u,
                    isHorizontal ? layoutSlotU : line.V,
                    isHorizontal ? line.V : layoutSlotU));
            u += layoutSlotU;
        }
    }

    public static readonly DependencyProperty HorizontalContentAlignmentProperty = DependencyProperty.Register(nameof(HorizontalContentAlignment), typeof(HorizontalAlignment),
            typeof(AlignableWrapPanel), new FrameworkPropertyMetadata(HorizontalAlignment.Left, FrameworkPropertyMetadataOptions.AffectsArrange));

    public HorizontalAlignment HorizontalContentAlignment {
        get { return (HorizontalAlignment)GetValue(HorizontalContentAlignmentProperty); }
        set { SetValue(HorizontalContentAlignmentProperty, value); }
    }

    public static readonly DependencyProperty VerticalContentAlignmentProperty = DependencyProperty.Register(nameof(VerticalContentAlignment), typeof(VerticalAlignment),
            typeof(AlignableWrapPanel), new FrameworkPropertyMetadata(VerticalAlignment.Top, FrameworkPropertyMetadataOptions.AffectsArrange));

    public VerticalAlignment VerticalContentAlignment {
        get { return (VerticalAlignment)GetValue(VerticalContentAlignmentProperty); }
        set { SetValue(VerticalContentAlignmentProperty, value); }
    }
}
4
répondu Surfin Bird 2016-03-10 11:47:56

remerciements spéciaux à @DTig,

using System.Windows.Controls;
using System.Windows;
using Telerik.Windows;
using System;
using System.Linq;

public class AlignableWrapPanel : Panel
{
    public HorizontalAlignment HorizontalContentAlignment
    {
        get { return (HorizontalAlignment)GetValue(HorizontalContentAlignmentProperty); }
        set { SetValue(HorizontalContentAlignmentProperty, value); }
    }

    public static readonly DependencyProperty HorizontalContentAlignmentProperty =
        DependencyProperty.Register("HorizontalContentAlignment", typeof(HorizontalAlignment), typeof(AlignableWrapPanel), new FrameworkPropertyMetadata(HorizontalAlignment.Left, FrameworkPropertyMetadataOptions.AffectsArrange));

    protected override Size MeasureOverride(Size constraint)
    {
        Size curLineSize = new Size();
        Size panelSize = new Size();

        UIElementCollection children = base.Children;

        for (int i = 0; i < children.Count; i++)
        {
            UIElement child = children[i] as UIElement;

            // Flow passes its own constraint to children
            child.Measure(constraint);
            Size sz = child.DesiredSize;

            if (curLineSize.Width + sz.Width > constraint.Width) //need to switch to another line
            {
                panelSize.Width = Math.Max(curLineSize.Width, panelSize.Width);
                panelSize.Height += curLineSize.Height;
                curLineSize = sz;

                if (sz.Width > constraint.Width) // if the element is wider then the constraint - give it a separate line                    
                {
                    panelSize.Width = Math.Max(sz.Width, panelSize.Width);
                    panelSize.Height += sz.Height;
                    curLineSize = new Size();
                }
            }
            else //continue to accumulate a line
            {
                curLineSize.Width += sz.Width;
                curLineSize.Height = Math.Max(sz.Height, curLineSize.Height);
            }
        }

        // the last line size, if any need to be added
        panelSize.Width = Math.Max(curLineSize.Width, panelSize.Width);
        panelSize.Height += curLineSize.Height;

        return panelSize;
    }

    protected override Size ArrangeOverride(Size arrangeBounds)
    {
        int firstInLine = 0;
        Size curLineSize = new Size();
        double accumulatedHeight = 0;
        UIElementCollection children = this.Children;

        for (int i = 0; i < children.Count; i++)
        {
            Size sz = children[i].DesiredSize;

            if (curLineSize.Width + sz.Width > arrangeBounds.Width) //need to switch to another line
            {
                ArrangeLine(accumulatedHeight, curLineSize, arrangeBounds.Width, firstInLine, i);

                accumulatedHeight += curLineSize.Height;
                curLineSize = sz;

                if (sz.Width > arrangeBounds.Width) //the element is wider then the constraint - give it a separate line                    
                {
                    ArrangeLine(accumulatedHeight, sz, arrangeBounds.Width, i, ++i);
                    accumulatedHeight += sz.Height;
                    curLineSize = new Size();
                }
                firstInLine = i;
            }
            else //continue to accumulate a line
            {
                curLineSize.Width += sz.Width;
                curLineSize.Height = Math.Max(sz.Height, curLineSize.Height);
            }
        }

        if (firstInLine < children.Count)
            ArrangeLine(accumulatedHeight, curLineSize, arrangeBounds.Width, firstInLine, children.Count);

        return arrangeBounds;
    }

    private void ArrangeLine(double y, Size lineSize, double boundsWidth, int start, int end)
    {
        double x = 0;
        if (this.HorizontalContentAlignment == HorizontalAlignment.Center)
        {
            x = (boundsWidth - lineSize.Width) / 2;
        }
        else if (this.HorizontalContentAlignment == HorizontalAlignment.Right)
        {
            x = (boundsWidth - lineSize.Width);
        }

        UIElementCollection children = Children;
        for (int i = start; i < end; i++)
        {
            UIElement child = children[i];
            var rect = new System.Windows.Rect(x, y, child.DesiredSize.Width, lineSize.Height);
            child.Arrange(rect);
            x += child.DesiredSize.Width;
        }
    }
}
3
répondu Davut Gürbüz 2013-04-03 13:09:34

j'ai besoin d'un WrapPanel que l'on peut étirer son contenu, en se basant sur https://stackoverflow.com/a/7747002/121122 et certains tripoter, je suis venu avec ceci:

public class AlignableWrapPanel : Panel
{
    public HorizontalAlignment HorizontalContentAlignment
    {
        get => (HorizontalAlignment)GetValue(HorizontalContentAlignmentProperty);
        set => SetValue(HorizontalContentAlignmentProperty, value);
    }

    public static readonly DependencyProperty HorizontalContentAlignmentProperty = DependencyProperty.Register(
        nameof(HorizontalContentAlignment),
        typeof(HorizontalAlignment),
        typeof(AlignableWrapPanel),
        new FrameworkPropertyMetadata(HorizontalAlignment.Left, FrameworkPropertyMetadataOptions.AffectsArrange));

    protected override Size MeasureOverride(Size constraint)
    {
        var curLineSize = new Size();
        var panelSize = new Size();

        var children = InternalChildren;

        for (var i = 0; i < children.Count; i++)
        {
            var child = children[i];

            // flow passes its own constraint to children
            child.Measure(constraint);
            var sz = child.DesiredSize;

            if (curLineSize.Width + sz.Width > constraint.Width) // need to switch to another line
            {
                panelSize.Width = Math.Max(curLineSize.Width, panelSize.Width);
                panelSize.Height += curLineSize.Height;
                curLineSize = sz;

                if (sz.Width > constraint.Width) // if the element is wider then the constraint - give it a separate line                    
                {
                    panelSize.Width = Math.Max(sz.Width, panelSize.Width);
                    panelSize.Height += sz.Height;
                    curLineSize = new Size();
                }
            }
            else // continue to add to the line
            {
                curLineSize.Width += sz.Width;
                curLineSize.Height = Math.Max(sz.Height, curLineSize.Height);
            }
        }

        // the last line size, if any need to be added
        panelSize.Width = Math.Max(curLineSize.Width, panelSize.Width);
        panelSize.Height += curLineSize.Height;

        return panelSize;
    }

    protected override Size ArrangeOverride(Size arrangeBounds)
    {
        var firstInLine = 0;
        var curLineSize = new Size();
        var accumulatedHeight = 0D;
        var children = InternalChildren;

        for (var i = 0; i < children.Count; i++)
        {
            var desiredSize = children[i].DesiredSize;

            if (curLineSize.Width + desiredSize.Width > arrangeBounds.Width) // need to switch to another line
            {
                ArrangeLine(accumulatedHeight, curLineSize, arrangeBounds.Width, firstInLine, i);

                accumulatedHeight += curLineSize.Height;
                curLineSize = desiredSize;

                if(
                    desiredSize.Width > arrangeBounds.Width || 
                    children[i] is FrameworkElement element && element.HorizontalAlignment == HorizontalAlignment.Stretch)
                {
                    // the element is wider then the constraint or it stretches - give it a separate line
                    ArrangeLine(accumulatedHeight, desiredSize, arrangeBounds.Width, i, ++i);
                    accumulatedHeight += desiredSize.Height;
                    curLineSize = new Size();
                }
                firstInLine = i;
            }
            else // continue to add to the line
            {
                curLineSize.Width += desiredSize.Width;
                curLineSize.Height = Math.Max(desiredSize.Height, curLineSize.Height);
            }
        }

        if (firstInLine < children.Count)
        {
            ArrangeLine(accumulatedHeight, curLineSize, arrangeBounds.Width, firstInLine, children.Count);
        }

        return arrangeBounds;
    }

    private void ArrangeLine(double y, Size lineSize, double boundsWidth, int start, int end)
    {
        var children = InternalChildren;

        var x = 0D;
        if (HorizontalContentAlignment == HorizontalAlignment.Center)
        {
            x = (boundsWidth - lineSize.Width) / 2;
        }
        else if (HorizontalContentAlignment == HorizontalAlignment.Right)
        {
            x = boundsWidth - lineSize.Width;
        }

        var stretchChildren = new List<UIElement>();
        for (var i = start; i < end; i++)
        {
            var child = children[i];
            if (child is FrameworkElement element && element.HorizontalAlignment == HorizontalAlignment.Stretch)
            {
                stretchChildren.Add(child);
            }
        }

        var spaceAvailableForStretchChildren = boundsWidth - lineSize.Width;
        var spaceAvailablePerStretchChild = 0D;
        if (stretchChildren.Any())
        {
            x = 0; // all available space will be filled so start at 0
            spaceAvailablePerStretchChild = spaceAvailableForStretchChildren / stretchChildren.Count;
            spaceAvailablePerStretchChild = spaceAvailablePerStretchChild >= 0 ? spaceAvailablePerStretchChild : 0;
        }

        for (var i = start; i < end; i++)
        {
            var child = children[i];
            double itemWidth;
            if(stretchChildren.Contains(child))
            {
                itemWidth = child.DesiredSize.Width + spaceAvailablePerStretchChild;
            }
            else
            {
                itemWidth = child.DesiredSize.Width;
            }

            child.Arrange(new Rect(x, y, itemWidth, lineSize.Height));
            x += itemWidth;
        }
    }
}
1
répondu eriksmith200 2018-02-21 20:42:36