Horizontal ListBox items vertical layout - wpf

I have horizontal ListBox. Here is code for it (removed some irrelevant):
<ListBox Grid.Row="1"
ItemContainerStyle="{StaticResource ListBoxUnselectableItemStyle}"
ItemsSource="{Binding ...}"
BorderThickness="0"
Background="{x:Null}"
ScrollViewer.CanContentScroll="False">
<ListBox.ItemsPanel>
<ItemsPanelTemplate>
<StackPanel Orientation="Horizontal"
VerticalAlignment="Top"
HorizontalAlignment="Center"
Background="Red"/>
</ItemsPanelTemplate>
</ListBox.ItemsPanel>
</ListBox>
And I get items' layout behavior like this:
]
As you can see, second item is smaller, and VerticalLayout is not Top as I want.
Can anybody help me?

HorizontalAlignment and VerticalAlignment refer to how the StackPanel is aligned within its parent container.
You probably need to be looking at ItemContainerStyle, where you would set VerticalAlignment to Stretch (so that the ListBoxItem occupies the full height of the ListBox), and VerticalContentAlignment to Top (so that the content of the ListBoxItem is aligned to the top of the ListBoxItem).

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.

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>

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>

ListBoxDragDropTarget prevents ListBox from filling its parent control

Its extremely easy with the Silverlight Toolkit to enable basic drag and drop.
http://silverlightfeeds.com/post/1325/Silverlight_Toolkit_adds_DragDrop_targets.aspx
Unfortunately it seems that the wrapper ListBoxDragDropTarget screws up the normal default behavior of a ListBox which is to stretch itself to the parent control - such as a grid cell in this example.
<Grid Background="Yellow">
<toolKit:ListBoxDragDropTarget AllowDrop="True">
<ListBox x:Name="customerListBoxMain"
DisplayMemberPath="Name">
<ListBox.ItemsPanel>
<ItemsPanelTemplate>
<StackPanel Orientation="Vertical"/>
</ItemsPanelTemplate>
</ListBox.ItemsPanel>
</ListBox>
</toolKit:ListBoxDragDropTarget>
</Grid>
I end up here (after binding data to the ListBox) with a small listbox resized to fit its contents sitting in the middle of a yellow box.
No amount of HorizontalAlignment=Stretch etc. seems to be able to get it to fill the parent box.
How can I get the ListBox to fill the Grid?
ListBoxDragDropTarget is derived from content control. Just set HorizontalContentAlignment and
VerticalContentAlignment.
.....
The best I have so far is listening for the size of the wrapper grid to change, and manually updating the size. (I couldn't get this working in XAML so had to use the event).
<Grid Name="myListBoxWrapper" SizeChanged="myListBoxWrapper_SizeChanged">
<controlsToolkit:ListBoxDragDropTarget AllowDrop="True">
<ListBox x:Name="myListBox" >
and in code-behind:
private void myListBoxWrapper_SizeChanged(object sender, SizeChangedEventArgs e)
{
myListBox.Width = myListBoxWrapper.ActualWidth;
myListBox.Height = myListBoxWrapper.ActualHeight;
}
As Archil said, setting HorizontalContentAlignment and VerticalContentAlignment is the way to go, but another way probably would be to bind Width and Height of ListBox to ActualWidth and ActualHeight of ListBoxDragDropTarget:
<toolkit:ListBoxDragDropTarget x:Name="dragdroptarget" AllowDrop="True">
<ListBox x:Name="customerListBoxMain"
DisplayMemberPath="Name"
Width="{Binding ElementName=dragdroptarget, Path=ActualWidth}"
Height="{Binding ElementName=dragdroptarget, Path=ActualHeight}" >
<ListBox.ItemsPanel>
<ItemsPanelTemplate>
<StackPanel Orientation="Vertical"/>
</ItemsPanelTemplate>
</ListBox.ItemsPanel>
</ListBox>
</toolkit:ListBoxDragDropTarget>

Resources