Question

I have a custom control ListBox that I would like to adjust its size according to the size of its ItemsPanel. For the items panel, I have a custom control WrapPanel that arranges the items accordingly. As you can see in the screen shot, the ListBox prefers to size itself to its parent control or availableSize.

So then I tried to make a custom control Grid, that had an ItemsSource property that handed the items to its listbox. But that didn't work either. When the Grid would arrange, then the ListBox would Arrange, which would cause the Grid to Arrange and so on.

So my question is, how to I create a custom control that has an ItemsSource property and an ItemsPresenter that sizes itself according to the contents of its children??

Screenshot of ListBox Custom Control

Was it helpful?

Solution 2

This is what I had to do to get the functionality I wanted. First I have to create my SquareWrapPanel. This is where the magic happens and arranges my items the way I want.

protected override Size ArrangeOverride(Size finalSize)
    {
        return ArrangeByChildCount(finalSize);
    }

    private Size ArrangeByChildCount(Size finalSize)
    {

        double childWidth = 0;
        double childHeight = 0;


        foreach (UIElement e in Children)
        {
            e.Measure(finalSize);

            childWidth = e.DesiredSize.Width;
            childHeight = e.DesiredSize.Height;
        }

        if (Children.Count > 0)
        {
            int square = (int)Math.Sqrt(Children.Count);


            int rowCount = square + Children.Count % square;
            int columnCount = square;

            double height = rowCount * childHeight;
            double width = columnCount * childWidth;

            Size size = new Size(width, height);
            base.ArrangeOverride(size);
            return size;
        }
        else
        {
            return new Size(300, 300);
        }
    }

Then I created a custom panel that extended ItemsControl. That way I could bind a collection of items to it. There is nothing in the code behind, but here is the style I had to use.

    <Style TargetType="{x:Type local:SquareItemsPanel}" BasedOn="{StaticResource {x:Type ItemsControl}}">
    <Setter Property="Template">
        <Setter.Value>
            <ControlTemplate TargetType="ItemsControl">
                <Border BorderBrush="Black" BorderThickness="2" CornerRadius="4">
                    <Expander x:Name="exp" Header="View">
                        <local:SquareWrapPanel IsItemsHost="True"/>
                    </Expander>
                </Border>
            </ControlTemplate>
        </Setter.Value>
    </Setter>
</Style>

OTHER TIPS

You just have to set your ListBox HorizontalAlignment to Left, and VerticalAlignment to Top. This should do the trick.

Simple example :

MainWindow.xaml

<Window x:Class="ListBoxFitItemsPanel.MainWindow"
        xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
        xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
        Title="MainWindow" Height="400" Width="400">
    <ListBox ScrollViewer.HorizontalScrollBarVisibility="Disabled" ScrollViewer.VerticalScrollBarVisibility="Disabled" Background="Red" HorizontalAlignment="Left" VerticalAlignment="Top" >
        <ListBox.ItemsPanel>
            <ItemsPanelTemplate>
                <WrapPanel Background="AntiqueWhite" Margin="5" />
            </ItemsPanelTemplate>
        </ListBox.ItemsPanel>

        <Rectangle Width="100" Height="100" Fill="LightSlateGray" Stroke="Black" StrokeThickness="1" Margin="5" />
        <Rectangle Width="100" Height="100" Fill="LightSlateGray" Stroke="Black" StrokeThickness="1" Margin="5" />
        <Rectangle Width="100" Height="100" Fill="LightSlateGray" Stroke="Black" StrokeThickness="1" Margin="5" />
        <Rectangle Width="100" Height="100" Fill="LightSlateGray" Stroke="Black" StrokeThickness="1" Margin="5" />
        <Rectangle Width="100" Height="100" Fill="LightSlateGray" Stroke="Black" StrokeThickness="1" Margin="5" />
        <Rectangle Width="100" Height="100" Fill="LightSlateGray" Stroke="Black" StrokeThickness="1" Margin="5" />
        <Rectangle Width="100" Height="100" Fill="LightSlateGray" Stroke="Black" StrokeThickness="1" Margin="5" />
        <Rectangle Width="100" Height="100" Fill="LightSlateGray" Stroke="Black" StrokeThickness="1" Margin="5" />
        <Rectangle Width="100" Height="100" Fill="LightSlateGray" Stroke="Black" StrokeThickness="1" Margin="5" />
    </ListBox>
</Window>

enter image description here

EDIT : It also works in a Binding scenario :

MainWindow.xaml

<Window x:Class="ListBoxFitItemsPanel.MainWindow"
        xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
        xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
        xmlns:local="clr-namespace:ListBoxFitItemsPanel"
        Title="MainWindow" Height="400" Width="400">
    <Window.Resources>
        <DataTemplate DataType="{x:Type local:Item}">
            <Rectangle Width="100" Height="100" Fill="LightSlateGray" Stroke="Black" StrokeThickness="1" Margin="5" />
        </DataTemplate>
    </Window.Resources>
    <ListBox ItemsSource="{Binding Items}" ScrollViewer.HorizontalScrollBarVisibility="Disabled" ScrollViewer.VerticalScrollBarVisibility="Disabled" Background="Red" HorizontalAlignment="Left" VerticalAlignment="Top" >
        <ListBox.ItemsPanel>
            <ItemsPanelTemplate>
                <WrapPanel Background="AntiqueWhite" Margin="5" />
            </ItemsPanelTemplate>
        </ListBox.ItemsPanel>
    </ListBox>
</Window>

MainWindow.xaml.cs

using System.Collections.Generic;
using System.Windows;

namespace ListBoxFitItemsPanel
{
    /// <summary>
    /// Interaction logic for MainWindow.xaml
    /// </summary>
    public partial class MainWindow : Window
    {
        public MainWindow()
        {
            InitializeComponent();
            DataContext = this;
        }

        public IEnumerable<Item> Items
        {
            get
            {
                for (int i = 0; i < 9; i++)
                {
                    yield return new Item();
                }
            }
        }
    }

    public class Item { }
}
Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top