Nested ItemsControls optimizations - c#

I have a question regarding nested collections usage in UWP apps with XAML/C#.
Let's say I have a list of items with multiple images in each.
I need to show a scrollable list with all of the images inside the item data.
So far I can see a solution to create GridView with ItemsTemplate that has ItemsControl inside it. But it seams very slow and not optimized solution.
Is there any better suggestion to solve that?

Without much context, I can only think of following :
You have a large amount of data to be displayed, but not all at once. In this case you should consider using controls that support virtualization.
Flattening the data source before binding it. This may improve the performance by a little.
Update
Here is how you can do it :
<ListViewItem ItemsSource="{Binding Posts}">
<ListView.ItemsPanel>
<ItemsPanelTemplate>
<VirtualizingStackPanel />
</ItemsPanelTemplate>
</ListView.ItemsPanel>
<ListView.ItemTemplate>
<DataTemplate>
<StackPanel>
<TextBlock Text="{Binding Title}" />
<TextBlock Text="{Binding Message}" />
<ScrollViewer HorizontalScrollBarVisibility="Auto">
<ItemsControl ItemsSource="{Binding Photos}">
<ItemsControl.ItemsPanel>
<ItemsPanelTemplate>
<VirtualizingStackPanel />
</ItemsPanelTemplate>
</ItemsControl.ItemsPanel>
<ItemsControl.ItemTemplate>
<DataTemplate>
<Image Source="{Binding}" />
</DataTemplate>
</ItemsControl.ItemTemplate>
</ItemsControl>
</ScrollViewer>
</StackPanel>
</DataTemplate>
</ListView.ItemTemplate>
</ListViewItem>

Related

ListView vs ListBox performance

So I have been dealing with a problem representing 10k+ rows in a listview (I might need pagination, but this is not about that), I had 2 problems:
Item source loading took at least 10 seconds to load into the listview which made my UI hang
Lag while focusing the listview
So I wanted to compare the same thing using a listbox than a datagrid. And I was amazed how the listbox reduced the loading time to almost nothing and also there is no lag.
Listview:
<ListView ItemsSource="{Binding SymbolContext}" BorderThickness="0">
<ListView.ItemsPanel>
<ItemsPanelTemplate>
<VirtualizingStackPanel />
</ItemsPanelTemplate>
</ListView.ItemsPanel>
<ListView.ItemTemplate>
<DataTemplate>
<TextBlock Text="{Binding Name}" />
</DataTemplate>
</ListView.ItemTemplate>
</ListView>
ListBox:
<ListBox ItemsSource="{Binding SymbolContext}">
<ListBox.ItemTemplate>
<DataTemplate>
<TextBlock Text="{Binding Name}"></TextBlock>
</DataTemplate>
</ListBox.ItemTemplate>
</ListBox>
So my question is why did it take for the listview so much to represent the data and why did the listbox perform so well?

How to display listbox items in column view in WPF c#

Am using listbox control in WPF its display listbox item in row wise but I want to display in column wise ( Something like bootstrap grid)
XMAL
<ListBox x:Name="lb_items">
<ListBox.ItemTemplate>
<DataTemplate>
<StackPanel Margin="10 0 0 0">
<Image Source="{Binding ImageSource}" />
</StackPanel>
</DataTemplate>
</ListBox.ItemTemplate>
</ListBox>
Am binding Listbox from code behind
lb_items.ItemsSource = modules.ToList();
Try this one.
<ListBox x:Name="lb_items">
<ListBox.ItemsPanel>
<ItemsPanelTemplate>
<WrapPanel IsItemsHost="True" />
</ItemsPanelTemplate>
</ListBox.ItemsPanel>
<ListBox.ItemTemplate>
<DataTemplate>
<StackPanel Margin="10 0 0 0">
<Image Source="{Binding ImageSource}" />
</StackPanel>
</DataTemplate>
</ListBox.ItemTemplate>
</ListBox>
ListBox has property called ItemsPanel, which determines how items are rendered.
Somewhere in application resource create different ItemsPanelTemplate, so you can easily reuse it:
<ItemsPanelTemplate x:Key="WrapPanelTemplate">
<WrapPanel />
</ItemsPanelTemplate>
<ItemsPanelTemplate x:Key="HorizontalStackPanelTemplate">
<StackPanel Orientation="Horizontal"/>
</ItemsPanelTemplate>
then you can easily use it:
<ListBox ItemsPanel="{StaticResource HorizontalStackPanelTemplate}">...
<ItemControl ItemsPanel="{StaticResource WrapPanelTemplate}">...
always put such assets into application resources in order to make the code for your views clean and readable.
Extra Tip:
Here is animated WrapPanel, that animates items, when you resize window or an item is inserted to the list:
<ItemsPanelTemplate x:Key="FluidWrapPanel">
<WrapPanel>
<i:Interaction.Behaviors>
<ei:FluidMoveBehavior AppliesTo="Children" Duration="0:0:0.5">
<ei:FluidMoveBehavior.EaseY>
<SineEase EasingMode="EaseInOut" />
</ei:FluidMoveBehavior.EaseY>
<ei:FluidMoveBehavior.EaseX>
<CubicEase EasingMode="EaseInOut" />
</ei:FluidMoveBehavior.EaseX>
</ei:FluidMoveBehavior>
</i:Interaction.Behaviors>
</WrapPanel>
</ItemsPanelTemplate>
dont forget to include these xml namespaces:
xmlns:i="http://schemas.microsoft.com/expression/2010/interactivity"
xmlns:ei="http://schemas.microsoft.com/expression/2010/interactions"

WPF ContentPresenter inside an ItemsControl.ItemTemplate

I have two user controls, WorkflowTileItemsControl and WorkflowTileControl. The WorkflowTileItemsControl hosts the WorkflowTileControl in an ItemsControl. However, there are dependency properties on the WorkflowTileControl that I would like to expose to anything using the WorkflowTileItemsControl. In order to do that here is ItemsControl code for WorkflowTileItemsControl.
<ItemsControl ItemsSource="{Binding Source={StaticResource WorkflowTilesViewSource}}">
<ItemsControl.ItemsPanel>
<ItemsPanelTemplate>
<WrapPanel />
</ItemsPanelTemplate>
</ItemsControl.ItemsPanel>
<ItemsControl.ItemTemplate>
<DataTemplate>
<ContentPresenter Content="{Binding WorkflowTileControl, ElementName=ctrlWorkflowTileItems}" />
</DataTemplate>
</ItemsControl.ItemTemplate>
</ItemsControl>
However this doesn't seem to work. It is only showing the last item in the ItemsControl. Below is code that works, and is the functionality I'm looking for (minus hard coding all the dependency properties).
<ItemsControl ItemsSource="{Binding Source={StaticResource WorkflowTilesViewSource}}">
<ItemsControl.ItemsPanel>
<ItemsPanelTemplate>
<WrapPanel />
</ItemsPanelTemplate>
</ItemsControl.ItemsPanel>
<ItemsControl.ItemTemplate>
<DataTemplate>
<OrderCommon:WorkflowTileControl IsReadOnly="True" Margin="5" TasksTitle="Defects" />
</DataTemplate>
</ItemsControl.ItemTemplate>
</ItemsControl>
And this is what my calling code looks like.
<OrderCommon:WorkflowTileItemsControl WorkflowRequirementTypeCode="DISBURSEMENTDFCT" Margin="5" MinWidth="1000" MaxWidth="1250" HorizontalAlignment="Left">
<OrderCommon:WorkflowTileItemsControl.WorkflowTileControl>
<OrderCommon:WorkflowTileControl IsReadOnly="True" Margin="5" TasksTitle="Defects" />
</OrderCommon:WorkflowTileItemsControl.WorkflowTileControl>
</OrderCommon:WorkflowTileItemsControl>
I feel like there is some simple step I'm missing. I'm not sure if ContentPresenter is the right tool for the job. I haven't done anything like this in WPF before. Can someone please assist?
So after some days of research I've found a solution. The WorkflowTileItemsControl needs to expose a Dependency Property of a DataTemplate which will be bound to the ItemsTemplate for the ItemsControl. Here is the xaml for the WorkflowTileItemsControl:
<ItemsControl ItemsSource="{Binding Source={StaticResource WorkflowTilesViewSource}}" ItemTemplate="{Binding WorkflowTileTemplate, ElementName=ctrlWorkflowTileItems}">
<ItemsControl.ItemsPanel>
<ItemsPanelTemplate>
<WrapPanel />
</ItemsPanelTemplate>
</ItemsControl.ItemsPanel>
</ItemsControl>
And Here is the xaml for the calling control:
<OrderCommon:WorkflowTileItemsControl WorkflowRequirementTypeCode="DISBURSEMENTDFCT" Margin="5" Width="1130" HorizontalAlignment="Left">
<OrderCommon:WorkflowTileItemsControl.WorkflowTileTemplate>
<DataTemplate>
<OrderCommon:WorkflowTileControl IsReadOnly="True" Margin="5" TasksTitle="Defects" />
</DataTemplate>
</OrderCommon:WorkflowTileItemsControl.WorkflowTileTemplate>
</OrderCommon:WorkflowTileItemsControl>

Windows Store App ListView: How to use the GroupStyle?

am porting a Windows Phone App to Windows Store and trying to create a grouped ListView. There is a good article in the Dev Center and grouping the list is no problem. But there are some things I do not understand about the GroupStyle.
The example from the article uses a GroupStyleSelector that leads to the following GroupStyle:
<GroupStyle x:Key="listViewGroupStyle">
<GroupStyle.HeaderTemplate>
<DataTemplate>
<Grid Background="LightGray" >
<TextBlock Text='{Binding Key}' Foreground="Black" Margin="10"
Style="{StaticResource SubheaderTextBlockStyle}" />
</Grid>
</DataTemplate>
</GroupStyle.HeaderTemplate>
<GroupStyle.Panel>
<ItemsPanelTemplate>
<VariableSizedWrapGrid MaximumRowsOrColumns="3" Orientation="Horizontal"/>
</ItemsPanelTemplate>
</GroupStyle.Panel>
</GroupStyle>
The purpose of GroupStyle.HeaderTemplate is obvious and changes on this template can be directly observed in the running app.
But what is GroupStyle.Panel good for? The docs says:
Gets or sets a template that creates the panel used to lay out the items.
Ok, but no matter how I change ItemsPanelTemplate (BackgroundColor, Orientation, etc.), the List does not change in the running app.
Additionally there is GroupStyle.ContainerStyle which is not used in the Example. If add this to the GroupStyle this has no influece on the list in the running app as well.
So, what are GroupStyle.Panel and GroupStyle.ContainerStyle good for? How are the used correctly?
I found out that you need to set the ItemsPanel of the ListView, too. By default the ItemsStackPanel is used and that doesn't seem to allow any custom panel in a grouped ListView. When setting the ItemsPanel to a VirtualizingStackPanel, the custom panel is applied, BUT the headers are not sticky anymore. It seems that there is something broken about the current ListView/ItemsStackPanel implementation and as we do not have source access it's hard to tell what.
<ListView
ItemsSource="{Binding Source={StaticResource namesSource}}"
>
<ListView.GroupStyle>
<GroupStyle>
<GroupStyle.HeaderTemplate>
<DataTemplate>
<TextBlock Text="{Binding Key}" Foreground="Yellow" FontSize="{ThemeResource HubHeaderFontSize}" Margin="6" />
</DataTemplate>
</GroupStyle.HeaderTemplate>
<GroupStyle.Panel>
<ItemsPanelTemplate>
<controls:WrapPanel />
</ItemsPanelTemplate>
</GroupStyle.Panel>
</GroupStyle>
</ListView.GroupStyle>
<ListView.ItemsPanel>
<ItemsPanelTemplate>
<!--<ItemsWrapGrid FlowDirection="LeftToRight" Orientation="Vertical"/>-->
<VirtualizingStackPanel Orientation="Vertical"/>
<!--<ItemsStackPanel Orientation="Vertical"/>-->
</ItemsPanelTemplate>
</ListView.ItemsPanel>
<ListView.ItemTemplate>
<DataTemplate>
<Grid Background="AliceBlue" Margin="3,0">
<TextBlock Text="{Binding}" Foreground="Black" Margin="4,2"/>
</Grid>
</DataTemplate>
</ListView.ItemTemplate>
</ListView>

Binding a list to a flow of WrapPanels

I am trying to do an interface in WPF where a list of item is displayed, taking as little vertical space as possible:
My instinct was to use an ItemsControl to bind my list, and to put the UI for each item into a WrapPanel. Unfortunately by default, each item starts at a new line regardless of the window's size. Adding a StackPanel with Orientation="Horizontal" makes all items in a single line, regardless of the size...
<!--<ScrollViewer Grid.Row="0" Grid.Column="0"
VerticalScrollBarVisibility="Auto">-->
<ItemsControl Margin="0,4" ItemsSource="{Binding Path=Watchers}">
<ItemsControl.ItemsPanel>
<ItemsPanelTemplate>
<StackPanel Orientation="Horizontal"></StackPanel>
</ItemsPanelTemplate>
</ItemsControl.ItemsPanel>
<ItemsControl.Resources>
<DataTemplate DataType="{x:Type Core:Watcher}">
<WrapPanel Orientation="Horizontal">
<TextBlock Margin="0,2" Text="{Binding Path=Name}"
Width="250px" />
<TextBox Text="{Binding Path=Value, Mode=OneWay}"
Width="300px">
</TextBox>
</WrapPanel>
</DataTemplate>
</ItemsControl.Resources>
</ItemsControl>
<!--</ScrollViewer>-->
Any pointers?
Bonus point: Where should I add a ScrollViewer in order to have the behavior shown in my mockup?
Thanks a lot!
The WrapPanel has, as well as the StackPanel, an Orientation property, which defaults to Vertical. So your list should wrap (assuming you have enough space at hand) if your ItemsPanelTemplate looks like this:
<ItemsPanelTemplate>
<WrapPanel Orientation="Horizontal" />
</ItemsPanelTemplate>
About the ScrollViewer:
The desired behavior should be achieved by defining it like follows (not tested though, and I omitted all for this example unnecessary stuff):
<ScrollViewer HorizontalAlignment="Stretch">
<ItemsControl MinWidth="550" Width="{Binding ActualWidth, Mode=OneWay, RelativeSource={RelativeSource FindAncestor, AncestorType={x:Type ScrollViewer}}}" />
</ScrollViewer>
I think you want the WrapPanel in the ItemsPanel and the StackPanel in the ItemTemplate.
why not use the WrapPanel as ItemsPanelTemplate?
<ItemsControl.ItemsPanel>
<ItemsPanelTemplate>
<WrapPanel Orientation="Horizontal"/>
</ItemsPanelTemplate>
</ItemsControl.ItemsPanel>
as long as the items have space to the left they would be arranged horizontal.

Categories