2011-10-21 10 views
5

Mam problemy z działaniem wirtualizacji z GF DataGrid.Problemy wirtualizacji Wpf DataGrid podczas wiązania z właściwością DataGridRow.IsSelected

Używam MVVM i wiążę wszystkie moje viewmodels wiersza z właściwością IsSelected. Muszę odejmować zaznaczenie wszystkich wierszy od czasu do czasu, więc robię to, aktualizując moje podstawowe modele widoku wiersza do IsSelected = false.

To wydaje się działać poprawnie na początku i odznacza wszystko. Zweryfikowałem, że wszystkie modele widoku wiersza mają wartość false, używając komunikatów debugowania i ustawiania warunkowych punktów przerwania.

Występuje jednak problem podczas przewijania siatki. Widzę, że niektóre wiersze są faktycznie wybrane. Mam warunkowy punkt przerwania dla właściwości IsSelected, gdy jest ustawiona na wartość true, a tak naprawdę nie zostanie ona przerwana, dopóki nie przejdę przez siatkę. Tak więc, jak przewijam, coś od czasu do czasu aktualizuje niektóre modele widoku wiersza z powrotem do IsSelected = true?

Nie mogę do końca zrozumieć, co się dzieje, ani tego o tym stanie ... czy ktoś może mi wyjaśnić, co się właściwie dzieje? Zakładam, że ma to coś wspólnego z wirtualizacją. Pomyślałem, że być może wirtualizacja była recyklingiem DataGridRow, a z kolei aktualizowanie moich viewmodeli z powrotem do IsSelected = true. Dzieje się tak jednak w obu trybach wirtualizacji (Recykling i Standard). Myślałbym, że DataGridRows będzie odtwarzany za każdym razem?

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: jestem delegowania kod moim zdaniem i ViewModels. Jestem w stanie odtworzyć go konsekwentnie, wywołując wywołanie "Wybierz wszystkie wiersze", aby zaktualizować wszystkie modele widoku wiersza do IsSelected = true. Następnie przewijam w górę iw dół, aby zobaczyć wszystkie wybrane wiersze. Następnie wywołuję opcję "Usuń zaznaczenie wszystkich wierszy", a wszystkie modele widoków wierszy powinny być odznaczone. Podczas przewijania w dół widzę, że modele podglądu wierszy są następnie aktualizowane do IsSelected = true (przez komunikaty debugowania). Jeśli się rozbiję i zobaczę, co to wywołuje, otrzymam powyższy ślad stosu.

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); 
      } 
     } 
    } 
} 

Zobacz

<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> 

View Code-Behind

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

Dziwne! Próbowałem tego, co robisz, ale bez powodzenia! kiedy ustawiam właściwość 'IsSelected' na poziomie mojego wiersza na wartość false (która jest dwukierunkowo związana z właściwością IsSelected w DataGridRow), nawet przewijanie nie wybiera żadnych wierszy z powrotem. Wszystkie pozostają niezaznaczone. : -/ –

+0

Czy możesz napisać swój kod? – Rachel

+0

Postaram się rozebrać mój kod i opublikować go. Problem prawdopodobnie dzieje się w około 20% przypadków. W 80% przypadków wszystko zostanie w porządku. Czy próbowałeś już z ponad 1200 rzędami i mając wszystkie zaznaczone? Właśnie to robię. – user832747

Odpowiedz

0

To może być problem gwintowania. Widzę, że nabywasz blokadę tego samego obiektu, który aktualizujesz wewnątrz bloku blokującego, tj. Obiekt zamówienia

Powinieneś zawsze blokować za pomocą osobnej nowej instancji obiektu, specjalnie zgłoszonej do użycia dla słowa kluczowego blokada np.

private readonly object _lockObject = new Object(); 

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

Spróbuj zmienić powyższy kod w blokadę i sprawdź, czy problem został rozwiązany.