0
votes

I have an ItemsControl with WrapPanel as ItemsPanel. The ItemsControl is inside a ScrollViewer.

Depending on how wide the window is, the ItemsControl/WrapPanel shall have more columns to make more use of the screen (and that the user has to scroll less).

If I set the WrapPanel to Orientation="Horizontal" it works as expected. But if I set the Orientation to "Vertical" the ItemsControl/WrapPanel only show one column.

The difference is, that I like the columns to be like newspaper columns, so like:

A   E   I
B   F   J
C   G
D   H

But if the WrapPanel is set to Horizontal the columns are like:

A   B   C
D   E   F
G   H   I
J

How can I setup the WrapPanel to behave like that?

I could of course limit the height. Of course I could realize this with some clever logic, that takes the number of items from the source into account as well as the width of the window. But this would be complicated and probably a mess, that no one else would ever understand (or even myself, If I review this in a couple of years).

I hope that something like this already exists in WPF.


Here is the Pseudo-XAML-Code

<ScrollViewer>
    <StackPanel>

        <!-- Some other stuff -->

        <ItemsControl ItemsSource="{Binding … }">
            <ItemsControl.ItemsPanel>
                <ItemsPanelTemplate>
                    <WrapPanel Orientation="Vertical" />
                </ItemsPanelTemplate>
            </ItemsControl.ItemsPanel>
            <ItemsControl.ItemTemplate>
                <DataTemplate>
                    <Grid Width="300"><!-- … --></Grid>
                </DataTemplate>
            </ItemsControl.ItemTemplate>
        </ItemsControl>

        <!-- Some more other stuff -->

    </StackPanel>
</ScrollViewer>

It works with Orientation="Horizontal" (but then cells are in wrong order), but only shows one column, when Orientation="Vertical".

2

2 Answers

0
votes

I believe Orientation="Vertical" is the option you are looking for. It will attempt to fill a column before wrapping to a new column.

A __D __G
| | | | | 
B | E | .
| | | | .
C_| F_| .->

The trouble you are having is that the WrapPanel is being allowed an infinite amount of vertical layout space by the StackPanel and, as a result, does not have to wrap.

To fix this issue, change your StackPanel to a WrapPanel or another control that does not allow infinite layout space.

Example

<WrapPanel>
    <ItemsControl>
        <ItemsControl.ItemsPanel>
            <ItemsPanelTemplate>
                <WrapPanel Orientation="Vertical"/>
            </ItemsPanelTemplate>
        </ItemsControl.ItemsPanel>

        <Rectangle Height="50" Width="50" Fill="#F000"/>
        <Rectangle Height="50" Width="50" Fill="#F111"/>
        <Rectangle Height="50" Width="50" Fill="#F222"/>
        <!- ...  ->
        <Rectangle Height="50" Width="50" Fill="#F111"/>
        <Rectangle Height="50" Width="50" Fill="#F000"/>
    </ItemsControl>    
</WrapPanel>

Result

Example project result

0
votes

Ok, I came up with a solution, that is pure XAML.

So basically I have a Grid. With two rows. Inside the grid are two almost identical ItemsControls with bindings and WrapPanels. The first has a WrapPanel with Orientation="Horizontal". It is only there to calculate the boundaries for the second ItemsControl. Therefor its Visibility="Hidden". Also it only spans over the first row.

The second ItemsControl has the WrapPanel with Orientation="Vertical". Its Height is bound to the ActualHeight of the first ItemsControl. Therefor it is forced to use multiple Columns, because the height can't get higher then of the height of ItemsControl #1. The second ItemsControl spans over both rows (this is important, otherwise the height can get stuck, wenn resizing the window*).

It's not a very nice solution. More like Brute-force. But of course it works. The first ItemsControl calculates the outer size. I force this size to the second one, which than can distribute the items in a nice pattern.

<ScrollViewer>
    <StackPanel>

        <!-- Some other stuff -->

        <Grid>
            <Grid.RowDefinitions>
                <RowDefinition/>
                <RowDefinition/>
            </Grid.RowDefinitions>

            <!-- first ItemsControl, to calculate the outer boundaries with --> 
            <ItemsControl ItemsSource="{Binding … }" 
                          Name="Calculating_Size"
                          Visibility="Hidden">
                <ItemsControl.ItemsPanel>
                    <ItemsPanelTemplate>
                        <WrapPanel Orientation="Horizontal" />
                    </ItemsPanelTemplate>
                </ItemsControl.ItemsPanel>
                <ItemsControl.ItemTemplate>
                    <DataTemplate>
                        <Grid Width="300"><!-- … --></Grid>
                    </DataTemplate>
                </ItemsControl.ItemTemplate>
            </ItemsControl>

            <!-- Second ItemsControl with fixed size for Vertical WrapPanel --> 
            <ItemsControl ItemsSource="{Binding … }" 
                          Height="{Binding ActualHeight, ElementName=Calculating_Size}"
                          Grid.RowSpan="2">
                <ItemsControl.ItemsPanel>
                    <ItemsPanelTemplate>
                        <WrapPanel Orientation="Vertical" />
                    </ItemsPanelTemplate>
                </ItemsControl.ItemsPanel>
                <ItemsControl.ItemTemplate>
                    <DataTemplate>
                        <Grid Width="300"><!-- … --></Grid>
                    </DataTemplate>
                </ItemsControl.ItemTemplate>
            </ItemsControl>

        <!-- Some more other stuff -->

    </StackPanel>
</ScrollViewer>



* The reason for the second row is, that if the first WrapPanel reorganizing, because the width increases, it still fixed with the height because the row cannot get smaller, that the second ItemsControl. But, if the second ItemsControl spans over two rows, the first row can get smaller independent of the second ItemsControl. The second gets smaller immediteately afterwards. But this seems to be an extra step.