Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Is there a way to display items of varying width in wpf wrappanel

Tags:

c#

wpf

I'm trying something like a windows 8 tiles and want to display tiles of varying width and/or height. WrapPanel makes each column equal width and height leaving blank spaces around the smaller items. Is there a way or a panel to display items much like a StackPanel where each items can have individual dimensions and wrap like a WrapPanel?

Edit: This is my ItemsControl. I replaced the Tile DataTemplate with a simple Border and TextBlock. Width is auto and Tiles looks fine except WrapPanel create equal sized cells.

            <ItemsControl ItemsSource="{Binding Path=Contents}" HorizontalContentAlignment="Left">
            <ItemsControl.ItemsPanel>
                <ItemsPanelTemplate>
                    <WrapPanel Orientation="Vertical" />
                </ItemsPanelTemplate>
            </ItemsControl.ItemsPanel>
            <ItemsControl.ItemTemplate>
                <DataTemplate>
                    <Border Background="#B1DBFC" Height="200px" BorderBrush="#404648" Margin="5">
                        <TextBlock VerticalAlignment="Center" Text="{Binding Path=Name}" />
                    </Border>
                </DataTemplate>
            </ItemsControl.ItemTemplate>
        </ItemsControl>

WrapPanel

You can see from the image that the width of each column is the width of widest item.

If I set the width explicitly on the border, things get more ugly. Items with width set

like image 611
Mat J Avatar asked Oct 27 '12 18:10

Mat J


1 Answers

The behaviour you are looking for is the default behaviour of the WrapPanel as can be seen from the following sample.

<WrapPanel >
    <WrapPanel.Resources>  
        <Style TargetType="{x:Type Rectangle}">
            <Setter Property="Width"
                    Value="80" />
            <Setter Property="Height"
                    Value="80" />
            <Setter Property="Margin"
                    Value="3" />
            <Setter Property="Fill"
                    Value="#4DB4DD" />
        </Style>
    </WrapPanel.Resources>

    <Rectangle Width="150" />
    <Rectangle />
    <Rectangle />
    <Rectangle />
    <Rectangle Width="200"/>
    <Rectangle />
    <Rectangle />
    <Rectangle />
    <Rectangle  Width="220"/>
    <Rectangle />
    <Rectangle />

</WrapPanel>

Which produces the following result:

enter image description here

As you can see, The width of each item is honoured.

Your problem is caused by setting the orientation of the WrapPanel to Vertical in your template. This is laying out the items from top-to-bottom rather than left-to-right which means that it is the Height property that you need to be setting (or you could revert back to horizontal layout as in my example).

Compare your output to my screenshot where the panel is set to horizontal orientation; each row is the size of the highest Rectangle. Don't believe me? Try setting one of the Rectangle's Height property to a larger value and you will observe that the row size will increase and the Rectangles no longer line up vertically.

Reading your comments I think the best way to get started is to do the following:

  • Have a WrapPanel that lays out its content horizontally.
  • Items should have uniform Height.
  • Constrain the Height of the WrapPanel to a certain size so that you don't get vertical scrollbars.

The height of your WrapPanel should be worked out using the following formula:

((Height of item + Top Margin + Bottom Margin) x Number of rows))

The width of each item also requires a bit of thought so that the panel lays the items out horizontally like the metro interface (lined up rather than staggered).

There are two tile sizes; the small one is 80px wide and the large one is 166px wide. The width of the large tile is worked out like this:

(item width * 2) + (left margin + right margin)

This ensures the tiles line up correctly.

So now my XAML looks something like this:

<ScrollViewer HorizontalScrollBarVisibility="Auto"
              VerticalScrollBarVisibility="Disabled">
    <StackPanel Orientation="Horizontal"
                Margin="10,0">
        <StackPanel.Resources>
            <Style TargetType="{x:Type Rectangle}">
                <Setter Property="Width"
                        Value="80" />
                <Setter Property="Height"
                        Value="80" />
                <Setter Property="Margin"
                        Value="3" />
                <Setter Property="Fill"
                        Value="#4DB4DD" />
            </Style>
            <Style TargetType="{x:Type WrapPanel}">
                <Setter Property="Margin"
                        Value="0,0,25,0" />
                <Setter Property="MaxWidth"
                        Value="516" />
            </Style>
        </StackPanel.Resources>
        <WrapPanel Height="258">
            <Rectangle Width="166" />
            <Rectangle />
            <Rectangle Width="166" />
            <Rectangle />
            <Rectangle Width="166" />
            <Rectangle />
            <Rectangle />
            <Rectangle />
            <Rectangle />
            <Rectangle Width="166" />
            <Rectangle />
        </WrapPanel>
        <WrapPanel Height="258">
            <Rectangle />
            <Rectangle Width="166" />
            <Rectangle />
            <Rectangle />
            <Rectangle />
            <Rectangle />
            <Rectangle />
            <Rectangle Width="166" />
            <Rectangle />
        </WrapPanel>
    </StackPanel>
</ScrollViewer>

Which produces the following result:

enter image description here

This should give you enough information to start to re-factor this into a full control. If you do this then keep the following in mind:

  • The layout properties (small item size, large item size, gaps etc) should be calculated rather than hard coded so that if you want to change, for example, the margins, the layout will still work correctly (I.E. the tiles will still line up).
  • I would limit the number of 'tiles' that can be displayed (in the current example, if the tiles do not fit in the layout they are simply hidden which is probably not what you want).
like image 77
Benjamin Gale Avatar answered Oct 04 '22 15:10

Benjamin Gale