Aligning columns without WPF Grid - wpf

I am looking for a good way to design a multi-column layout which reflows the controls in the columns according to the space available. I have a list of labels and fields which display information, and sometimes the view they are contained in needs to be tall and skinny, other times short and wide.
A simple solution is to use a WrapPanel:
<WrapPanel Orientation="Vertical">
<StackPanel Orientation="Horizontal">
<Label>Some label:</Label>
<TextBlock>Some value</TextBlock>
</StackPanel>
<StackPanel Orientation="Horizontal">
<Label>Some other label:</Label>
<TextBlock>Some bigger value</TextBlock>
</StackPanel>
<StackPanel Orientation="Horizontal">
<Label>A:</Label>
<TextBlock>B</TextBlock>
</StackPanel>
</WrapPanel>
I want the labels and values all to line up horizontally into columns, without specifying a static width. Right now the Labels and TextBlocks are just sized based on their content.

Did you try to add WrapPanel as an ItemsContainer in ListBox?
<ListBox>
<ListBox.ItemsContainer>
<WrapPanel />
</ListBox.ItemsContainer>
</ListBox>

Related

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 ListView shrink contents to fit container

I have a ListView that displays x items. It's defined like so;
<UserControl>
<UserControl.Resources>
<DataTemplate x:Key="MyCard" DataType="models:MyModel">
<StackPanel Background="{Binding BackgroundColour}"
Orientation="Vertical">
<Image Source="{Binding ImageSource}"
Stretch="Uniform"/>
<Label Content="{Binding MyType, Converter={StaticResource EnumDisplayNameConverter}}" />
</StackPanel>
</DataTemplate>
</UserControl.Resources>
<ListView ItemsSource="{x:Static myNamespace:MyViewModel.Options}"
ScrollViewer.HorizontalScrollBarVisibility="Disabled"
ScrollViewer.VerticalScrollBarVisibility="Disabled"
ItemTemplate="{StaticResource MyCard}">
<ListView.ItemsPanel>
<ItemsPanelTemplate>
<WrapPanel Orientation="Horizontal"/>
</ItemsPanelTemplate>
</ListView.ItemsPanel>
</ListView>
</UserControl>
Unfortunately, the items still flow off the viewable area and get clipped, like so;
I was expecting that the items would resize to fit the bounds, but this is obviously not the case. I've tried adding a ViewBox to the DataTemplate to resolve this but it's still not working as expected.
The images are all 300px x 300px and I've tried setting MaxWidth/MaxHeight on the StackPanel, ViewBox and Image but it still flows off the "end".
Any suggestions on how I can resolve this?
Inside inner template, do not use StackPanel as it's growing bounds automatically to accommodate content (and would overlap ListBox scrollable area as well). If you expect auto-resizing, use Grid instead or set exact sizes on inner controls like Image etc.

Silverlight 4 - simple control template

<DataTemplate x:Key="dirtSimple">
<TextBlock Margin="10,0,0,0" Text="{Binding Path=CurrentBook.Published, StringFormat=d}"></TextBlock>
</DataTemplate>
<ControlTemplate x:Key="lbWrapPanelTemplate">
<StackPanel Orientation="Horizontal" Margin="2" Background="Aqua">
<ItemsPresenter></ItemsPresenter>
</StackPanel>
</ControlTemplate>
...
<ListBox Template="{StaticResource lbWrapPanelTemplate}" x:Name="bookListBox" Grid.Row="0" ItemsSource="{Binding Path=BookSource}" ItemTemplate="{StaticResource dirtSimple}" >
</ListBox>
The list box is displaying correctly, with a beautiful "Aqua" background, and each item is boringly displayed with just a date. For some reason though the items are not flowing horizontally. I originally tried it with the Silverlight Toolkit's WrapPanel, with the same problem, but I can't even get it to work with a built-in StackPanel, so I suspect I'm missing something.
Are you trying to get selection-based behavior that a ListBox provides? If not, use an ItemsControl (and supply an ItemsPanel as below).
The reason it's not going horizontal is the ItemsPresenter ultimately has its own panel it lays out items in. It's not inserting each item separately into your StackPanel (or WrapPanel), it's putting them in its own panel
What you want to do is specify a value for ItemsPanel like so:
<ListBox ItemTemplate="{StaticResource dirtSimple}">
<ListBox.ItemsPanel>
<ItemsPanelTemplate>
<StackPanel Orientation="Horizontal" />
</ItemsPanelTemplate>
</ListBox.ItemsPanel>
</ListBox>

Window sizing related to contents

I've got two sizing issue regarding a Window I've got. The basic layout is like this
<Window MaxHeight="{DynamicResource {x:Static SystemParameters.VirtualScreenHeight}}"
MaxWidth="{DynamicResource {x:Static SystemParameters.VirtualScreenWidth}}"
>
<StackPanel>
<DockPanel LastChildFill="False">
<StackPanel DockPanel.Dock="Left"
Orientation="Horizontal">
<!--Some buttons-->
</StackPanel>
<StackPanel DockPanel.Dock="Right"
Orientation="Horizontal">
<!--Some buttons-->
</StackPanel>
</DockPanel>
<ScrollViewer>
<WrapPanel x:Name="Container">
</WrapPanel>
</ScrollViewer>
</StackPanel>
</Window>
1) How do I made the Window not get smaller horizontally than the DockPanel's width?
2) How do I make the ScrollViewer be restricted to the limits of the Window? It is sizing itself to its contents, extending past the bounds of the Window.
It sort of used to work when I had
<Window><ScrollViewer/></Window>
, but I really don't want the DockPanel inside the scroller. In the current form, it is even forcing the Window to break its MaxHeight.
I would recommend you to use Grid with * Lenght instead of DockPanel and StackPanel.
Just get rid of those StackPanels. Replace them with Grids and you should be good. The layout logic of the StackPanel is such that it will give children as much room in a certain direction (perpendicular to the StackPanels orientation) as they ask for. That's why you're seeing the odd layout issues.

How to space items evenly in a horizontal Silverlight Listbox

In Silverlight, I have a Vertical ListBox that has a Horizontal ListBox for each item. I want the items in the HorizontalListbox to space evenly across the width of the parent (Vertical) ListBox. How can I do this?
<ListBox x:Name="MachineListBox" Background="Green">
<ListBox.ItemTemplate>
<DataTemplate>
<ListBox ItemsSource="{Binding CoilList}" Background="Red" HorizontalAlignment="Stretch">
<ListBox.ItemsPanel>
<ItemsPanelTemplate>
<StackPanel
HorizontalAlignment="Stretch" />
</ItemsPanelTemplate >
</ListBox.ItemsPanel>
<ListBox.ItemTemplate >
<DataTemplate>
<TextBlock
Text="{Binding Coil}"
HorizontalAlignment="Stretch"/>
</DataTemplate>
</ListBox.ItemTemplate>
</ListBox>
</DataTemplate>
</ListBox.ItemTemplate>
</ListBox>
I'll take the liberty of suggesting you use a custom control of my own invention. It's called a ProportionalPanel and it does just what you need - spaces items evenly. You could use it for the ItemsPanel in the inner ListBox instead of the StackPanel. I also provide the source code, so you can tweak the logic any way you like. The relevant post on my blog is here.
I think the proportional sizing operator will do what you're looking for. Haven't tried it but it sounds like an option. Width="*" and Margin="2*".

Resources