Raj123
Raj123

Reputation: 981

Items not resizing to fill available space

I am working on a WPF application where I have a ListBox.

    <ListBox Grid.Column="1" ItemsSource="{Binding MyUserControls}" ScrollViewer.HorizontalScrollBarVisibility="Disabled"
             HorizontalContentAlignment="Stretch" VerticalContentAlignment="Stretch">
        <ListBox.ItemsPanel>
            <ItemsPanelTemplate>
                <WrapPanel IsItemsHost="True" Orientation="Horizontal"/>
            </ItemsPanelTemplate>
        </ListBox.ItemsPanel>
    </ListBox>

When user clicks a button I am adding a UserControl instance to MyUserControls(ObservableCollection). The Listbox is in a Grid in my Main Window. What I need is when more and more user controls are added they should resize dynamically upto a min width and then add to the next row. But I couldn't achieve this. I am able to get wrappanel behaviour but the elements are not resizing (I have to set the width properties to make sure that only 3 user controls are there in a row). Even when one user control is added its not filling the whole grid.

Please tell me if you need more information.

Upvotes: 0

Views: 255

Answers (2)

dev hedgehog
dev hedgehog

Reputation: 8791

There are already tons of nice tuts on the internet showing you how to have such WrapPanel. Here is one:

public class ElasticWrapPanel : Panel
{
    /// <summary>
    /// Identifies the <see cref="DesiredColumnWidth"/> dependency property. 
    /// </summary>
    internal static readonly DependencyProperty DesiredColumnWidthProperty = DependencyProperty.Register("DesiredColumnWidth", typeof(double), typeof(ElasticWrapPanel), new PropertyMetadata(100d, new PropertyChangedCallback(OnDesiredColumnWidthChanged)));

    private int _columns;

    protected override Size MeasureOverride(Size availableSize)
    {
        _columns = (int) (availableSize.Width / DesiredColumnWidth);

        foreach (UIElement item in this.Children)
        {
            item.Measure(availableSize);
        }

        return base.MeasureOverride(availableSize);
    }

    protected override Size ArrangeOverride(Size finalSize)
    {
        if (_columns != 0)
        {
            double columnWidth = Math.Floor(finalSize.Width/_columns);

            double top = 0;
            double rowHeight = 0;
            int column = 0;
            foreach (UIElement item in this.Children)
            {
                item.Arrange(new Rect(columnWidth * column, top, columnWidth, item.DesiredSize.Height));                    
                column++;
                rowHeight = Math.Max(rowHeight, item.DesiredSize.Height);

                if (column == _columns)
                {
                    column = 0;                        
                    top += rowHeight;
                    rowHeight = 0;
                }
            }
        }

        return base.ArrangeOverride(finalSize);
    }

    private static void OnDesiredColumnWidthChanged(DependencyObject obj, DependencyPropertyChangedEventArgs e)
    {
        var panel = (ElasticWrapPanel)obj;
        panel.InvalidateMeasure();
        panel.InvalidateArrange();
    }

    public double DesiredColumnWidth
    {
        get
        {
            return (double)GetValue(DesiredColumnWidthProperty);
        }

        set
        {
            SetValue(DesiredColumnWidthProperty, value);
        }
    }
}

In xaml you will have something like this:

   <c:ElasticWrapPanel DesiredColumnWidth="200">
        <Button Content="Item1" />
        <Button Content="Item2" />
        <Button Content="Item3" />
        <Button Content="Item4" />
        <Button Content="Item5" />
        <Button Content="Item6" />
    </c:ElasticWrapPanel>

Upvotes: 2

Sheridan
Sheridan

Reputation: 69979

The behaviour that you describe is the normal behaviour of the WrapPanel. If you want to have a Panel that automatically fills the space on each row, then you will need to create a custom Panel for yourself. Creating a custom Panel is not as difficult as it seems if you're good at mathematics... there are only two methods to override in which you calculate the sizes of each element.

You can find out hot to create a custom Panel from the following links:

How to create a Custom Layout Panel in WPF
Creating Custom Panels In WPF
Custom Panel Elements section of Panels Overview

Upvotes: 1

Related Questions