我有以下列表框
<ListBox SelectionMode="Extended" ItemsSource="{Binding Containers}" AllowDrop="True" Margin="0,0,5,0">
<ListBox.ItemContainerStyle>
<Style TargetType="ListBoxItem">
<Setter Property="IsSelected" Value="{Binding Content.IsSelected, Mode=TwoWay, RelativeSource={RelativeSource Self}}"/>
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="ListBoxItem">
<ContentPresenter/>
</ControlTemplate>
</Setter.Value>
</Setter>
</Style>
</ListBox.ItemContainerStyle>
<ListBox.ItemTemplate>
<DataTemplate DataType="{x:Type vm:ContainerViewModel}">
<local:ContainerUserControl DataContext="{Binding}" />
</DataTemplate>
</ListBox.ItemTemplate>
<ListBox.ItemsPanel>
<ItemsPanelTemplate>
<VirtualizingStackPanel IsVirtualizing="True" VirtualizationMode="Recycling" />
</ItemsPanelTemplate>
</ListBox.ItemsPanel>
ContainerUserControl是具有扩展器(具有标头和内容)的用户控件。
绑定的项源为:
private ObservableCollection<ContainerViewModel> _containers;
public ObservableCollection<ContainerViewModel> Containers
{
get => _containers;
set
{
_containers = value;
OnPropertyChanged();
}
}
问题是,当我为Containers分配新集合时,每个元素的构造函数都被调用:
public partial class ContainerUserControl : UserControl
{
public ContainerUserControl()
{
InitializeComponent();
Debug.Print("in ContainerUserControl");
}
}
如果我有成千上万的项目,它可能需要很长的时间。现在让我们假设我有10 k个项目,我想使用以下代码对这个集合进行排序:
Containers = new ObservableCollection<ContainerViewModel>(Containers.OrderByDescending(i => i.Name));
我将看到usercontrol构造函数被调用了10 k次。在阅读了一些帖子后,我决定使用“Move”方法实现就地排序,但不幸的是,即使我这样做了:
_containers.Move(0, 1);
我看到我遍历了userControl c 'tor。如果我有成千上万的移动操作,就像使用orderby方法并分配排序列表一样。此外,我试图创建一个新的排序集合,并在itemsSource之间切换,但没有帮助,仍然输入了c' tor 10 k次。
public ObservableCollection<ContainerViewModel> SortedContainers { get; set; } // already sorted
public ListBox ContainerListBox { get; set; } // the listbox from xaml
ContainerListBox.ItemsSource = null;
ContainerListBox.ItemsSource = SortedContainers;
无论我怎么尝试,我都无法避免c 'tor被调用数千次,并产生可怕的性能问题。我如何避免c' tor调用?为什么这个c 'tor无论如何都被调用?
编辑:我对500个对象进行了一些时间测量:我看到每个c 'tor通常需要1-2毫秒,但有时超过20毫秒。此外,构建500个对象集合的总时间大于所有c' tor时间的总和。下面是我测量的开始和结束:\n开始:x1c 0d1x结束:
你可以看到所有c 'tor时间的总和是1169 ms,但总时间是18:52:25.835 - 18:52:29.153,也就是3.318秒。(通常500个对象需要4秒,所以大约10 k甚至更多)为什么会这样?
任何帮助将不胜感激:)
1条答案
按热度按时间pzfprimi1#
ListBox
使用UI虚拟化。它不会加载所有项目。只加载虚拟化视口中的项目。因为默认情况下启用了虚拟化,所以ListBox.ItemPanel
的覆盖是多余的。这同样适用于
DataTemplate
内部的DataCOntext绑定:DataTemplate
(或者一般来说父元素)的DataContext
是隐式继承的。不需要显式设置它。在您的例子中,所有项目都被加载,因为您的
ListBox
没有高度限制。它会自动拉伸以使所有项目适合。要启用UI虚拟化,请为ListBox
分配Height
,以便ScrollViewer
可以工作。ScrollViewer
对于UI虚拟化至关重要。高度可以显式设置,例如通过设置
ListBox.Height
属性,或者隐式设置,例如通过将ListBox
添加到Grid
,其中行高设置为除Auto
之外的任何值。此外,您不应该替换源集合。这样做效率很低。这会严重影响性能。排序和分组是通过修改绑定到
ItemsControl
的CollectionView
来完成的。在WPF中,绑定引擎将自动使用默认的ColectionView
来显示项目。这使您可以修改显示的项,而无需修改原始基础集合。例如,对CollectionView
进行排序不会对基础集合进行排序。使用静态
CollectionViewSource.GetDefaultView
方法获得默认的CollectionView
: