StackPanel items don't wrap to new lines - wpf

I have an ItemsControl with a StackPanel as the ItemsPanelTemplate and the items which get displayed inside (the Button) are not wrapped to new lines. I've tried adding the HorizontalContentAlignment property to the ItemsControl and play with the options but it doesn't help. What am I doing wrong?
<ItemsControl ItemsSource="{Binding RecipientsNames}">
<ItemsControl.ItemsPanel>
<ItemsPanelTemplate>
<StackPanel Orientation="Horizontal"/>
</ItemsPanelTemplate>
</ItemsControl.ItemsPanel>
<ItemsControl.ItemTemplate>
<DataTemplate>
<Button x:Name="btnContact" Click="BtnContact_Click"
Width="Auto" Height="14" Padding="0" BorderThickness="0" Margin="0 0 6 0" HorizontalAlignment="Left" VerticalAlignment="Top">
<TextBlock Text="{Binding Path=Name}" FontSize="12" Margin="0 -2 0 -2"/>
</Button>
</DataTemplate>
</ItemsControl.ItemTemplate>
</ItemsControl>

you are getting your panels mixed up,
A StackPanel expands infinity in a linear direction.
a WrapPanel stacks elements until they hit the edge of the visible
area then starts a new stack under the previous expanding
perpendicular to the stacking direction
a UniformGrid sizes itself to fit the entire area and then splits it
into a set number of rows and columns, so if you set it to 3 columns
and add 4 items the 4th will appear in column 1 of a second row,
because the uniform grid unlike the WrapPanel this isn't affected by item size i generally find it produces more visually appealing layouts to the wrap panel but does need to be configured to make best use of the available space

Related

How to setup a vertical WPF WrapPanel to use as much columns as possible

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".
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
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.

How to make the items in a WPF ListBox wrap horizontally and vertically

I want to show a list of thumbnails, and allow the user to choose one. A ListBox seemed an obvious choice, setting the template to include the thumbnail and description.
The following code does this correctly...
<ListBox Margin="5"
Name="BackgroundsLb">
<ListBox.ItemTemplate>
<DataTemplate>
<Border Margin="5"
BorderBrush="Black"
BorderThickness="1">
<StackPanel Margin="5">
<Image Source="{Binding Path=Data, Converter={StaticResource BytesToImageVC}}"
Width="150"
HorizontalAlignment="Center" />
<TextBlock Text="{Binding Path=Description}"
HorizontalAlignment="Center" />
</StackPanel>
</Border>
</DataTemplate>
</ListBox.ItemTemplate>
</ListBox>
However, this displays the thumbnails vertically, one per row, as is normal for a ListBox. Given that the thumbnails are only 150 pixels wide, I would like to show them in something more like a grid, but (ideally) in a way so that the number of columns adapts to the size of the window.
I tried replacing the ListBox with an ItemsControl, adding in the following...
<ItemsControl.ItemsPanel>
<ItemsPanelTemplate>
<WrapPanel Orientation="Vertical" />
</ItemsPanelTemplate>
</ItemsControl.ItemsPanel>
...and it displayed exactly how I wanted it, but the ItemsControl does not allow selection, so is no use for my purpose.
Is there a way to achieve a flexible, selectable display that fills the horizontal space, and breaks onto a new row according to the size of the window?
Thanks
You can use an ItemsPanelTemplate in a ListBox just the same as you are using one in the ItemsControl. The difference I think you're seeing is that ListBox uses scroll bars by default rather than wrapping the content. Basically the content is allowed to grow forever, so you never get the wrap. Instead you get a scrollbar. The good news is you can disable this behavior. The following should give you a horizontal wrap, where new rows are created as needed.
<ListBox ScrollViewer.HorizontalScrollBarVisibility="Disabled">
<ListBox.ItemsPanel>
<ItemsPanelTemplate>
<WrapPanel Orientation="Horizontal" />
</ItemsPanelTemplate>
</ListBox.ItemsPanel>
</ListBox>

WPF itemscontrol child alignment

I have a WPF MVVM application with an itemscontrol. The number of horizontal child items is influenced by the itemscontrol width. There is only 1 thing i haven't solved. I'm trying to align the child elements in a way that there are always centered. I have made quick pictures in paint to demonstrate what i mean.
How it's now:
If the width further inceares then a forth item will be added horizontally. This function needs to stay.
How I would like to see it:
If there is enough room for a forth item then it needs to be added. I'm thinking that the answer could be a simple XAML property. Anybody an idea?
Put the ItemsControl in a Grid and set its HorizontalAlignment to Center:
<Grid>
<ItemsControl ItemsSource="{Binding Images}" HorizontalAlignment="Center">
<ItemsControl.ItemsPanel>
<ItemsPanelTemplate>
<WrapPanel Orientation="Horizontal"/>
</ItemsPanelTemplate>
</ItemsControl.ItemsPanel>
<ItemsControl.ItemTemplate>
<DataTemplate>
<Image Source="{Binding}" .../>
</DataTemplate>
</ItemsControl.ItemTemplate>
</ItemsControl>
</Grid>

Looking for a WPF multicolumn list with scaling

I would like to create a multi-column list of checkboxes, but here's the catch - as I resize the window I would like everything to scale accordingly, including text size. I've been trying to make use of a WrapPanel and ViewBox but can't get the XAML right. Are these controls the best option or should I be using a ListBox (note I don't need selection functionality or scrollbars)? Any suggestions or examples on how I could achieve this would be much appreciated. I'm using MVVM and the list will be data bound, if that makes a difference.
BTW since starting WPF I've been struggling to understand which controls size to their children and which size to their parent. Are there any good sites, cheat sheets, or whatever summarising the behaviour of each control?
If you have a variable number of child elements, you could put a UniformGrid into a ViewBox.
If the child elements have to be visualized by a DataTemplate, you would have to use an ItemsControl with the ItemsPanel property set to such a UniformGrid:
<Viewbox Stretch="Uniform">
<ItemsControl ItemsSource="{Binding Items}" Width="400" Height="200">
<ItemsControl.ItemsPanel>
<ItemsPanelTemplate>
<UniformGrid Columns="4"/>
</ItemsPanelTemplate>
</ItemsControl.ItemsPanel>
<ItemsControl.ItemTemplate>
<DataTemplate>
<Border Background="AliceBlue">
<CheckBox Content="{Binding Label}" IsChecked="{Binding IsChecked}"
HorizontalAlignment="Center" VerticalAlignment="Center"/>
</Border>
</DataTemplate>
</ItemsControl.ItemTemplate>
</ItemsControl>
</Viewbox>

How to set the number of ListView columns dynamically

I got a window containing two usercontrols, a usercontrol with a listview and a usercontrol with some other controls.
Something like this:
+-------------------------------------------------+
| header stuff |
+---------------------------+---------------------+
| usercontrol with listview | another usercontrol |
+-------------------------------------------------+
| footer stuff |
+-------------------------------------------------+
Problem: on screens with low resolution its only possible to see around 2 of the 3 columns and you need to scroll to see the thrid column. I want to avoid the horizontal scrolling by dynamically setting whether to show 1,2 or 3 columns depending on the width. Another problem is that the names can
be very long, so the width of the items in the listview all have same width as the the longest name.
Code for listview:
<ListView Name="lstContacts"
ItemsSource="{Binding Path=Contacts}"
IsSynchronizedWithCurrentItem="True"
ItemContainerStyle="{StaticResource RoundedItem}"
SelectionMode="Single"
HorizontalContentAlignment="Center"
VerticalAlignment="Top"
HorizontalAlignment="Stretch" >
<ListView.ItemTemplate>
<DataTemplate>
<StackPanel Orientation="Vertical" Margin="3">
<TextBlock FontWeight="Bold" FontSize="20" Text="{Binding Path=Identifier}" HorizontalAlignment="Center" />
<TextBlock FontSize="16" Text="{Binding Path=Name}" HorizontalAlignment="Center" />
</StackPanel>
</DataTemplate>
</ListView.ItemTemplate>
<ListView.ItemsPanel>
<ItemsPanelTemplate>
<UniformGrid Columns="3" />
</ItemsPanelTemplate>
</ListView.ItemsPanel>
</ListView>
Any ideas on how to sort this out ?
Perhaps the listview is a bad choice ?
Thanks.
You could use a WrapPanel as the ItemsPanel:
<ItemsPanelTemplate>
<WrapPanel />
</ItemsPanelTemplate>
Note that there is no native virtualizing WrapPanel, so this will not perform well for a large collection of items.
since your names can be long you can :
1- use textbox having textwrapping and Max/Min Height.
2- decrease fonts size for Names.
3- put Names in a ViewBox for them to always fit. (wrap also)

Resources