wpf DataGrid 行虚拟化显示问题

声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow 原文地址: http://stackoverflow.com/questions/9949358/
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

提示:将鼠标放在中文语句上可以显示对应的英文。显示中英文
时间:2020-09-08 23:20:54  来源:igfitidea点击:

DataGrid row virtualization display issue

wpfdatagridrowvirtualization

提问by WPFNewbie

We currently have a DataGridthat is bound to a DataTable. It also has a template column with a CheckBoxin it that we add in programatically. This purpose of this column is track multiple selections in the DataGrid.

我们目前有一个DataGrid绑定到 a 的DataTable。它还有一个模板列,CheckBox其中包含我们以编程方式添加的。此列的目的是跟踪DataGrid.

A factory is used to create the CheckBoxes for each row.

工厂用于CheckBox为每一行创建es。

There are quite a few records, so row virtualization is set to true so that the performance is acceptable. However, we're seeing a strange issue where if we check some CheckBoxes on the first 10 rows and then scroll down about 50 rows (the grid has about 10 rows visible at any one time), there are a bunch of other CheckBoxes that appear to be checked at random.

记录比较多,所以行虚拟化设置为true,性能可以接受。然而,我们看到了一个奇怪的问题,如果我们CheckBox在前 10 行检查一些es 然后向下滚动大约 50 行(网格有大约 10 行在任何时间可见),还有一堆其他CheckBoxes 出现要随机检查。

If we disable row virtualization this problem does not exist (but the performance is terrible). Is there any way around this? Anyone know what we may be doing wrong?

如果我们禁用行虚拟化,则此问题不存在(但性能很差)。有没有办法解决?有谁知道我们可能做错了什么?

回答by paparazzo

If you are looking for speed ListView Gridview is much much faster (and has less features).

如果您正在寻找速度 ListView Gridview 快得多(并且功能较少)。

Try disable container recycling.

尝试禁用容器回收。

             <tk:DataGrid x:Name="dataGrid" 
             ItemsSource="{Binding Path=Bookings}" 
             AutoGenerateColumns="False" 
             EnableRowVirtualization="True" 
             EnableColumnVirtualization="True"
             VirtualizingStackPanel.VirtualizationMode="Standard"
             VirtualizingStackPanel.IsVirtualizing="True">

回答by denis morozov

If you turned on Virtualization because the datagrid loading time is terrible, then, here's the solution:

如果您因为数据网格加载时间很长而打开虚拟化,那么,这是解决方案:

  • turn off virtualization
  • let's assume/call your datagrid's items source binding as "Rows",

    public IEnumerable<Row> Rows {get; set;}
    
  • Add a property to your "Row" class IsVisible and toggle it when you want to load the data (not when UI thread decides to resolve the binding and load each control)

  • 关闭虚拟化
  • 让我们假设/调用数据网格的项目源绑定为“行”,

    public IEnumerable<Row> Rows {get; set;}
    
  • 向“Row”类 IsVisible 添加一个属性,并在您想要加载数据时切换它(而不是在 UI 线程决定解析绑定并加载每个控件时)

the reason this works, is because when you load the grid, it checks the binding, all row are invisible, so the UI thread doesn't have to spin through all your rows*columns to create them, it can go to the next thing it needs to do. You on the other hand, can detect when is convenient time to turn those rows into visible, when the View.Visibility is visible, when the ViewModel loaded the data from somewhere, etc. So you are in total control. Below I am iterating through my item source (rows) using a task (in a background thread), yet setting Visibility on the UI thread.

这样做的原因是因为当您加载网格时,它会检查绑定,所有行都是不可见的,因此 UI 线程不必遍历所有行 * 列来创建它们,它可以转到下一件事情它需要做。另一方面,您可以检测何时方便将这些行变为可见、何时 View.Visibility 可见、何时 ViewModel 从某处加载数据等。因此您可以完全控制。下面我使用任务(在后台线程中)遍历我的项目源(行),但在 UI 线程上设置可见性。

   private _isVisible = false;

    /// <summary>
    /// is false by default, for performance when loading first time. 
    /// </summary>
    public bool IsVisible
    {
        get { return _isVisible; }
        set
        {
            if (_isVisible == value)
                return;
            _isVisible = value;
            RaisePropertyChanged(() => IsVisible);
        }
    }
  • In the View, when datagrid is loading don't allow it to hog the UI thread by putting the iteration of rows in the background thread, then set the Visibility to true. Even though you're on the background thread, IsVisible property changed will trigger you to update.

    private void OnGridLoaded(object sender, RoutedEventArgs e)
    {
       //sample bool checks, you might not need them...
       if (firstTimeLoad && !_isDataGridLoaded)
       {
           Task.Factory
               .StartNew(() =>
                {
                    /*first time loading performance tweak*/
                     foreach (var row in _viewModel.Rows)
                        ExeOnUi(() => { row.IsVisible = true; });
    
                     _firstTimeLoad = false;
                 })
       }
    
  • forgot to add ExeOnUi code (you might check access using something else like whateverControl.Dispatcher.CheckAccess, I just use Microsoft.Practices.ServiceLocator):

    static void ExeOnUi (Action action)
    {
        var srv= ServiceLocator.Current.GetInstance<IDispatchService> ();
        if (srv.CheckAccess ())
            action.Invoke ();
        else
            srv.Invoke (action);
    }
    
  • 在视图中,当加载数据网格时,不允许它通过将行的迭代放在后台线程中来占用 UI 线程,然后将可见性设置为 true。即使您在后台线程上,更改的 IsVisible 属性也会触发您进行更新。

    private void OnGridLoaded(object sender, RoutedEventArgs e)
    {
       //sample bool checks, you might not need them...
       if (firstTimeLoad && !_isDataGridLoaded)
       {
           Task.Factory
               .StartNew(() =>
                {
                    /*first time loading performance tweak*/
                     foreach (var row in _viewModel.Rows)
                        ExeOnUi(() => { row.IsVisible = true; });
    
                     _firstTimeLoad = false;
                 })
       }
    
  • 忘记添加 ExeOnUi 代码(您可能会使用诸如whateverControl.Dispatcher.CheckAccess之类的其他东西来检查访问权限,我只使用Microsoft.Practices.ServiceLocator):

    static void ExeOnUi (Action action)
    {
        var srv= ServiceLocator.Current.GetInstance<IDispatchService> ();
        if (srv.CheckAccess ())
            action.Invoke ();
        else
            srv.Invoke (action);
    }
    

回答by igorushi

I just run into similar problem and its resolved after adding UpdateSourceTrigger=PropertyChangedto the binding.

我刚刚遇到了类似的问题,并在添加UpdateSourceTrigger=PropertyChanged到绑定后解决了。

<DataGridTemplateColumn Header="Visible">
    <DataGridTemplateColumn.CellTemplate>
        <DataTemplate>
            <CheckBox IsChecked="{Binding IsShown,Mode=TwoWay,UpdateSourceTrigger=PropertyChanged}"/>
        </DataTemplate>
    </DataGridTemplateColumn.CellTemplate>
</DataGridTemplateColumn>