WPF ListView shrink contents to fit container - wpf

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.

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>

Items Box with limit of objects in row (Large icons view style in Windows Explorer)

Can't find a solution to a pretty simple UI problem:
I have a model with a Images property. The Images property holds a collection of items Image.
As for now on - I have a ListBox and binding a ListBoxItem data template to Images.Image and all good. But I have each item on a new line. Not good.
What I am willing to achieve is, lets describe as, a Listbox with Horizontal items orientation and limit of items in a row. Just like Large icons view style in Windows Explorer.
Have somebody previously implemented such a solution? Any advice will be highly appreciated.
Thank you in advance.
Use a WrapPanel (or some other appropriate Panel) as the ListBox's ItemsPanel, and disable horizontal scrolling:
<ListBox ItemsSource="{Binding Images}"
ScrollViewer.HorizontalScrollBarVisibility="Disabled">
<ListBox.ItemsPanel>
<ItemsPanelTemplate>
<WrapPanel/>
</ItemsPanelTemplate>
</ListBox.ItemsPanel>
<ListBox.ItemTemplate>
<DataTemplate>
<Image Source="{Binding}" Width="100" Margin="5"/>
</DataTemplate>
</ListBox.ItemTemplate>
</ListBox>
You can set the ListBox's ItemPanelTemplate to WrapPanel, like this.
I am not sure why it is always like that - but as soon as I asked, I have found an alternative solution with usage of ListView:
<ListView ItemsSource="{Binding Images}">
<ListView.ItemTemplate>
<DataTemplate>
<Image Source="{Binding Image}" />
</DataTemplate>
</ListView.ItemTemplate>
<ListView.ItemsPanel>
<ItemsPanelTemplate>
<UniformGrid Columns="2" />
</ItemsPanelTemplate>
</ListView.ItemsPanel>
</ListView>

How do you make a vertically scrolling scrollviewer inside a tabControl?

I want to make it so that my TabControl is vertically scrollable, but I can't seem to do it. The following sample acts as though there was no scrollviewer at all. I even tried putting the TabControl inside the scrollviewer, or putting it all in a grid and constraining the height of the grid, but nothing works.
<DataTemplate x:Key="tabControlTemplate">
<TabControl ItemsSource="{Binding guiItems}" DisplayMemberPath="Title" Height="Auto" Template="{StaticResource mainTabControlTemplateEx}">
<TabControl.ContentTemplate>
<DataTemplate>
<ScrollViewer Grid.Row="1" VerticalScrollBarVisibility="Auto" CanContentScroll="True">
<StackPanel Margin="2" HorizontalAlignment="Stretch" VerticalAlignment="Stretch">
<ItemsControl ItemsSource="{Binding guiItems }" ItemTemplateSelector="{DynamicResource templateSelector}"/>
</StackPanel>
</ScrollViewer>
</DataTemplate>
</TabControl.ContentTemplate>
</TabControl>
</DataTemplate>
The actual problem is not clear from the question.
You are not getting to see scrollviewer and the content inside is clipped? PLease confirm if that is the case.
If the problem is you are getting to see entire content taking up all the available space, and you would like to control that using scroll viewer, then you would need to set 'MaxHeight' property on Scroll Viewer. This would limit the height of your DataTemplate and would make verticall scroll bar visible if the inner content goes beyond the MaxHeight.
Hope that helps.

WPF disable ListBox autosizing in uniform grid

i do have the following structure:
<UniformGrid HorizontalAlignment="Stretch" Grid.Row="0" VerticalAlignment="Top" Columns="6" DockPanel.Dock="Right" >
<StackPanel HorizontalAlignment="Stretch">
<DockPanel Background="#FF393939" >
<Label Content="{lex:Loc Site}" Foreground="#FFE0E0E0"/>
</DockPanel>
<ListBox Height="300" ItemsSource="{Binding Sites.View}" DisplayMemberPath="Name.ActualTranslation">
</ListBox>
</StackPanel>...
These stackpanels in my UniformGrid should be spreaded over the whole MainWindow (or View)... Still if there is an item in a Listbox which has a longer string which needs more place than the standard width it autofits the string and i have to scroll vertically.
I dont want the listview to gain the width if the content has not enough place.
I also have a scrollviewer in my MainWindow in which this view is placed...
What can i do that the uniform grid stays the same width like the MainWindow-...
I've already tried to give a name to the MainWindow and Bind the width to something like this: Width="{Binding ElementName=MainWindow, Path=ActualWdith}" ..
though i get the error:
Cannot find source for binding with reference 'ElementName=MainWindow'. BindingExpression:Path=ActualWdith; DataItem=null;
What can i do to fix this issue?
If you need more information or my error description isnt clear dont hesitate to ask.
UPDATE:
how it should be:
behavior when the text is too long:
and a scrollbar appears on the bottom..
If you are trying to access a WPF object by name it needs to have x:Key="name" which wasn't shown in your code above, i.e.
<Window x:Name="MainWindow" ....
Alternatively you can walk back up the tree until you find an object of the correct type,
Width="{Binding RelativeSource={RelativeSource FindAncestor, AncestorType={x:Type MainWindow}, Path=ActualWidth}"
Alternatively you can simply change the way you show the text to constrain its width
<ListBox Height="300" ItemsSource="{Binding Sites.View}" DisplayMemberPath="Name.ActualTranslation">
<ListBox.ItemTemplate>
<DataTemplate>
<TextBlock Text="{Binding}" TextWrapping="Wrap" MaxWidth="200"/>
</DataTemplate>
</ListBox.ItemTemplate>
</ListBox>

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>

Resources