【问题标题】:DataGrid sorting very slow when ItemsControl also binding to the same ItemsSource当 ItemsControl 也绑定到同一个 ItemsSource 时,DataGrid 排序非常慢
【发布时间】:2012-03-23 23:33:18
【问题描述】:

我有一个绑定到可观察对象列表的 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 排序的性能产生影响?

【问题讨论】:

    标签: c# wpf datagrid


    【解决方案1】:

    虚拟化是关键!

    默认情况下,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>
    

    【讨论】:

      【解决方案2】:

      我怀疑这是因为 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>
      

      【讨论】:

      • 这是否意味着当您更改 ItemsPanelTemplate 时 ListBox 会失去其虚拟化功能?
      • @Scroog1 我不这么认为。虚拟化是ControlTemplate 的一部分,因此更改ItemsPanelTemplate 应该不会影响它。
      • 但是,如果我将上面的示例更改为 ListBox,其 ItemsPanelTemplate 为 ,则排序性能再次下降。
      • @Scroog1 虚拟化尽可能重用现有的控件模板,因此它实际上不会为不可见的项目绘制控件。通过切换到Canvas,您的所有项目都是可见的,因此尽管启用了虚拟化,所有项目都会被绘制。
      • 当我对 DataGrid 进行排序时,是否有停止画布重绘的方法?在 ListBox 上将绑定模式设置为 OneTime 似乎不起作用。
      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2013-01-14
      • 2017-05-11
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2018-07-16
      相关资源
      最近更新 更多