数据网格行虚拟化显示问题

28
我们目前有一个绑定到DataTable的DataGrid。它还有一个模板列,其中包含我们在程序中添加的CheckBox。此列的目的是在DataGrid中跟踪多个选择。
工厂用于为每行创建CheckBox。
有相当多的记录,因此将行虚拟化设置为true以使性能可接受。但是,我们看到一个奇怪的问题,即如果我们选中前10行的一些CheckBox,然后向下滚动大约50行(网格每次最多显示10行),会出现一堆其他的CheckBox 似乎被随机选中了。
如果我们禁用行虚拟化,则不存在这个问题(但性能很差)。有没有解决办法?有人知道我们可能做错了什么吗?

当你说去除虚拟化后性能很差,是指在加载时很差还是滚动时很差?如果是在加载时很差,有一个解决方案可以让它不那么差,但如果是在滚动时很差,那就是另一个问题了... - denis morozov
仅在加载时发生。所有性能损失都在包含DataGrid的窗口的Show方法上。使用行虚拟化,在DataGrid中显示2,500条记录需要200毫秒。如果没有行虚拟化,则需要28秒。 - WPFNewbie
我为你添加了一些代码...前几天在工作中仅需要微调性能,现在运行得非常完美。 - denis morozov
3个回答

32

如果你追求速度,ListView和Gridview会更快(而且功能较少)。

尝试禁用容器回收。

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

5
如果您在VirtualisationMode之前写了IsVirtualising,就会收到异常(至少在.NET 4.5中是这样)... 您需要改变顺序。 - Boppity Bop
1
我遇到了类似的问题,只是当我滚动时行的顺序不断变化。我没有显式地进行虚拟化。不过这个答案还是帮了我很多的。 - Nikola Novak
2
VirtualizationMode="Standard" 赢了! - Ondřej

4
如果您因为数据表格的加载时间太慢而打开了虚拟化,那么这里有一个解决方案:
  • 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)

这个方法有效的原因是,当你加载网格时,它会检查绑定,所有的行都是不可见的,因此UI线程不必在所有的行*列中旋转以创建它们,它可以转到下一个需要执行的任务。而你则可以在方便的时间将这些行变为可见状态,例如当View.Visibility可见时,当ViewModel从某个地方加载数据时等等。因此,你完全掌控着整个过程。下面我使用一个任务(在后台线程中)遍历我的项目源(行),但在UI线程上设置了Visibility。
   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);
    }
    

0
我遇到了类似的问题,添加UpdateSourceTrigger=PropertyChanged到绑定后问题得到解决。
<DataGridTemplateColumn Header="Visible">
    <DataGridTemplateColumn.CellTemplate>
        <DataTemplate>
            <CheckBox IsChecked="{Binding IsShown,Mode=TwoWay,UpdateSourceTrigger=PropertyChanged}"/>
        </DataTemplate>
    </DataGridTemplateColumn.CellTemplate>
</DataGridTemplateColumn>

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接