WPF 如何虚拟化 ItemsControl
WPF How to virtualize ItemsControl
我有 List
项,共 774 项。当我将它设置为绑定到 ItemsSource
的 ViewModel 的 Items
属性(也 List
)时,它需要大约 10+ 秒。
我已经尝试了 Virtualizing an ItemsControl? 的答案,但它没有用 - 仍然超过 10 秒。
这是未修改的代码。请注意 ItemsControl
在 ScrollViewer
.
中
XAML:
<Grid d:DataContext="{x:Static local:RulesListDesignModel.Instance}" Background="{StaticResource ForegroundLightBrush}">
<ScrollViewer VerticalScrollBarVisibility="Auto">
<ItemsControl ItemsSource="{Binding Items}">
<ItemsControl.ItemTemplate>
<DataTemplate>
<local:RulesListItemControl />
</DataTemplate>
</ItemsControl.ItemTemplate>
</ItemsControl>
</ScrollViewer>
</Grid>
C#
ViewModelApplication.CurrentRulesListViewModel.Items = mList;
这是XAML根据Virtualizing an ItemsControl?的回答修改代码后的(好像用了10多秒):
<Grid d:DataContext="{x:Static local:RulesListDesignModel.Instance}" Background="{StaticResource ForegroundLightBrush}">
<ScrollViewer VerticalScrollBarVisibility="Auto">
<ItemsControl ItemsSource="{Binding Items}"
VirtualizingStackPanel.IsVirtualizing="True"
ScrollViewer.CanContentScroll="True">
<ItemsControl.ItemTemplate>
<DataTemplate>
<local:RulesListItemControl />
</DataTemplate>
</ItemsControl.ItemTemplate>
<ItemsControl.ItemsPanel>
<ItemsPanelTemplate>
<VirtualizingStackPanel />
</ItemsPanelTemplate>
</ItemsControl.ItemsPanel>
<ItemsControl.Template>
<ControlTemplate>
<Border
BorderThickness="{TemplateBinding Border.BorderThickness}"
Padding="{TemplateBinding Control.Padding}"
BorderBrush="{TemplateBinding Border.BorderBrush}"
Background="{TemplateBinding Panel.Background}"
SnapsToDevicePixels="True">
<ScrollViewer
Padding="{TemplateBinding Control.Padding}"
Focusable="False">
<ItemsPresenter
SnapsToDevicePixels="{TemplateBinding UIElement.SnapsToDevicePixels}" />
</ScrollViewer>
</Border>
</ControlTemplate>
</ItemsControl.Template>
</ItemsControl>
</ScrollViewer>
</Grid>
您应该使用 ListBox
或 ListView
,它们集成了 ScrollViewer
并且默认启用了 UI 虚拟化。没有必要使用更基本的 ItemsControl
.
您应该尝试以下方法来使用 UI 虚拟化:
<ListBox VirtualizingStackPanel.VirtualizationMode="Recycling" />
将 VirtualizingStackPanel.VirtualizationMode
设置为 VirtualizationMode.Recycling
可提高滚动性能。
如果您想继续使用 ItemsControl
(为什么要这样做?),您需要重新设计可视化树。
您目前正在使用两个 ScrollViewers
。一个在模板内,一个包裹在 ItemsControl
周围。请注意,由于 ScrollViewer.CanContentScroll
默认为 false
,内部 ScrollViewer
负责禁用 UI 虚拟化。将 CanContentScroll
设置为 true
是必不可少的,因为它将滚动单位设置为项目(而不是像素)。 VirtualizingStackPanel
需要知道可见项的数量。
您应该移除外部 ScrollViewer
并且您的性能应该会显着提高:
<ItemsControl ItemsSource="{Binding Items}">
<ItemsControl.ItemTemplate>
<DataTemplate>
<local:RulesListItemControl />
</DataTemplate>
</ItemsControl.ItemTemplate>
<ItemsControl.ItemsPanel>
<ItemsPanelTemplate>
<VirtualizingStackPanel IsVirtualizing="True"
VirtualizationMode="Recycling" />
</ItemsPanelTemplate>
</ItemsControl.ItemsPanel>
<ItemsControl.Template>
<ControlTemplate TargetType="ItemsControl">
<Border BorderThickness="{TemplateBinding BorderThickness}"
BorderBrush="{TemplateBinding BorderBrush}"
Background="{TemplateBinding Background}">
<ScrollViewer CanContentScroll="True"
Padding="{TemplateBinding Padding}"
Focusable="False">
<ItemsPresenter />
</ScrollViewer>
</Border>
</ControlTemplate>
</ItemsControl.Template>
</ItemsControl>
但更重要的是专注于您的习惯 RulesListItemControl
。为每个项目加载此控件。复杂的控件引入了复杂的初始化。你应该尽量减少这个控件的可视化树。
删除所有不需要的 Border
,将 Label
替换为 TextBlock
,重新访问触发器等。目标是减少每个项目容器的渲染时间。
为此,您需要覆盖用于组成 RulesListItemControl
.
的控件的 ControlTemplate
我有 List
项,共 774 项。当我将它设置为绑定到 ItemsSource
的 ViewModel 的 Items
属性(也 List
)时,它需要大约 10+ 秒。
我已经尝试了 Virtualizing an ItemsControl? 的答案,但它没有用 - 仍然超过 10 秒。
这是未修改的代码。请注意 ItemsControl
在 ScrollViewer
.
XAML:
<Grid d:DataContext="{x:Static local:RulesListDesignModel.Instance}" Background="{StaticResource ForegroundLightBrush}">
<ScrollViewer VerticalScrollBarVisibility="Auto">
<ItemsControl ItemsSource="{Binding Items}">
<ItemsControl.ItemTemplate>
<DataTemplate>
<local:RulesListItemControl />
</DataTemplate>
</ItemsControl.ItemTemplate>
</ItemsControl>
</ScrollViewer>
</Grid>
C#
ViewModelApplication.CurrentRulesListViewModel.Items = mList;
这是XAML根据Virtualizing an ItemsControl?的回答修改代码后的(好像用了10多秒):
<Grid d:DataContext="{x:Static local:RulesListDesignModel.Instance}" Background="{StaticResource ForegroundLightBrush}">
<ScrollViewer VerticalScrollBarVisibility="Auto">
<ItemsControl ItemsSource="{Binding Items}"
VirtualizingStackPanel.IsVirtualizing="True"
ScrollViewer.CanContentScroll="True">
<ItemsControl.ItemTemplate>
<DataTemplate>
<local:RulesListItemControl />
</DataTemplate>
</ItemsControl.ItemTemplate>
<ItemsControl.ItemsPanel>
<ItemsPanelTemplate>
<VirtualizingStackPanel />
</ItemsPanelTemplate>
</ItemsControl.ItemsPanel>
<ItemsControl.Template>
<ControlTemplate>
<Border
BorderThickness="{TemplateBinding Border.BorderThickness}"
Padding="{TemplateBinding Control.Padding}"
BorderBrush="{TemplateBinding Border.BorderBrush}"
Background="{TemplateBinding Panel.Background}"
SnapsToDevicePixels="True">
<ScrollViewer
Padding="{TemplateBinding Control.Padding}"
Focusable="False">
<ItemsPresenter
SnapsToDevicePixels="{TemplateBinding UIElement.SnapsToDevicePixels}" />
</ScrollViewer>
</Border>
</ControlTemplate>
</ItemsControl.Template>
</ItemsControl>
</ScrollViewer>
</Grid>
您应该使用 ListBox
或 ListView
,它们集成了 ScrollViewer
并且默认启用了 UI 虚拟化。没有必要使用更基本的 ItemsControl
.
您应该尝试以下方法来使用 UI 虚拟化:
<ListBox VirtualizingStackPanel.VirtualizationMode="Recycling" />
将 VirtualizingStackPanel.VirtualizationMode
设置为 VirtualizationMode.Recycling
可提高滚动性能。
如果您想继续使用 ItemsControl
(为什么要这样做?),您需要重新设计可视化树。
您目前正在使用两个 ScrollViewers
。一个在模板内,一个包裹在 ItemsControl
周围。请注意,由于 ScrollViewer.CanContentScroll
默认为 false
,内部 ScrollViewer
负责禁用 UI 虚拟化。将 CanContentScroll
设置为 true
是必不可少的,因为它将滚动单位设置为项目(而不是像素)。 VirtualizingStackPanel
需要知道可见项的数量。
您应该移除外部 ScrollViewer
并且您的性能应该会显着提高:
<ItemsControl ItemsSource="{Binding Items}">
<ItemsControl.ItemTemplate>
<DataTemplate>
<local:RulesListItemControl />
</DataTemplate>
</ItemsControl.ItemTemplate>
<ItemsControl.ItemsPanel>
<ItemsPanelTemplate>
<VirtualizingStackPanel IsVirtualizing="True"
VirtualizationMode="Recycling" />
</ItemsPanelTemplate>
</ItemsControl.ItemsPanel>
<ItemsControl.Template>
<ControlTemplate TargetType="ItemsControl">
<Border BorderThickness="{TemplateBinding BorderThickness}"
BorderBrush="{TemplateBinding BorderBrush}"
Background="{TemplateBinding Background}">
<ScrollViewer CanContentScroll="True"
Padding="{TemplateBinding Padding}"
Focusable="False">
<ItemsPresenter />
</ScrollViewer>
</Border>
</ControlTemplate>
</ItemsControl.Template>
</ItemsControl>
但更重要的是专注于您的习惯 RulesListItemControl
。为每个项目加载此控件。复杂的控件引入了复杂的初始化。你应该尽量减少这个控件的可视化树。
删除所有不需要的 Border
,将 Label
替换为 TextBlock
,重新访问触发器等。目标是减少每个项目容器的渲染时间。
为此,您需要覆盖用于组成 RulesListItemControl
.
ControlTemplate