C# 如何在标准 WPF ListView 中启用 UI 虚拟化
声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow
原文地址: http://stackoverflow.com/questions/14456075/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me):
StackOverFlow
How to enable UI virtualization in Standard WPF ListView
提问by imgen
I'm using .NET 4.5/VS2012, and I have a ListView looks something like this
我正在使用 .NET 4.5/VS2012,我有一个 ListView 看起来像这样
<ListView
VirtualizingPanel.IsContainerVirtualizable="True"
VirtualizingPanel.IsVirtualizing="True"
VirtualizingPanel.IsVirtualizingWhenGrouping="True"
Grid.Row="1"
Name="eventLogList"
Margin="5,0,5,0"
BorderBrush="Black"
BorderThickness="2"
ItemsSource="{Binding EventLogs}"
SelectedItem="{Binding SelectedEventLog}"
local:ListViewSorter.CustomListViewSorter="EventLogViewer.UI.EventLogItemComparer"
SelectionMode="Single">
<ListView.GroupStyle>
<GroupStyle HidesIfEmpty="False">
<GroupStyle.ContainerStyle>
<Style TargetType="GroupItem">
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="GroupItem">
<Expander IsExpanded="True">
<Expander.Header>
<TextBlock FontSize="20" TextWrapping="Wrap" Margin="0,10,0,5" >
<Bold><TextBlock Text="{Binding Name}"/></Bold> - <TextBlock FontSize="20" Text="{Binding ItemCount}"/> logs
</TextBlock>
</Expander.Header>
<ItemsPresenter/>
</Expander>
</ControlTemplate>
</Setter.Value>
</Setter>
</Style>
</GroupStyle.ContainerStyle>
</GroupStyle>
</ListView.GroupStyle>
<ListView.View>
<GridView>
<GridViewColumn
Header="event id"
Width="120"
DisplayMemberBinding="{Binding EventID}" />
<GridViewColumn
Header="level"
Width="160"
DisplayMemberBinding="{Binding Level}" />
<GridViewColumn
Header="server"
Width="160"
DisplayMemberBinding="{Binding Server}" />
<GridViewColumn
Header="log name"
Width="160"
DisplayMemberBinding="{Binding LogName}" />
<GridViewColumn
Header="source"
Width="240"
DisplayMemberBinding="{Binding Source}" />
<GridViewColumn
Header="logged"
Width="240"
DisplayMemberBinding="{Binding Logged}" />
</GridView>
</ListView.View>
</ListView>
But still the performance is not improving at all. I found an example using ListBox, but how to virtualize a ListView? I struggled quite a bit.
I heard that with grouping, the virtualization is turned off in previous version of WPF, but with .NET 4.5, WPF has a IsVirtualizingWhenGrouping
property, I already set it to True
.
但是性能仍然没有改善。我找到了一个使用 ListBox 的示例,但是如何虚拟化 ListView?我很挣扎。我听说通过分组,在以前版本的 WPF 中关闭了虚拟化,但是在 .NET 4.5 中,WPF 有一个IsVirtualizingWhenGrouping
属性,我已经将其设置为True
.
Update: The culprit is custom styling, after removing it, the list view runs smoothly like butter
更新:罪魁祸首是自定义样式,删除它后,列表视图像黄油一样流畅运行
回答by Kalin Krastev
"UI virtualization stores only visible items in memory but in a data-binding scenario stores the entire data structure in memory. In contrast, data virtualization stores only the data items that are visible on the screen in memory."
“UI 虚拟化仅将可见项存储在内存中,但在数据绑定场景中,将整个数据结构存储在内存中。相比之下,数据虚拟化仅将屏幕上可见的数据项存储在内存中。”
"By default, UI virtualization is enabled for the ListView and ListBox controls when their list items are bound to data."
“默认情况下,当 ListView 和 ListBox 控件的列表项绑定到数据时,它们会启用 UI 虚拟化。”
回答by sfaust
I know this is an old question but I came across it looking for an answer to my question and wanted to share what I discovered in case it's useful for anyone else. I had a very similar situation with a ListView control that was not virtualizing. I remove the custom style that I had on it (after reading this thread and associated links) and it started virtualizing correctly.
我知道这是一个老问题,但我遇到它是为了寻找我的问题的答案,并想分享我的发现,以防它对其他人有用。我有一个非常相似的情况,一个 ListView 控件没有虚拟化。我删除了我拥有的自定义样式(在阅读此线程和相关链接后),它开始正确虚拟化。
After much investigation, comparison to the default template, and narrowing down I figure out that it was the 'CanContentScroll' property on the ScrollContentPresenter inside that template. I had not set it at all, and when I set it to true it started virtualizing properly. I also noticed that the default template had 'CanHorizontallyScroll="False"' and 'CanVerticallyScroll="False"'; those didn't seem to make a difference that I could tell in my limited testing (I'm sure someone can chime in and say what they do) but I left them in anyway.
经过大量调查,与默认模板进行比较,并缩小范围后,我发现它是该模板内 ScrollContentPresenter 上的“CanContentScroll”属性。我根本没有设置它,当我将它设置为 true 时,它开始正确虚拟化。我还注意到默认模板有 'CanHorizontallyScroll="False"' 和 'CanVerticallyScroll="False"'; 在我有限的测试中我可以看出这些似乎没有什么不同(我相信有人可以插话并说出他们的所作所为),但无论如何我还是把它们留在了那里。
Here is my final style (note that this was started from default and modified, so not sure where the CanContentScroll property got dropped...):
这是我的最终样式(请注意,这是从默认值开始并进行了修改,因此不确定 CanContentScroll 属性的删除位置...):
<Style x:Key="{x:Static GridView.GridViewScrollViewerStyleKey}"
TargetType="ScrollViewer">
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="ScrollViewer">
<Grid Background="{TemplateBinding Background}">
<Grid.ColumnDefinitions>
<ColumnDefinition Width="*"/>
<ColumnDefinition Width="Auto"/>
</Grid.ColumnDefinitions>
<Grid.RowDefinitions>
<RowDefinition Height="*"/>
<RowDefinition Height="Auto"/>
</Grid.RowDefinitions>
<DockPanel Margin="{TemplateBinding Padding}">
<ScrollViewer DockPanel.Dock="Top"
HorizontalScrollBarVisibility="Hidden"
VerticalScrollBarVisibility="Hidden"
Focusable="false">
<GridViewHeaderRowPresenter Margin="2,0,2,0"
Columns="{Binding Path=TemplatedParent.View.Columns, RelativeSource={RelativeSource TemplatedParent}}"
ColumnHeaderContainerStyle="{Binding
Path=TemplatedParent.View.ColumnHeaderContainerStyle,
RelativeSource={RelativeSource TemplatedParent}}"
ColumnHeaderTemplate="{Binding
Path=TemplatedParent.View.ColumnHeaderTemplate,
RelativeSource={RelativeSource TemplatedParent}}"
ColumnHeaderTemplateSelector="{Binding
Path=TemplatedParent.View.ColumnHeaderTemplateSelector,
RelativeSource={RelativeSource TemplatedParent}}"
AllowsColumnReorder="{Binding
Path=TemplatedParent.View.AllowsColumnReorder,
RelativeSource={RelativeSource TemplatedParent}}"
ColumnHeaderContextMenu="{Binding
Path=TemplatedParent.View.ColumnHeaderContextMenu,
RelativeSource={RelativeSource TemplatedParent}}"
ColumnHeaderToolTip="{Binding
Path=TemplatedParent.View.ColumnHeaderToolTip,
RelativeSource={RelativeSource TemplatedParent}}"
SnapsToDevicePixels="{TemplateBinding SnapsToDevicePixels}"/>
</ScrollViewer>
<ScrollContentPresenter Name="PART_ScrollContentPresenter"
KeyboardNavigation.DirectionalNavigation="Local"
CanContentScroll="True"
CanHorizontallyScroll="False"
CanVerticallyScroll="False"/>
</DockPanel>
<ScrollBar Name="PART_HorizontalScrollBar"
Orientation="Horizontal"
Grid.Row="1"
Maximum="{TemplateBinding ScrollableWidth}"
ViewportSize="{TemplateBinding ViewportWidth}"
Value="{TemplateBinding HorizontalOffset}"
Style="{StaticResource StScrollBarNoMargin}"
Visibility="{TemplateBinding ComputedHorizontalScrollBarVisibility}"/>
<ScrollBar Name="PART_VerticalScrollBar"
Grid.Column="1"
Style="{StaticResource StScrollBarNoMargin}"
Maximum="{TemplateBinding ScrollableHeight}"
ViewportSize="{TemplateBinding ViewportHeight}"
Value="{TemplateBinding VerticalOffset}"
Visibility="{TemplateBinding ComputedVerticalScrollBarVisibility}"/>
</Grid>
</ControlTemplate>
</Setter.Value>
</Setter>
</Style>