Как заставить WPF обернуть дочерние элементы в растяжку? - PullRequest
14 голосов
/ 04 ноября 2011

Я хотел бы создать ItemsControl, где дочерние элементы размещаются как WrapPanel, но дочерние элементы должны занимать как можно больше места. Таким образом, когда размер окна становится больше или меньше, дочерние элементы должны растягиваться в соответствии с определенным соотношением ширины и высоты. Когда дочерние элементы добавляются или удаляются из ItemsControl ItemsSource, WrapPanel должен соответствующим образом размещать разрывы строк среди элементов, чтобы сохранить соотношение ширины и высоты дочернего элемента.
Ниже то, что я имею до сих пор. Возможно ли это сделать в Xaml? или я должен создать собственный элемент управления для этого? Заранее спасибо!

<Window>
<Grid>
    <ItemsControl ItemsSource="{Binding DataCollection}">
       <ItemsControl.ItemsPanel>
          <ItemsPanelTemplate>
             <WrapPanel Orientation="Vertical"/>
          </ItemsPanelTemplate>
       </ItemsControl.ItemsPanel>
       <ItemsControl.ItemTemplate>
          <DataTemplate>
             <Border BorderBrush="Black" BorderThickness="1" HorizontalAlignment="Stretch">
                <StackPanel Orientation="Horizontal" >
                   <TextBlock TextWrapping="Wrap" Text="{Binding Name}" />
                   <TextBlock TextWrapping="Wrap" Text="{Binding Value}"/>
                   <TextBlock TextWrapping="Wrap" Text="{Binding Time,  StringFormat='hh:mm:ss' }"/>
                </StackPanel>
             </Border>          
         </DataTemplate>
       </ItemsControl.ItemTemplate>
    </ItemsControl>
</Grid>
</Window>

Ответы [ 4 ]

14 голосов
/ 16 ноября 2011

Используйте UniformGrid вместо WrapPanel. Просто установите желаемое количество столбцов с помощью свойства Columns, и оно должно дать желаемый результат.

5 голосов
/ 08 января 2017

В качестве другого решения (иногда ширина может быть разной, поэтому UniformGrid не сработает), здесь модифицирован WrapPanel, который растягивает элементы в соответствии с линиями (при сохранении пропорций):

public class StretchyWrapPanel : Panel {
    public static readonly DependencyProperty ItemWidthProperty = DependencyProperty.Register(nameof(ItemWidth),
            typeof(double), typeof(StretchyWrapPanel), new FrameworkPropertyMetadata(double.NaN, FrameworkPropertyMetadataOptions.AffectsMeasure));

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

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

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

    public static readonly DependencyProperty OrientationProperty = StackPanel.OrientationProperty.AddOwner(typeof(StretchyWrapPanel),
            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) {
        ((StretchyWrapPanel)d)._orientation = (Orientation)e.NewValue;
    }

    private Orientation _orientation = Orientation.Horizontal;

    public static readonly DependencyProperty StretchProportionallyProperty = DependencyProperty.Register(nameof(StretchProportionally), typeof(bool),
            typeof(StretchyWrapPanel), new PropertyMetadata(true, OnStretchProportionallyChanged));

    public bool StretchProportionally {
        get { return _stretchProportionally; }
        set { SetValue(StretchProportionallyProperty, value); }
    }

    private static void OnStretchProportionallyChanged(DependencyObject o, DependencyPropertyChangedEventArgs e) {
        ((StretchyWrapPanel)o)._stretchProportionally = (bool)e.NewValue;
    }

    private bool _stretchProportionally = true;

    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; }
            set {
                if (_orientation == Orientation.Horizontal) {
                    U = value;
                } else {
                    V = value;
                }
            }
        }

        internal double Height {
            get { return _orientation == Orientation.Horizontal ? V : U; }
            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) {
                    // 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
                if (!useItemU && StretchProportionally) {
                    ArrangeLineProportionally(accumulatedV, curLineSize.V, firstInLine, i, uvFinalSize.Width);
                } else {
                    ArrangeLine(accumulatedV, curLineSize.V, firstInLine, i, true, useItemU ? itemU : uvFinalSize.Width / Math.Max(1, i - firstInLine - 1));
                }

                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
                    if (!useItemU && StretchProportionally) {
                        ArrangeLineProportionally(accumulatedV, sz.V, i, ++i, uvFinalSize.Width);
                    } else {
                        ArrangeLine(accumulatedV, sz.V, i, ++i, true, useItemU ? itemU : uvFinalSize.Width);
                    }

                    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) {
            if (!useItemU && StretchProportionally) {
                ArrangeLineProportionally(accumulatedV, curLineSize.V, firstInLine, children.Count, uvFinalSize.Width);
            } else {
                ArrangeLine(accumulatedV, curLineSize.V, firstInLine, children.Count, true,
                        useItemU ? itemU : uvFinalSize.Width / Math.Max(1, children.Count - firstInLine - 1));
            }
        }

        return finalSize;
    }

    private void ArrangeLineProportionally(double v, double lineV, int start, int end, double limitU) {
        var u = 0d;
        var horizontal = Orientation == Orientation.Horizontal;
        var children = InternalChildren;

        var total = 0d;
        for (var i = start; i < end; i++) {
            total += horizontal ? children[i].DesiredSize.Width : children[i].DesiredSize.Height;
        }

        var uMultipler = limitU / total;
        for (var i = start; i < end; i++) {
            var child = children[i];
            if (child != null) {
                var childSize = new UVSize(Orientation, child.DesiredSize.Width, child.DesiredSize.Height);
                var layoutSlotU = childSize.U * uMultipler;
                child.Arrange(new Rect(horizontal ? u : v, horizontal ? v : u,
                        horizontal ? layoutSlotU : lineV, horizontal ? lineV : layoutSlotU));
                u += layoutSlotU;
            }
        }
    }

    private void ArrangeLine(double v, double lineV, int start, int end, bool useItemU, double itemU) {
        var u = 0d;
        var horizontal = Orientation == Orientation.Horizontal;
        var children = InternalChildren;
        for (var i = start; i < end; i++) {
            var child = children[i];
            if (child != null) {
                var childSize = new UVSize(Orientation, child.DesiredSize.Width, child.DesiredSize.Height);
                var layoutSlotU = useItemU ? itemU : childSize.U;
                child.Arrange(new Rect(horizontal ? u : v, horizontal ? v : u,
                        horizontal ? layoutSlotU : lineV, horizontal ? lineV : layoutSlotU));
                u += layoutSlotU;
            }
        }
    }
}

В действии:

List of stretched tags

0 голосов
/ 18 января 2019

Для достижения желаемого поведения вы можете попытаться использовать Width или MaxWidth свойства WrapPanel, например, таким образом

        <ListView
            Name="RootControl"
            ItemsSource="{Binding [icons]}"
            ItemTemplate="{StaticResource IconTemplate}">
            <ListView.ItemsPanel>
                <ItemsPanelTemplate>
                    <WrapPanel
                        Width="{Binding ElementName=RootControl, Path=ActualWidth}"/>
                </ItemsPanelTemplate>
            </ListView.ItemsPanel>
        </ListView>

Это работает enter image description here

0 голосов
/ 04 ноября 2011

Вы пробовали свойство HorizontalContentAlignment = "Stretch" в элементе управления.

...