2011-10-21 57 views
5

WPF DataGridで仮想化がどのように機能するかに問題があります。DataGridRow.IsSelectedプロパティへのバインド時にWpf DataGrid仮想化の問題が発生する

MVVMを使用していて、すべての行ビューモデルをIsSelectedプロパティにバインドしています。私は時々すべての行を選択解除する必要があるので、私は基本的な行のビューモデルをIsSelected = falseに更新することでこれを行います。

これは最初は正常に動作していて、すべてを選択解除しています。すべての行ビューモデルがデバッグメッセージを使用してfalseに設定され、条件付きブレークポイントが設定されていることを確認しました。

ただし、グリッドをスクロールするときに問題が発生します。私はいくつかの行が実際に選択されて参照してください。 Trueに設定されている場合、IsSelectedプロパティに条件付きブレークポイントがあり、グリッドをスクロールするまで実際にブレークしません。だから、私はscrolldownとして、何かが時々IsSelected = trueに行ビューモデルのいくつかを更新しますか?

実際に何が起こっているのか、誰かに説明することができますか?私はそれが仮想化と関係があると考えています。私は、仮想化がDataGridRowをリサイクルしていて、それから私のビューモデルをIsSelected = trueに更新していると考えました。ただし、これは両方の仮想化モード(リサイクルと標準)で発生します。私はDataGridRowsが毎回再作成されると思っていたでしょうか?

MyApp.exe!MyApp.MyViewModel.IsSelected.set(bool value = true) Line 67 C# 
[Native to Managed Transition] 
PresentationFramework.dll!MS.Internal.Data.PropertyPathWorker.SetValue(object item, object value) + 0x106 bytes 
PresentationFramework.dll!MS.Internal.Data.ClrBindingWorker.UpdateValue(object value) + 0xa3 bytes 
PresentationFramework.dll!System.Windows.Data.BindingExpression.UpdateSource(object value = true) + 0x99 bytes 
PresentationFramework.dll!System.Windows.Data.BindingExpressionBase.UpdateValue() + 0x66 bytes 
PresentationFramework.dll!System.Windows.Data.BindingExpression.Update(bool synchronous) + 0x4f bytes 
PresentationFramework.dll!System.Windows.Data.BindingExpressionBase.Dirty() + 0x30 bytes  
PresentationFramework.dll!System.Windows.Data.BindingExpression.SetValue(System.Windows.DependencyObject d, System.Windows.DependencyProperty dp, object value) + 0x27 bytes  
WindowsBase.dll!System.Windows.DependencyObject.SetValueCommon(System.Windows.DependencyProperty dp = {System.Windows.DependencyProperty}, object value = true, System.Windows.PropertyMetadata metadata = {System.Windows.FrameworkPropertyMetadata}, bool coerceWithDeferredReference = false, bool coerceWithCurrentValue = true, System.Windows.OperationType operationType = Unknown, bool isInternal) + 0x3c7 bytes 
WindowsBase.dll!System.Windows.DependencyObject.SetCurrentValueInternal(System.Windows.DependencyProperty dp, object value) + 0x35 bytes  
PresentationFramework.dll!System.Windows.Controls.Primitives.Selector.ItemSetIsSelected(object item, bool value) + 0xb2 bytes 
PresentationFramework.dll!System.Windows.Controls.Primitives.Selector.OnGeneratorStatusChanged(object sender, System.EventArgs e) + 0xf8 bytes 
PresentationFramework.dll!System.Windows.Controls.ItemContainerGenerator.SetStatus(System.Windows.Controls.Primitives.GeneratorStatus value) + 0x81 bytes 
PresentationFramework.dll!System.Windows.Controls.ItemContainerGenerator.Generator.System.IDisposable.Dispose() + 0x4a bytes  
PresentationFramework.dll!System.Windows.Controls.VirtualizingStackPanel.MeasureOverride(System.Windows.Size constraint) + 0x976 bytes 
PresentationFramework.dll!System.Windows.Controls.Primitives.DataGridRowsPresenter.MeasureOverride(System.Windows.Size constraint) + 0x28 bytes 
PresentationFramework.dll!System.Windows.FrameworkElement.MeasureCore(System.Windows.Size availableSize) + 0x1d6 bytes 
PresentationCore.dll!System.Windows.UIElement.Measure(System.Windows.Size availableSize) + 0x207 bytes 
PresentationCore.dll!System.Windows.ContextLayoutManager.UpdateLayout() + 0x1d9 bytes 
PresentationCore.dll!System.Windows.ContextLayoutManager.UpdateLayoutCallback(object arg) + 0x19 bytes 
PresentationCore.dll!System.Windows.Media.MediaContext.InvokeOnRenderCallback.DoWork() + 0x10 bytes 
PresentationCore.dll!System.Windows.Media.MediaContext.FireInvokeOnRenderCallbacks() + 0x6f bytes 
PresentationCore.dll!System.Windows.Media.MediaContext.RenderMessageHandlerCore(object resizedCompositionTarget = null) + 0x8a bytes  
PresentationCore.dll!System.Windows.Media.MediaContext.RenderMessageHandler(object resizedCompositionTarget) + 0x2c bytes 
WindowsBase.dll!System.Windows.Threading.ExceptionWrapper.InternalRealCall(System.Delegate callback, object args, int numArgs) + 0x53 bytes 
WindowsBase.dll!MS.Internal.Threading.ExceptionFilterHelper.TryCatchWhen(object source = {System.Windows.Threading.Dispatcher}, System.Delegate method, object args, int numArgs, System.Delegate catchHandler = null) + 0x42 bytes 
WindowsBase.dll!System.Windows.Threading.DispatcherOperation.InvokeImpl() + 0x8d bytes 
WindowsBase.dll!System.Windows.Threading.DispatcherOperation.InvokeInSecurityContext(object state) + 0x38 bytes 
mscorlib.dll!System.Threading.ExecutionContext.runTryCode(object userData) + 0x51 bytes 

UPDATE:私は私のビューとのviewmodelsためのコードを掲示してい 。すべての行ビューモデルをIsSelected = trueに更新するために、「すべての行を選択」コールを呼び出すことによって、一貫して再現できるようになりました。この後、選択したすべての行を表示するために上下にスクロールします。次に、「Unselect All rows」を呼び出すと、すべての行ビュー・モデルを選択解除する必要があります。下にスクロールすると、RowviewModelsがIsSelected = true(デバッグメッセージ経由)に更新されています。私が壊れてこれを呼んでいるものを見ると、私は上記のスタックトレースを取得します。

のviewmodels:

using System; 
using System.Collections; 
using System.Collections.Generic; 
using System.Collections.ObjectModel; 
using System.ComponentModel; 
using System.Linq; 
using System.Windows.Input; 

namespace WpfDataGridVirtualization 
{ 
    public interface IOrderViewModel : INotifyPropertyChanged 
    { 
     Guid Key { get; } 
     decimal Level { get; } 
     bool IsSelected { get; set; } 
    } 

    public class OrderViewModel : NotifyPropertyChanged, IOrderViewModel 
    { 
     private string _market; 
     private int _quantity; 
     private decimal _level; 
     private bool _isSelected; 

     public OrderViewModel(OrderData orderData) 
     { 
      Key = Guid.NewGuid(); 
      Market = orderData.Market; 
      IsSelected = false; 
      Quantity = orderData.Quantity; 
      Level = orderData.Level; 
     } 

     public Guid Key { get; private set; } 

     public bool IsSelected 
     { 
      get { return _isSelected; } 
      set 
      { 
       if (value) 
        System.Diagnostics.Debug.WriteLine("setting isselected to true"); 

       _isSelected = value; 
       RaisePropertyChanged("IsSelected"); 
      } 
     } 

     public string Market 
     { 
      get { return _market; } 
      private set 
      { 
       _market = value; 
       RaisePropertyChanged("Market"); 
      } 
     } 

     public int Quantity 
     { 
      get { return _quantity; } 
      private set 
      { 
       _quantity = value; 
       RaisePropertyChanged("Quantity"); 
      } 
     } 

     public decimal Level 
     { 
      get { return _level; } 
      private set 
      { 
       _level = value; 
       RaisePropertyChanged("Level"); 
      } 
     } 
    } 

    public class OrderData 
    { 
     public OrderData(string market, int qty, decimal level) 
     { 
      Key = Guid.NewGuid(); 
      Market = market; 
      Quantity = qty; 
      Level = level; 
     } 

     public Guid Key { get; set; } 
     public string Market { get; set; } 
     public int Quantity { get; set; } 
     public decimal Level { get; set; } 
    } 

    public class OrderCollectionViewModel : NotifyPropertyChanged, IDisposable 
    { 
     private readonly ObservableCollection<IOrderViewModel> _orders = new ObservableCollection<IOrderViewModel>(); 

     public ObservableCollection<IOrderViewModel> Orders { get { return _orders; } } 

     public void AddOrders(IEnumerable<OrderData> orders) 
     { 
      orders.ToList().ForEach(o => AddOrder(o)); 
     } 

     private void AddOrder(OrderData order) 
     { 
      var viewModel = _orders.Where(o => o.Key == order.Key).SingleOrDefault(); 
      if (viewModel == null) 
      { 
       viewModel = new OrderViewModel(order); 
       lock (_orders) 
       { 
        _orders.Add(viewModel); 
       } 
      } 
      viewModel.IsSelected = false; 
     } 

     public void ApplyFiltering() 
     { 
      UnSelectAll(); 
     } 

     public void SelectAll(bool select) 
     { 
      UpdateAllOrders(row => 
      { 
       row.IsSelected = select; 
      }); 
     } 

     public void SelectSingleRow(Guid key) 
     { 
      UpdateAllOrders(row => 
      { 
       row.IsSelected = true; 
      }); 
     } 

     public IEnumerable<IOrderViewModel> GetSelected() 
     { 
      lock (_orders) 
      { 
       return _orders.Where(s => s.IsSelected).ToList(); 
      } 
     } 

     public void UnSelectAll() 
     { 
      var count = 0; 
      UpdateAllOrders(row => 
      { 
       row.IsSelected = false; 
       count++; 
      }); 
      System.Diagnostics.Debug.WriteLine("{0} orders were unselected", count); 
     } 

     private void UpdateAllOrders(Action<IOrderViewModel> action) 
     { 
      lock (_orders) 
      { 
       _orders.ToList().ForEach(action); 
      } 
     } 

     public void Dispose() 
     { 
      _orders.Clear(); 
     } 

     public class OrderSorter : IComparer 
     { 
      public int Compare(object x, object y) 
      { 
       var orderX = x as OrderViewModel; 
       var orderY = y as OrderViewModel; 

       var result = orderX.Market.CompareTo(orderY.Market); 
       if (result != 0) 
        return result; 

       return orderX.Level.CompareTo(orderY.Level); 
      } 
     } 
    } 

    public class OrderGridViewModel : NotifyPropertyChanged, IDisposable 
    { 
     private ICommand _selectAllOrdersCommand; 
     private ICommand _unselectAllOrdersCommand; 

     public OrderGridViewModel() 
     { 
      OrderCollection = new OrderCollectionViewModel(); 
      InitializeOrders(); 
     } 

     public ObservableCollection<IOrderViewModel> Orders { get { return OrderCollection.Orders; } } 
     public OrderCollectionViewModel OrderCollection { get; private set; } 

     public ICommand SelectAllOrdersCommand 
     { 
      get { return _selectAllOrdersCommand ?? (_selectAllOrdersCommand = new RelayCommand(p => OrderCollection.SelectAll(true))); } 
     } 

     public ICommand UnSelectAllOrdersCommand 
     { 
      get { return _unselectAllOrdersCommand ?? (_unselectAllOrdersCommand = new RelayCommand(p => OrderCollection.ApplyFiltering())); } 
     } 

     private void InitializeOrders() 
     { 
      OrderCollection.AddOrders(OrderDataHelper.GetOrderData()); 
     } 

     public void Dispose() 
     { 
      OrderCollection.Dispose(); 
     } 
    } 

    public static class OrderDataHelper 
    { 
     public static IEnumerable<OrderData> GetOrderData() 
     { 
      Dictionary<int, string> marketMap = new Dictionary<int, string>() 
      { 
       {0, "AUD"}, 
       {1, "EUR"}, 
       {2, "USD"}, 
       {3, "CAD"}, 
       {4, "CHF"}, 
       {5, "BOBL"}, 
       {6, "EMiniNasdaq"}, 
       {7, "Corn"}, 
       {8, "Oil"}, 
       {9, "Starch"}, 
      }; 

      var multiplyFactor = 1; 

      for (int j = 0; j < 10; j++) 
      { 
       var market = marketMap[j]; 
       for (int i = 0; i < 50 * multiplyFactor; i++) 
        yield return new OrderData(market, 1000000, 100); 
       for (int i = 0; i < 50 * multiplyFactor; i++) 
        yield return new OrderData(market, 1000000, 100); 
       for (int i = 0; i < 5 * multiplyFactor; i++) 
        yield return new OrderData(market, 1000000, 100); 
       for (int i = 0; i < 2 * multiplyFactor; i++) 
        yield return new OrderData(market, 1000000, 100); 
       for (int i = 0; i < 5 * multiplyFactor; i++) 
        yield return new OrderData(market, 1000000, 100); 
       for (int i = 0; i < 5 * multiplyFactor; i++) 
        yield return new OrderData(market, 1000000, 100); 
       for (int i = 0; i < 5 * multiplyFactor; i++) 
        yield return new OrderData(market, 1000000, 100); 
      } 
     } 
    } 
} 

ビュー

<Window x:Class="WpfDataGridVirtualization.MainWindow" 
     xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation" 
     xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" 
     Title="MainWindow" Height="600" Width="800" Closing="WindowClosing"> 
    <DockPanel> 
     <DockPanel x:Name="dockHeader" DockPanel.Dock="Top" Background="Transparent">    
      <Button Content="Select All Orders" Margin="2" Command="{Binding SelectAllOrdersCommand}" /> 
      <Button Content="UnSelect All Orders" Margin="2" Command="{Binding UnSelectAllOrdersCommand}" /> 
      <DockPanel/> 
     </DockPanel> 
     <DockPanel DockPanel.Dock="Top"> 
      <DataGrid x:Name="dgOrders" Margin="5" 
         ItemsSource="{Binding OrderCollection.Orders}" 
         IsReadOnly="True" 
         AutoGenerateColumns="False" 
         SelectionUnit="FullRow" 
         VirtualizingStackPanel.IsVirtualizing="True" 
         VirtualizingStackPanel.VirtualizationMode="Standard" 
         > 
       <DataGrid.RowStyle> 
        <Style TargetType="{x:Type DataGridRow}"> 
         <Setter Property="IsSelected" Value="{Binding IsSelected, Mode=TwoWay, UpdateSourceTrigger=PropertyChanged}" /> 
        </Style> 
       </DataGrid.RowStyle> 
       <DataGrid.Columns> 
        <DataGridTextColumn Header="IsSelected" Binding="{Binding IsSelected}" /> 
        <DataGridTextColumn Header="Market" Binding="{Binding Market}" /> 
        <DataGridTextColumn Header="Quantity" Binding="{Binding Quantity}" /> 
        <DataGridTextColumn Header="Level" Binding="{Binding Level}" /> 
       </DataGrid.Columns> 
      </DataGrid> 
     </DockPanel> 
    </DockPanel> 
</Window> 

コードの表示ビハインド

using System.Windows; 

namespace WpfDataGridVirtualization 
{ 
    /// <summary> 
    /// Interaction logic for MainWindow.xaml 
    /// </summary> 
    public partial class MainWindow : Window 
    { 
     private readonly OrderGridViewModel _viewModel; 

     public MainWindow() 
     { 
      InitializeComponent(); 
      _viewModel = new OrderGridViewModel(); 
      DataContext = _viewModel; 
     } 

     private OrderGridViewModel GetViewModel() 
     { 
      return DataContext as OrderGridViewModel; 
     } 

     private void WindowClosing(object sender, System.ComponentModel.CancelEventArgs e) 
     { 
      GetViewModel().Dispose(); 
     } 
    } 
} 
+0

奇妙な!私はあなたがしていることを試みたが、運がなかった!行レベルの項目の 'IsSelected'プロパティをfalseに設定すると(これはDataGridRowのIsSelectedプロパティに2つの方法でバインドされています)、スクロールしてもすべての行が選択されません。彼らはすべて未選択のままです。 : -/ –

+0

あなたのコードを投稿できますか? – Rachel

+0

私は自分のコードを削除して投稿しようとします。この問題はおそらく約20%の時間で起こります。 80%の時間で、それはすべてうまく選択解除されます。あなたは1200以上の行を試してみて、それらをすべて選択しましたか?これは私がやっていることです。 – user832747

答えて

0

これは、スレッドの問題である可能性があります。ロックブロック、つまりオーダオブジェクト内で更新しているオブジェクトと同じオブジェクトに対するロックを取得しています。

ロックキーワードに使用するために特別に宣言された別個の新しいオブジェクトインスタンスで常にロックする必要があります。

private readonly object _lockObject = new Object(); 

lock(_lockObject) 
{ 
    orders.Add(...); 
} 

上記のコードをロックブロックに変更し、問題が解決されるかどうかを確認してください。

関連する問題