How to use UI virtualization with redefined ListBox templates - wpf

I'm trying to use ListBox as a view containing multiple items and, of course, I need to use UI virtualization in it.
The problem is virtualization works only when I declare ListBox this way:
<ListBox
ItemsSource="{Binding ItemsSource}"
VirtualizingStackPanel.IsVirtualizing="True"
VirtualizingStackPanel.VirtualizationMode="Recycling">
<ListBox.ItemTemplate>
<DataTemplate>
<views:SiteEntryView />
</DataTemplate>
</ListBox.ItemTemplate>
</ListBox>
But if I try to customize it, it doesn't virtualizing anymore:
<ListBox
ItemsSource="{Binding ItemsSource}"
VirtualizingStackPanel.IsVirtualizing="True"
VirtualizingStackPanel.VirtualizationMode="Recycling">
<ListBox.Template>
<ControlTemplate>
<ScrollViewer>
<ItemsPresenter />
</ScrollViewer>
</ControlTemplate>
</ListBox.Template>
<ListBox.ItemsPanel>
<ItemsPanelTemplate>
<StackPanel />
</ItemsPanelTemplate>
</ListBox.ItemsPanel>
<ListBox.ItemTemplate>
<DataTemplate>
<views:SiteEntryView />
</DataTemplate>
</ListBox.ItemTemplate>
</ListBox>
As far as I've found, this sample contains just the same that ListBox contains by default. But virtualization isn't working. I've read several articles and also couple of answers here, but still can't figure out the "general way" - what and where I must set, bind, add, etc to make virtualization work with custom templates?

Two things:
Update your PanelTemplate to use a VirtualizingStackPanel and add your virtualization options to the ScrollViewer of the ControlTemplate.
<ListBox.Template>
<ControlTemplate>
<ScrollViewer VirtualizingStackPanel.IsVirtualizing="True"
VirtualizingStackPanel.VirtualizationMode="Recycling">
<ItemsPresenter />
</ScrollViewer>
</ControlTemplate>
</ListBox.Template>
<ListBox.ItemsPanel>
<ItemsPanelTemplate>
<VirtualizingStackPanel />
</ItemsPanelTemplate>
</ListBox.ItemsPanel>
<ListBox.ItemTemplate>
<DataTemplate>
<views:SiteEntryView />
</DataTemplate>
</ListBox.ItemTemplate>

The reason is that you're using a StackPanel for your ItemsPanel - you should be using a VirtualizingStackPanel instead (which is also the default ItemsPanel for ListBox).
Either remove your ItemsPanel definition or modify it to use a VirtualizingStackPanel:
<ListBox.ItemsPanel>
<ItemsPanelTemplate>
<VirtualizingStackPanel />
</ItemsPanelTemplate>
</ListBox.ItemsPanel>

Related

User Controls Wrapped inside a panel MVVM

I am new to WPF/Xaml and searched for this issue I am facing but found it tough. Requesting some help on this.
I need to display usercontrols (DATABOUND) (horizontally) inside a panel/listview so that they wrap when the width of listview/panel is met, with a vertical scroll bar autoshown (as in figure).
so far I have this code.
<ListView Grid.Row="3"
ItemsSource="{Binding Controls}"
VerticalAlignment="Bottom" Background="{x:Null}" BorderBrush="{x:Null}"
>
<ListView.ItemsPanel>
<ItemsPanelTemplate>
<StackPanel Orientation="Horizontal" Margin="0,0,0,10"></StackPanel>
</ItemsPanelTemplate>
</ListView.ItemsPanel>
<ListView.ItemContainerStyle>
<Style TargetType="ListViewItem">
<Setter Property="VerticalContentAlignment" Value="Bottom"/>
<Setter Property="Focusable" Value="False" />
</Style>
</ListView.ItemContainerStyle>
<!--<ListView.ItemTemplate>
<DataTemplate>
<ContentControl Content="{Binding}" VerticalContentAlignment="Bottom"/>
</DataTemplate>
</ListView.ItemTemplate>-->
</ListView>
I have even tried the below code. Yes, it wraps but no scrollbar appears!
<ItemsControl Grid.Row="3"
Width="100" Height="200"
ItemsSource="{Binding Controls}"
VerticalAlignment="Bottom" Background="{x:Null}" BorderBrush="{x:Null}"
>
<ItemsControl.ItemsPanel>
<ItemsPanelTemplate>
<WrapPanel Orientation="Horizontal" Margin="0,0,0,10"></WrapPanel>
</ItemsPanelTemplate>
</ItemsControl.ItemsPanel>
</ItemsControl>
Note: I am okay with any control that makes this happens. Not necessarily be a listview.
To wrap items you need to set ItemsPanel to WrapPanel, like in second example, but you may need to disable horizontal scrolling on ListView:
<ListView ScrollViewer.HorizontalScrollBarVisibility="Disabled" .../>
also, if you want to use ItemsControl then ScrollViewer is not part of default Template, like for ListView, so you'll need to wrap in in ScrollViewer
<ScrollViewer Grid.Row="3">
<ItemsControl ...>
<!-- .... -->
</ItemsControl>
</ScrollViewer>
and don't forget to move Grid.Row="3" from ItemsControl definition to ScrollViewer
You were close :-)
Put your listview with a view panel that is a wrapanel inside your scrollviewer.
<ScrollViewer>
<ItemsSource="{Binding Controls}">
<ItemsControl.ItemsPanel>
<ItemsPanelTemplate>
<WrapPanel></WrapPanel>
</ItemsPanelTemplate>
</ItemsControl.ItemsPanel>
</ItemsControl>
</ScrollViewer>
You are using Wrap panel so you need to specify the width it supposed to take... since wrappanel is inside a items host control wrap panel itself cannot calculate the width.It will try to take all the width with respective to Orientation (in your case its horizontal)
Here is the sample code with list box as items host... in this code i have binded the wrappanel width to the actual width of the list box so that it will never take more width than the list box and also i disabled the horizontal scrolling which you don't need for horizontal orientation and vertical wrapping
Note: Make sure to change item template before using following code and it will work with all items hosts like ListView, ItemsControl...
<ListBox Width="500" Height="500" Name="listbox" ScrollViewer.HorizontalScrollBarVisibility="Disabled">
<ListBox.ItemsPanel>
<ItemsPanelTemplate>
<WrapPanel Orientation="Horizontal" Width="{Binding ActualWidth,ElementName=listbox}"></WrapPanel>
</ItemsPanelTemplate>
</ListBox.ItemsPanel>
<ListBox.ItemTemplate>
<DataTemplate>
<Grid Height="100" Width="100">
<TextBlock Text="{Binding}" VerticalAlignment="Center" HorizontalAlignment="Center"/>
</Grid>
</DataTemplate>
</ListBox.ItemTemplate>
</ListBox>
For ItemsControl
<ItemsControl Grid.Row="3"
Width="100" Height="200"
ItemsSource="{Binding Controls}"
VerticalAlignment="Bottom" Background="{x:Null}" BorderBrush="{x:Null}"
Name="listbox"
>
<ItemsControl.ItemsPanel>
<ItemsPanelTemplate>
<WrapPanel IsItemsHost="True" Orientation="Horizontal" Width="{Binding ActualWidth,ElementName=listbox}"></WrapPanel>
</ItemsPanelTemplate>
</ItemsControl.ItemsPanel>
<ItemsControl.Template>
<ControlTemplate>
<ScrollViewer x:Name="ScrollViewer" Padding="{TemplateBinding Padding}">
<ItemsPresenter />
</ScrollViewer>
</ControlTemplate>
</ItemsControl.Template>
<ItemsControl.ItemTemplate>
<DataTemplate>
<Grid Height="30" Width="30">
<TextBlock Text="{Binding}" VerticalAlignment="Center" HorizontalAlignment="Center"/>
</Grid>
</DataTemplate>
</ItemsControl.ItemTemplate>
</ItemsControl>

How to set WrapPanel itemsource to list?

I want to show in WrapPanel a list of images. How can I do that or maybe I shall use other control ?
You can absolutely use the WrapPanel to show a list of images, scrolling vertically or horizontally. To get the kind of panoramic tile effect like in People hub with your images, you could do something like this:
<controls:PanoramaItem Header="something" Orientation="Horizontal" Margin="0,-15,0,0" >
<ListBox Name="SomeList" Margin="0,0,-12,0" ItemsSource="{Binding SomeItemsList}" >
<ListBox.ItemsPanel>
<ItemsPanelTemplate>
<toolkit:WrapPanel x:Name="wrapPanel" Width="700" />
</ItemsPanelTemplate>
</ListBox.ItemsPanel>
<ListBox.ItemTemplate>
<DataTemplate>
<StackPanel Orientation="Horizontal" Margin="0,0,0,17">
<Image Height="200" Width="200" Margin="12,0,9,0" Source="{Binding ImageURL}" />
</StackPanel>
</DataTemplate>
</ListBox.ItemTemplate>
</ListBox>
</controls:PanoramaItem>
Please note that a WrapPanel inside a ListBox does pick up the DataTemplate you define .. so you have complete liberty to bind any list to your WrapPanel.
Hope this helps!
Search for the same thing and came across this: Displaying a Collection of Items in a WrapPanel.
<ItemsControl ItemsSource="{Binding ActorList}">
<ItemsControl.ItemTemplate>
<DataTemplate>
<Image Source="{Binding Image}" Height="100"/>
</DataTemplate>
</ItemsControl.ItemTemplate>
<ItemsControl.ItemsPanel>
<ItemsPanelTemplate>
<WrapPanel/>
</ItemsPanelTemplate>
</ItemsControl.ItemsPanel>
</ItemsControl>
or you can use Xceed's SwitchPanel.
Yes definetly not the WrapPanel, it has not ItemsSource, it can't take a list.
Use the ListBox, and you can set the ItemsSource.
Edit

Wrap Panel is not wrapping DataItems

I have WrapPanel where I want the control inside of it to go horizontally and centered, but when I have a listbox or a ItemsControl those elements just go downwards.
<toolkit:WrapPanel>
<ItemsControl x:Name="AnswerListBox" ItemsSource="{Binding Answers}" ScrollViewer.VerticalScrollBarVisibility="Disabled" >
<ItemsControl.ItemTemplate>
<DataTemplate>
<!-- those don't wrap horizontally and go downwards -->
<local:spriteToggleButton Text="{Binding text}" Selected="{Binding selected}" Sprites="{Binding Path=DataContext.UISprites, ElementName=questionField}" IsChecked="{Binding selected, Mode=TwoWay}" GroupName="{Binding Path=DataContext.QuestionTitle, ElementName=questionField}" ClickMode="Press" />
</DataTemplate>
</ItemsControl.ItemTemplate>
</ItemsControl>
</toolkit:WrapPanel>
I came across some similar issues and found out about ItemsPanel, so I tried that but it wrap but only to Content and didn't display the rest of the control inside of it.
<ItemsControl.ItemsPanel>
<ItemsPanelTemplate>
<toolkit:WrapPanel />
</ItemsPanelTemplate>
</ItemsControl.ItemsPanel>
I take it that ItemsPanel is telling the ItemsControl which control to wrap it with but then it seems to ignore the rest of my datatemplate.
Joseph,
Slightly shooting in the dark here; but this is how I have used the WrapPanel (not through the ItemControl).
<ListBox>
<ListBox.ItemsPanel>
<ItemsPanelTemplate>
<toolkit:WrapPanel Width="700" />
</ItemsPanelTemplate>
</ListBox.ItemsPanel>
<ListBox.ItemTemplate>
<DataTemplate>
....
</DataTemplate>
</ListBox.ItemTemplate>
</ListBox>
Essentially the Listbox's ItemsPanel tells it to use the WrapPanel and then you could have anything you need in the DataTemplate. Note that the width is important as it tells the WrapPanel where to start wrapping.
Does this help?

Move down overflow contents in horizontal stackpanel

I have a list box in expander:
<ListBox ItemsSource="{Binding MySource">
<ListBox.ItemTemplate>
<DataTemplate>
<RadioButton Content="{Binding MyContent}" />
</DataTemplate>
</ListBox.ItemTemplate>
<ListBox.ItemsPanel>
<ItemsPanelTemplate>
<StackPanel Orientation="Horizontal" />
</ItemsPanelTemplate>
</ListBox.ItemsPanel>
</ListBox>
I wrap the radio button with horizontal orientation stackpanel. I want the overflow radio buttons move down like right image shown below (no horizontal scrollbar). Now, mine is like the left one.
Stackpanel Orientation="Horizontal" http://www.empirepic.com/images/i8f5sevyzqch10uodso.jpg
You need to use a WrapPanel, not a StackPanel. In WPF it's built into the main assemblies but in Silverlight you'll need to get the Silverlight Toolkit.
<ListBox ScrollViewer.HorizontalScrollBarVisibility="Disabled" ItemsSource="{Binding MySource">
<ListBox.ItemTemplate>
<DataTemplate>
<RadioButton Content="{Binding MyContent}" />
</DataTemplate>
</ListBox.ItemTemplate>
<ListBox.ItemsPanel>
<ItemsPanelTemplate>
<t:WrapPanel />
</ItemsPanelTemplate>
</ListBox.ItemsPanel>
</ListBox>

Silverlight ItemsControl vertical scrollbar, using a wrappanel as ControlTemplate

I have a collection of elements, each one with a name and a subcollection of image blobs.
I want to display an Accordion, with each item representing each of the MainElements. inside each element, I display the images in the subcollecion of said MainElement.
The Accordion gets resized by the user, so I use a wrappanel for presenting the images. When the accordion is wide enough, the images reorder themselves fitting as many as posible in each row.
the problem comes when the wrappanel only displays one image per row (because there's no space enough for more), the image list continues, but I can't see all the images, because they don't fit inside the control's height.
I need a vertical scrollbar to be displayed inside the AccordionItem so I can scroll down the image list.
So, here's my code:
<layoutToolkit:Accordion Width="Auto" Height="Auto" ItemsSource="{Binding MainElementCollection}">
<layoutToolkit:Accordion.ItemTemplate>
<DataTemplate>
<TextBlock Text="{Binding MainElementName}" />
</DataTemplate>
</layoutToolkit:Accordion.ItemTemplate>
<layoutToolkit:Accordion.ContentTemplate>
<DataTemplate>
<ItemsControl ItemsSource="{Binding SubElementCollection}" ScrollViewer.VerticalScrollBarVisibility="Auto" >
<ItemsControl.Template>
<ControlTemplate>
<controlsToolkit:WrapPanel />
</ControlTemplate>
</ItemsControl.Template>
<ItemsControl.ItemTemplate>
<DataTemplate>
<Grid>
<Image Margin="2" Width="150" Source="{Binding PreviewImage, Converter={StaticResource ImageConverter}}" />
</Grid>
</DataTemplate>
</ItemsControl.ItemTemplate>
</ItemsControl>
</DataTemplate>
</layoutToolkit:Accordion.ContentTemplate>
</layoutToolkit:Accordion>
http://www.silverlightshow.net/tips/How-to-add-scrollbars-to-ItemsControl.aspx suggests that I should surround my wrappanel with a scrollviewer, like this
<ItemsControl.Template>
<ControlTemplate>
<scrollviewer>
<controlsToolkit:WrapPanel />
</scrollviewer>
</ControlTemplate>
</ItemsControl.Template>
But then my wrappanel gets really small and I can only see a small vertical scrollbar
Any ideas?
Thanks a lot.
Edit: I figured thatthe wrappanel loses its width when used in the controltemplate
It should be used as follows:
<ItemsControl.ItemsPanel>
<ItemsPanelTemplate>
<controlsToolkit:WrapPanel ScrollViewer.VerticalScrollBarVisibility="Visible" />
</ItemsPanelTemplate>
</ItemsControl.ItemsPanel>
Anyway, I tried adding the ScrollViewer.VerticalScrollBarVisibility="Visible" line but I'm stuck again.
Edited again:
Now my wrappanel looks like this:
<ItemsControl ItemsSource="{Binding StageVideos}">
<ItemsControl.ItemsPanel>
<ItemsPanelTemplate>
<controlsToolkit:WrapPanel />
</ItemsPanelTemplate>
</ItemsControl.ItemsPanel>
<ItemsControl.ItemTemplate>
<DataTemplate>
<Image Margin="2" Width="150" Cursor="Hand" MouseLeftButtonDown="videoPreview_MouseLeftButtonDown" Tag="{Binding}" Source="{Binding PreviewImage, Converter={StaticResource ImageConverter}}" />
</DataTemplate>
</ItemsControl.ItemTemplate>
<ItemsControl.Template>
<ControlTemplate>
<ScrollViewer VerticalScrollBarVisibility="Visible">
<ItemsPresenter />
</ScrollViewer>
</ControlTemplate>
</ItemsControl.Template>
</ItemsControl>
I'm using a wrappanel as the items panel, and I'm using the ControlTemplate to surround the presenter with a scrollviewer. Still, no luck :/
It's working perfectly. i had two different Accordions on the same page, and I was checking my code changes in the one whose code I wasn't touching.
Sometimes you need to pause, go for a walk and then look at the whole screen.
The right code is the last one:
<ItemsControl ItemsSource="{Binding StageVideos}">
<ItemsControl.ItemsPanel>
<ItemsPanelTemplate>
<controlsToolkit:WrapPanel />
</ItemsPanelTemplate>
</ItemsControl.ItemsPanel>
<ItemsControl.ItemTemplate>
<DataTemplate>
<Image Margin="2" Width="150" Cursor="Hand" MouseLeftButtonDown="videoPreview_MouseLeftButtonDown" Tag="{Binding}" Source="{Binding PreviewImage, Converter={StaticResource ImageConverter}}" />
</DataTemplate>
</ItemsControl.ItemTemplate>
<ItemsControl.Template>
<ControlTemplate>
<ScrollViewer VerticalScrollBarVisibility="Visible">
<ItemsPresenter />
</ScrollViewer>
</ControlTemplate>
</ItemsControl.Template>
</ItemsControl>

Resources