web-dev-qa-db-ja.com

DataGrid行仮想化表示の問題

現在、DataGridにバインドされているDataTableがあります。また、プログラムで追加するCheckBoxを含むテンプレート列もあります。この列のこの目的は、DataGridでの複数の選択を追跡することです。

ファクトリを使用して、各行のCheckBoxesを作成します。

かなりの数のレコードがあるため、行の仮想化はtrueに設定されているため、パフォーマンスは許容範囲内です。ただし、最初の10行のCheckBoxesをいくつかチェックしてから約50行下にスクロールすると(グリッドには常に約10行が表示されます)、たくさんの奇妙な問題が発生しますランダムにチェックされているように見える他のCheckBoxes.

行の仮想化を無効にすると、この問題は発生しません(ただし、パフォーマンスはひどいです)。これを回避する方法はありますか?誰が私たちが間違っているのか知っていますか?

26
WPFNewbie

速度を求めている場合、ListView Gridviewははるかに高速です(機能が少ない)。

コンテナのリサイクルを無効にしてみてください。

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

データグリッドの読み込み時間がひどいために仮想化をオンにした場合、解決策は次のとおりです。

  • 仮想化をオフにする
  • データグリッドのアイテムソースバインディングを「行」と想定/呼び出します。

    public IEnumerable<Row> Rows {get; set;}
    
  • 「Row」クラスのIsVisibleにプロパティを追加し、データをロードするときに(UIスレッドがバインディングを解決して各コントロールをロードすることを決定したときではなく)プロパティを切り替えます。

これが機能する理由は、グリッドをロードするとバインディングがチェックされ、すべての行が非表示になるため、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);
        }
    }
  • ビューでは、datagridの読み込み時に、行の反復をバックグラウンドスレッドに入れてUIスレッドを独占させないようにし、Visibilityを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);
    }
    
4
denis morozov

バインディングにUpdateSourceTrigger=PropertyChangedを追加すると、同様の問題が発生し、解決しました。

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