XAML virtualization of ItemsControl with header - wpf

I have a long page with a ScrollViewer (like web-page).
Page contains header and list of lazy loading infinite scrolling content. Header should scroll away with list content. Header height is 5 times larger than list item height.
Is there any good way to virtualize this?
<ScrollViewer>
<StackPanel>
<TextBlock Text="Header:" />
<StackPanel Orientation="Horizontal" Height="500">
<!--Complex UI-->
</StackPanel>
<TextBlock Text="Videos:"/>
<ItemsControl ItemsSource="{Binding Videos}"
ItemsTemplate="{StaticResource VideoDataTemplate}" />
</StackPanel>
</ScrollViewer>
What I've tried:
Make DataTemplateSelector for header/list item and put everything into one ItemsControl. Reason:
ScrollViewer with CanContentScroll="True" scrolls header away on first scroll tick. This is not acceptable because header is too large.
Make two ScrollViewers. One for header with large bottom margin, one for items with header height top margin. Reason: Too hard to sync top offset and mouse events.

I found a surprising solution: use of Virtualizing TreeView with HierarchicalDataTemplate.
<TreeView ItemsSource="{Binding VideosViewModel}"
ScrollViewer.VerticalScrollBarVisibility="Visible"
VirtualizingStackPanel.IsVirtualizing="True"
VirtualizingStackPanel.IsVirtualizingWhenGrouping="True"
VirtualizingStackPanel.ScrollUnit="Pixel"
VirtualizingStackPanel.VirtualizationMode="Recycling">
<TreeView.ItemsPanel>
<ItemsPanelTemplate>
<VirtualizingStackPanel />
</ItemsPanelTemplate>
</TreeView.ItemsPanel>
<TreeView.ItemContainerStyle>
<Style TargetType="{x:Type TreeViewItem}">
<Setter Property="IsExpanded" Value="True" />
</Style>
</TreeView.ItemContainerStyle>
<TreeView.ItemTemplate>
<HierarchicalDataTemplate ItemsSource="{Binding Videos}">
<!--Header-->
<StackPanel Orientation="Horizontal" Height="500">
<!--Complex UI-->
</StackPanel>
<!--Items-->
<HierarchicalDataTemplate.ItemTemplate>
<DataTemplate>
<TextBlock Text="{Binding Name, Mode=OneTime}" />
</DataTemplate>
</HierarchicalDataTemplate.ItemTemplate>
</HierarchicalDataTemplate>
</TreeView.ItemTemplate>
</TreeView>
This solution is stable and fast.

Related

Scrolling Listbox Fails To Scroll items Horizontally

I have a Listbox where i am binding a list of strings to it. By default the items in the List box scroll vertically. But I want these items to scroll horizontally. Here is the piece of xaml I have.
<ListBox Grid.Row="1" FontSize="25" Name="lstitems" Height="400"
ScrollViewer.HorizontalScrollBarVisibility="Auto"
ScrollViewer.VerticalScrollBarVisibility="Disabled">
<ListBox.ItemContainerStyle>
<Style TargetType="ListBoxItem">
<Setter Property="Padding" Value="0,15,0,0"/>
</Style>
</ListBox.ItemContainerStyle>
<ListBox.ItemTemplate>
<DataTemplate>
<StackPanel>
<TextBlock Name="tbl" Text="{Binding}">
<LineBreak></LineBreak>
</TextBlock>
</StackPanel>
</DataTemplate>
</ListBox.ItemTemplate>
</ListBox>
I tried various approaches but it does not scroll horizontally. With this code I am able to move the items to the left but the rest of the items are not being loaded. The item source property is set from the cs file.
You might try to use a WrapPanel oriented vertically.
<ListBox.ItemsPanel>
<ItemsPanelTemplate>
<WrapPanel Orientation="Vertical"/>
</ItemsPanelTemplate>
</ListBox.ItemsPanel>
Also, don't forget to set ScrollViewer.HorizontalScrollBarVisibility to Auto

Silverlight Textblock inside Listbox causes it to expand instead of wrapping the text

I have something like this:
<ListBox ItemsSource="{Binding List}">
<ListBox.ItemContainerStyle>
<Style TargetType="ListBoxItem">
<Setter Property="HorizontalContentAlignment" Value="Stretch"></Setter>
</Style>
</ListBox.ItemContainerStyle>
<ListBox.ItemTemplate>
<DataTemplate>
<Grid>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="*" />
</Grid.ColumnDefinitions>
<TextBlock Text="{Binding Property}" TextWrapping="Wrap" Grid.Column="0"/>
</Grid>
</DataTemplate>
</ListBox.ItemTemplate>
</ListBox>
And the problem I'm having is that the TextBlock will expand the Grid column (and the Listbox) when the text is too long, instead of wrapping it as expected. Maybe I don't understand the star-sizing concept of Grids completely, but the way I see it, since the column width is set to "1*" which means "the remaining space available", the Textblock should NOT try to expand beyond this width, and should wrap the text instead.
So how can I fix this problem? By the way, I need the Grid (or some other container) because there will be other components besides the Textblock. Also the ItemContainerStyle section is there so that the Listbox element occupies the whole space.
Thanks in advance!
Try to add ScrollViewer.HorizontalScrollBarVisibility="Disabled" to your ListBox.
you don't need column definitions if it will be just one, try with the following:
<ListBox ItemsSource="{Binding List}">
<ListBox.ItemContainerStyle>
<Style TargetType="ListBoxItem">
<Setter Property="HorizontalContentAlignment" Value="Stretch"></Setter>
</Style>
</ListBox.ItemContainerStyle>
<ListBox.ItemTemplate>
<DataTemplate>
<Grid>
<TextBlock Text="{Binding Property}" TextWrapping="Wrap" HorizontalAlignment="Stretch"/>
</Grid>
</DataTemplate>
</ListBox.ItemTemplate>
</ListBox>

Can the groups of a grouped CollectionView be presented horizontally?

I'm implementing a ListBox whose ItemsPanel is a WrapPanel as per this answer, but there's a twist: my ItemsSource is a grouped CollectionView. With a GroupStyle applied to my ListBox, the wrapping shown in that question doesn't work: the groups are always displayed vertically.
Snooping on my app, here's why:
As you can see, the WrapPanel, defined as my ListBox's ItemsPanelTemplate, appears in the ItemsPresenter within each GroupItem; an implicit, vertically-oriented StackPanel (top item in the pink box) is created to contain the GroupItems themselves.
Is there a way to override this behavior, so the GroupItems are placed in a WrapPanel? Would I have to re-template the entire ListBox?
Update: To illustrate what I'm actually doing with my ListBox and the CollectionView grouping, let me post a little XAML:
<Grid>
<ListBox ItemsSource="{Binding}"
ScrollViewer.VerticalScrollBarVisibility="Disabled"
SelectionMode="Multiple"
ItemContainerStyle="{StaticResource itemStyle}">
<ListBox.GroupStyle>
<GroupStyle>
<GroupStyle.HeaderTemplate>
<DataTemplate>
<TextBlock Text="{Binding Name}" FontWeight="Bold"/>
</DataTemplate>
</GroupStyle.HeaderTemplate>
</GroupStyle>
</ListBox.GroupStyle>
<ListBox.ItemTemplate>
<DataTemplate DataType="{x:Type WpfApplication1:Item}">
<StackPanel Orientation="Vertical">
<TextBlock Text="{Binding Name}" FontSize="10"/>
<TextBlock Text="{Binding Amount, StringFormat={}{0:C}}" FontSize="10"/>
</StackPanel>
</DataTemplate>
</ListBox.ItemTemplate>
<ListBox.ItemsPanel>
<ItemsPanelTemplate>
<WrapPanel Orientation="Vertical"/>
</ItemsPanelTemplate>
</ListBox.ItemsPanel>
</ListBox>
</Grid>
The GroupStyle is at the heart of it: if you remove that, the GroupItems don't get rendered, and the WrapPanel (which you can see appearing beneath the GroupItem in the screenshot above) appears in place of (StackPanel) 98 in the screenshot.
This behavior only seems to occur if you have defined a HeaderTemplate in the GroupStyle.
It can be corrected by setting the GroupStyle.Panel property to contain a WrapPanel:
<ListBox.GroupStyle>
<GroupStyle>
<GroupStyle.HeaderTemplate>
<DataTemplate>
<TextBlock Text="{Binding Name}" FontWeight="Bold"/>
</DataTemplate>
</GroupStyle.HeaderTemplate>
<GroupStyle.Panel>
<ItemsPanelTemplate>
<WrapPanel></WrapPanel>
</ItemsPanelTemplate>
</GroupStyle.Panel>
</GroupStyle>
</ListBox.GroupStyle>
It will look something like this:
You should be able to replace the group style from the items control (ListBox):
<ListBox.GroupStyle>
<Style />
<ListBox.GroupStyle/>
Or, you should also be able to create a DataTemplate based on the group item object:
<DataTemplate DataType="{x:Type GroupItem}">
<Panel />
</DataTemplate>

Silverlight: Set Items Widths in ItemsControl to Stretch

I've got an ItemsControl which fills from top to bottom, but I can't get it's child items to occupy the whole width of the ItemsControl:
I basically need to stretch the green bits to fill the width of the control (as shown by the blue bits).
I've tried things like setting the HorizontalAlignment property of the template item to Stretch and I've even tried binding it's Width property to the ItemsControl Width, but neither worked.
Should be a straight forward one, but it's something I just can't quite figure out...
Edit: here's the ItemTemplate (the whole thing is the ItemTemplate which itself contains an ItemsControl which is bound to a list of child objects):
<DataTemplate>
<Border CornerRadius="5" Background="#ddd">
<StackPanel>
<TextBlock Text="{Binding Name}" FontSize="18" Foreground="#bbb"/>
<ItemsControl ItemsSource="{Binding PlugIns}">
<ItemsControl.ItemsPanel>
<ItemsPanelTemplate>
<toolkit:WrapPanel HorizontalAlignment="Stretch" />
</ItemsPanelTemplate>
</ItemsControl.ItemsPanel>
<ItemsControl.ItemTemplate>
<DataTemplate>
<StackPanel Margin="10,0,10,10" Tag="{Binding}"
MouseEnter="StackPanel_MouseEnter">
<Border Child="{Binding Icon}" />
<TextBlock Text="{Binding Name}" />
</StackPanel>
</DataTemplate>
</ItemsControl.ItemTemplate>
</ItemsControl>
</StackPanel>
</Border>
</DataTemplate>
You need to configure the ListBoxItem HorizontalContentAlignment, you do this by using a Style object in the ListBox ItemContainerStyle as follows:-
<ListBox ....>
<ListBox.ItemContainerStyle>
<Style TargetType="ListBoxItem">
<Setter Property="HorizontalContentAlignment" Value="Stretch" />
</Style>
</ListBox.ItemContainerStyle>
OK, so as I continued to build the app up, I figured out how to resolve this. Essentially, when I changed the template of the ItemsControl to support scrolling, the items spanned to fill horizontally :)
<ItemsControl>
<ItemsControl.Template>
<ControlTemplate>
<ScrollViewer Padding="{TemplateBinding Padding}">
<ItemsPresenter />
</ScrollViewer>
</ControlTemplate>
</ItemsControl.Template>
...
</ItemsControl>

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