当ItemsControl也绑定到相同的ItemsSource时,DataGrid排序非常慢

时间:2012-03-06 13:22:46

标签: c# wpf datagrid

我有一个DataGrid绑定到一个可观察的对象列表。如果我还有一个绑定到该列表的ItemsControl,则排序性能(通过单击DataGrid标头)非常糟糕(对于下面的示例,大约几秒钟)。当ItemsControl未绑定到同一列表时,则排序是即时的。

以下是一些展示此行为的示例代码

namespace LargeDataGridViewTest
{
    public partial class MainWindow
    {
        public MainWindow()
        {
            InitializeComponent();
            DataContext = new MainPresenter();
        }
    }

    public class MainPresenter : INotifyPropertyChanged
    {
        private readonly ObservableCollection<Item> _items = new ObservableCollection<Item>();
        public IEnumerable<Item> Items { get { return _items; } }

        public MainPresenter()
        {
            for (var i = 0; i < 10000; i++)
                _items.Add(new Item());
        }
    }

    public class Item : INotifyPropertyChanged
    {
        public int Random { get; private set; }
        private static readonly Random Rand = new Random();

        public Item()
        {
            Random = Rand.Next(0, 1000000);
        }
    }
}

和相应的XAML

<Window.Resources>
    <DataTemplate DataType="{x:Type LargeDataGridViewTest:MainPresenter}">
        <DockPanel>
            <DataGrid ItemsSource="{Binding Items}"/>
            <!--ListBox ItemsSource="{Binding Items}"/-->
            <ItemsControl ItemsSource="{Binding Items}"/>
        </DockPanel>
    </DataTemplate>
</Window.Resources>

<ContentPresenter Content="{Binding}"/>

如果我使用ListBox代替ItemsControl,那么排序性能很好。如果我使用ListBox但通过更改ItemsPanelTemplate来访问它的底层ItemsControl,则性能再次变坏。

如果我获取列表的浅表副本(引用相同的项目)并将ItemsControl绑定到该列表,则性能再次正常。

通过EQATEC探查器运行速度较慢的ItemsControl绑定和快速ListBox绑定,除顶级应用时间外,没有显示性能差异。

有谁知道这里发生了什么?

修改

部分答案似乎是ItemsControls没有虚拟化,因此必须绘制所有项目而不仅仅是可见项目。在这种情况下,为什么在DataGrid排序时重新绘制所有ItemsControl项(即使ItemsControl绑定模式是OneTime)?我怎样才能阻止这对DataGrid排序的性能产生影响?

2 个答案:

答案 0 :(得分:0)

我怀疑这是因为默认情况下DataGridListBox都会对其项进行虚拟化,而ItemsControl则不会。

这意味着DataGridListBox仅创建在屏幕上可见的UI对象,而ItemsControl将创建所有10,000个UI对象。

要修复效果,virtualize your ItemsControl。这是所需的基本部分,但请查看链接的问题以获取更多详细信息。

<ItemsControl
    VirtualizingStackPanel.IsVirtualizing="True" <!-- needed -->
    ScrollViewer.CanContentScroll="True" <!-- needed -->
    ... >
    <ItemsControl.ItemsPanel>
        <ItemsPanelTemplate>
            <VirtualizingStackPanel /> <!-- needed -->
        </ItemsPanelTemplate>
    </ItemsControl.ItemsPanel>
    <ItemsControl.Template>
        <ControlTemplate>
            <ScrollViewer>  <!-- needed -->
                <ItemsPresenter  />
            </ScrollViewer>
        </ControlTemplate>
    </ItemsControl.Template>
</ItemsControl>

答案 1 :(得分:0)

虚拟化是关键!

默认情况下,ItemsControls不是虚拟化的,当数据网格对Items集合进行排序时,默认集合视图将被排序,同样的排序也适用于另一个ItemsControl。但是ItemsControl没有实现虚拟化,因此它不仅对项目进行排序,而且还在项目容器上呈现它们。 ListBox表现更好,因为默认情况下在其中实施了虚拟化。

为此,快速修复将模拟ListBox看起来像ItemsControl。你可以通过摆脱列表框的选择颜色来做到这一点。

   <ListBox ItemsSource="{StaticResource MyData}" DisplayMemberPath="ID">
       <ListBox.ItemContainerStyle>
            <Style TargetType="{x:Type ListBoxItem}">
                <Style.Resources>
                    <SolidColorBrush x:Key="{x:Static SystemColors.HighlightBrushKey}"
                                     Color="Transparent"/>
                </Style.Resources>
                <Style.Triggers>
                    <Trigger Property="IsSelected" Value="True">
                        <Setter Property="Foreground" Value="Black"/>
                    </Trigger>
                </Style.Triggers>
            </Style>
        </ListBox.ItemContainerStyle>
    </ListBox>