WPF - Panel seems to load all item at once - wpf

I want to show 2000 items with image (thumbnail).
If I use normal list view, it will consume about 2GB memory with UI virtualization. Therefore, I tried to handle this problem using Data Virtualization followed [https://github.com/lvaleriu/Virtualization]. The idea is to load only a page of items. When we scroll down until access more than half of a page, a new page is requested. Hence, only several pages are stored in the memory and the memory consumed is about 150MB.
All good until I want to display items in a grid view (like using wrap panel or uniform gird). The problem now is that all pages are requested at the beginning leading to 2GB memory used. I investigate a bit and the problem may lie on itemspanel. It tries to load all data to locate the item and that what's exactly what I am trying to avoid.
Does anyone meet similar problem? Can you help me figure it out?
Thank you in advanced
<ListView ItemsSource="{Binding}" VirtualizingPanel.IsVirtualizing="True"
VirtualizingPanel.VirtualizationMode="Recycling"
ScrollViewer.IsDeferredScrollingEnabled="True">
<!-- Problem when I add this part-->
<ListView.ItemsPanel>
<ItemsPanelTemplate>
<WrapPanel></WrapPanel>
</ItemsPanelTemplate>
</ListView.ItemsPanel>
<!-- End -->
<ListView.ItemContainerStyle>
<Style TargetType="ListBoxItem">
<Setter Property="MinWidth" Value="200"/>
<Setter Property="MaxWidth" Value="250"/>
</Style>
</ListView.ItemContainerStyle>
<ListView.ItemTemplate>
<DataTemplate>
<DockPanel LastChildFill="True" MinWidth="200" MaxWidth="250">
<TextBlock TextWrapping="WrapWithOverflow" Text="{Binding Data.FullPath}"
TextAlignment="Center" DockPanel.Dock="Bottom" Width="auto"/>
<Image Source="{Binding Data.FullPath, Converter={local:FileToIconConverter}}"
DockPanel.Dock="Top" MaxHeight="200"/>
</DockPanel>
</DataTemplate>
</ListView.ItemTemplate>
</ListView>
Data virtualization code can be found in [https://github.com/lvaleriu/Virtualization]. I won't post here since it is a bit complicated.

Related

Which kind of UI should I provide for Cameras monitoring in WPF?

I've been asked to develop a WPF application that retrieves data from some cameras and shows the output on some panes inside the application. I've just found the UserControl to have the video playback and it's based on VideoOSPlatform (by milestone).
My question doesn't concert about how to reproduce the video playback but about the user-interface, I should present.
The scenario is the following. I can have max 6 video playback on the application but eventually, the user can choose which camera to show. (For this I thought about a treeview where the user drags the camera into the workspace.
The main question is about how do I divide the application context to show the playback? I think I can't just use a Grid.RowsDefinition / Grid.ColumnsDefinition and divide it into 6 pieces. What if the user has just one camera active? I would have it fullscreen.
Any suggestion/experience about this?
Thanks
Use an ItemsControl with a UniformGrid. The UniformGrid automatically adds rows / columns depending on configuration and amount of items shown.
<ItemsControl Grid.Row="0" ItemsSource="{Binding LoadedDevices}">
<ItemsControl.ItemContainerStyle>
<Style>
<Setter Property="FrameworkElement.Margin" Value="5"/>
</Style>
</ItemsControl.ItemContainerStyle>
<ItemsControl.ItemsPanel>
<ItemsPanelTemplate>
<UniformGrid HorizontalAlignment="Stretch" VerticalAlignment="Stretch"/>
</ItemsPanelTemplate>
</ItemsControl.ItemsPanel>
<ItemsControl.ItemTemplate>
<DataTemplate>
<Border BorderThickness="1" BorderBrush="Black">
<Grid>
<views:DeviceView x:Name="display" Img="{Binding DeviceImage}"/>
</Grid>
</Border>
</DataTemplate>
</ItemsControl.ItemTemplate>
</ItemsControl>
Which then (can) look like this:

ItemsControl Not Creating ContentPresenter

Why isn't my ItemsControl creating a ContentPresenter for each item? I'm guessing this is what's making my items not show up (they're set to visible and in the right spot when I inspect using the Live Visual Tree). I'm basically reusing code that works up above in a different ItemsControl and I haven't been able to find anything while searching Google/Stackoverflow with this issue. I can include view model code but I don't think it's related because I see the appropriate values in the Live Property Explorer and can see each WellContainer is in it's appropriate grid cell.
XAML:
<ItemsControl
Grid.Row="1"
Grid.Column="1"
ItemsSource="{Binding Wells}">
<ItemsControl.ItemsPanel>
<ItemsPanelTemplate>
<Grid
x:Name="m_WellGrid"
Margin="5"
wpf:GridHelpers.RowCount="{Binding RowCount}"
wpf:GridHelpers.ColumnCount="{Binding ColumnCount}">
</Grid>
</ItemsPanelTemplate>
</ItemsControl.ItemsPanel>
<ItemsControl.ItemContainerStyle>
<Style>
<Setter
Property="Grid.Row"
Value="{Binding Path=WellRow}"/>
<Setter
Property="Grid.Column"
Value="{Binding Path=WellCol}"/>
</Style>
</ItemsControl.ItemContainerStyle>
<ItemsControl.ItemTemplate>
<DataTemplate>
<TextBlock
Text="A"
Margin="4"/>
</DataTemplate>
</ItemsControl.ItemTemplate>
</ItemsControl>
Live Visual Tree Inspection:
The ItemsControl is designed to wrap the items in a container only when necessary, that is, when the item is not eligible to be its own container. From your comment we find that WellContainer derives from Control, thus is eligible to be its own container1 and is not wrapped in a ContentPresenter. Unfortunately there's no way to control this behavior directly, but you could subclass ItemsControl and override the ItemsControl.IsItemItsOwnContainerOverride method to modify the default behavior.
1 As we can see in the ItemsControl source code it is enough for the item to be of UIElement type to be eligible to be its own container.

Grouping, virtualization and scrolling in a ListBox

Using following code ListBox jumps to the next group during scrolling. Since one group shows more items than can fit screen, user never sees all items of the group.
Since I have a lot of items I need viritualization.
Setting CanContentScroll="False" fixes the issue but then virtualization is gone and UI hangs for 20 seconds. Is there a way around this?
<ListBox
VirtualizingStackPanel.IsVirtualizing="True"
VirtualizingStackPanel.VirtualizationMode="Recycling"
ScrollViewer.CanContentScroll="True">
<ListBox.GroupStyle>
<GroupStyle HidesIfEmpty="True">
<GroupStyle.ContainerStyle>
<Style TargetType="{x:Type GroupItem}">
<Setter Property="Template">
<Setter.Value>
<ControlTemplate>
<Expander IsExpanded="True">
<Expander.Header>
<StackPanel Orientation="Horizontal">
<TextBlock Text="{Binding Type}" />
...
</StackPanel>
</Expander.Header>
<ItemsPresenter />
Try using the ScrollUnit=Pixel property to get smooth scrolling. The default is ScrollUnit=Item which causes the group to scroll as one unit.
<ListBox
VirtualizingPanel.IsVirtualizing='True'
VirtualizingPanel.IsVirtualizingWhenGrouping='True'
VirtualizingPanel.ScrollUnit='Pixel'
/>

Expander with Virtualization inside WPF Datagrid

I have a performance issue in a prototype I am working on. The requirement is to build a datagrid with multiple synchronized frozen panes, supporting grouping and sorting etc... For more details about the grid I am building, see this previous question.
Now, I have a question related to Grouping and in particular Expanders. I have a GroupStyle defined by the following Xaml, and taken from this blog post.
<!--Default GroupStyle-->
<GroupStyle x:Key="gs_Default">
<GroupStyle.Panel>
<ItemsPanelTemplate>
<DataGridRowsPresenter/>
</ItemsPanelTemplate>
</GroupStyle.Panel>
<GroupStyle.HeaderTemplate>
<DataTemplate>
<StackPanel>
<TextBlock Text="{Binding Path=Name}" FontWeight="Bold" Padding="3"/>
</StackPanel>
</DataTemplate>
</GroupStyle.HeaderTemplate>
<GroupStyle.ContainerStyle>
<Style TargetType="{x:Type GroupItem}">
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="{x:Type GroupItem}">
<Expander x:Name="exp"
BorderBrush="#FFA4B97F"
BorderThickness="0,0,0,1"
IsExpanded="{Binding Path=Items[0].IsExpanded}">
<Expander.Header>
<DockPanel TextBlock.FontWeight="Bold">
<TextBlock Text="{Binding Path=Name}" Margin="5,0,5,0" />
<TextBlock Text="{Binding Path=ItemCount}"/>
</DockPanel>
</Expander.Header>
<ItemsPresenter/>
</Expander>
</ControlTemplate>
</Setter.Value>
</Setter>
</Style>
</GroupStyle.ContainerStyle>
</GroupStyle>
The Expander is not virtualized and we are experiencing a performance issue when there are several hundred rows in a group.
Has anyone encoutered this before and have a fix? I am ideally looking for a Virtualizing Expander, and have seen workarounds such as this (which doesn't solve the problem).
If you're using .NET 4 then get the ItemsPresenters visibility to track/be dependent on the IsExpanded state.
WPF DataGrid Virtualization with Grouping
In .NET 4.5 there's a new attached property VirtualizingPanel.IsVirtualizingWhenGrouping.
Another alternative is to push the grouping into the ViewModel rather than making the DataGrid/CollectionView be responsible for that. See here:
http://blog.smoura.com/wpf-toolkit-datagrid-part-iv-templatecolumns-and-row-grouping/
In order to solve this, we did the following.
We used DataTemplates to present a different row view for different row viewmodels. We had a GroupRowViewModel and ItemRowViewModel. Also a parent ViewModel which had a sorted list of Group/Item viewmodels.
When the grid was instantiated, the parent ViewModel would sort all child viewmodels into the following:
Group
Item
Item
Item
Group
Item
Item
Item
When a GroupRow is clicked you want to execute some code where the parent (which contains a sorted list of group+item rows) will remove or include the items. E.g. say the second gropu was clicked, your list of rowviewmodels you bind to now becomes
Group
Item
Item
Item
Group (Collapsed)
That's it. So no magic at all, you manually remove or include the rows you want depending on what was clicked. It worked with virtualization and hundreds of thousands of rows at an acceptible speed.
Sorry I can't post any code (due to NDA) but I hope that helps you. Also - I would suggest looking at Telerik Grid as this is awesomely fast for large datasets

silverlight 4 treeview slow on expanding when many items

In XAML I have:
<sdk:TreeView x:Name="navigationTreeView" Grid.Column="0" Grid.Row="1" SelectedItemChanged="TreeView_SelectedItemChanged">
<sdk:TreeView.ItemContainerStyle>
<Style TargetType="sdk:TreeViewItem">
<Setter Property="IsExpanded" Value="True"/>
</Style>
</sdk:TreeView.ItemContainerStyle>
<sdk:TreeView.ItemTemplate>
<sdk:HierarchicalDataTemplate ItemsSource="{Binding Path=Nodes}">
<StackPanel Orientation="Horizontal">
<!--<Image Source="{Binding Path=ImageUri}" />-->
<TextBlock Text="{Binding Path=Title}" ToolTipService.ToolTip="{Binding Path=Title}"/>
</StackPanel>
</sdk:HierarchicalDataTemplate>
</sdk:TreeView.ItemTemplate>
</sdk:TreeView>
In code behind:
this.navigationTreeView.ItemsSource = nodes;
navigationTreeView.ExpandAll();
There are 1000 items as children of one node. If I'm not expanding elements everything is fine. But when I expand that node it's pretty slow (10 sec maybe). What could I do to speed it up?
Silverlight 4, 2010 april toolkit.
Unlike WPF, Silverlight (until Silverlight 4, not sure about 5) does NOT support UI virtualization for hierarchical data, and this is why when you expand the node, the 1000 items which are inside the HierarchicalDataTemplate are not virtualized and take more than 10 seconds to load.
I believe Telerik's RadTreeView has its built-in UI virtualization, but the control is not free.
The best solution I have found so far is from this site. Please note it is still a ListBox solution, however, because it doesn't use HierarchicalDataTemplate, it is fully virtualized and looks exactly like a TreeView. Also by looking at the source code, it is quite easy to implement.
I know it is not the perfect answer for this question but at least give you some alternatives. :)
This is how the TreeView works. It's virtualized by default: it won't create TreeViewItems until they're needed. However, you're expanding the entire tree which forces all items to be created. That's just inherently slow. If you really need this sort of behavior (where all choices are expanded), I'd suggest something else like a ListBox. Anything that is scrolled off the screen won't be created until it's needed (but see this caveat.)
My bad stack panel is not in the right place...
Ok try this instead:
<sdk:TreeView x:Name="navigationTreeView" Grid.Column="0" Grid.Row="1" SelectedItemChanged="TreeView_SelectedItemChanged">
<sdk:TreeView.ItemContainerStyle>
<Style TargetType="sdk:TreeViewItem">
<Setter Property="IsExpanded" Value="True"/>
</Style>
</sdk:TreeView.ItemContainerStyle>
<sdk:TreeView.ItemTemplate>
<sdk:HierarchicalDataTemplate ItemsSource="{Binding Path=Nodes}">
<StackPanel Orientation="Horizontal">
<!--<Image Source="{Binding Path=ImageUri}" />-->
<TextBlock Text="{Binding Path=Title}" ToolTipService.ToolTip="{Binding Path=Title}"/>
</StackPanel>
</sdk:HierarchicalDataTemplate>
</sdk:TreeView.ItemTemplate>
<sdk:TreeView.ItemsPanel>
<ItemsPanelTemplate>
<VirtualizingStackPanel />
</ItemsPanelTemplate>
</sdk:TreeView.ItemsPanel>
</sdk:TreeView>
Adding:
<sdk:TreeView.ItemsPanel>
<ItemsPanelTemplate>
<VirtualizingStackPanel />
</ItemsPanelTemplate>
</sdk:TreeView.ItemsPanel>
Let me know the results :D
try this:
<sdk:TreeView.ItemTemplate>
<sdk:HierarchicalDataTemplate ItemsSource="{Binding Path=Nodes}">
<VirtualizingStackPanel Orientation="Horizontal">
<!--<Image Source="{Binding Path=ImageUri}" />-->
<TextBlock Text="{Binding Path=Title}" ToolTipService.ToolTip="{Binding Path=Title}"/>
</VirtualizingStackPanel>
</sdk:HierarchicalDataTemplate>
</sdk:TreeView.ItemTemplate>
Replace the normal StackPanel with one VirtualizingStackPanel...

Resources