2011-10-21 4 views
5

Ho problemi con il modo in cui la virtualizzazione funziona con WPF DataGrid.Problemi di virtualizzazione Wpf DataGrid durante l'associazione a DataGridRow.Is Proprietà selezionata

Sto utilizzando MVVM e collego tutti i miei modelli di riga di vista alla proprietà IsSelected. Ho bisogno di deselezionare tutte le righe occasionalmente, quindi lo faccio aggiornando il mio viewmodels riga sottostante a IsSelected = false.

Questo sembra funzionare correttamente all'inizio e deseleziona tutto. Ho verificato che tutti i modelmodelli di riga sono impostati su false utilizzando i messaggi di debug e impostando i breakpoint condizionali.

Tuttavia, c'è un problema quando sto scorrendo la griglia. Vedo che alcune delle righe sono effettivamente selezionate. Ho un punto di interruzione condizionale sulla proprietà IsSelected quando è impostato su true e in realtà non si interrompe finché non faccio scorrere la griglia. Quindi, mentre eseguo lo scrolldown, qualcosa aggiornerà di tanto in tanto alcuni dei modelli di riga con IsSelected = true?

Non riesco a capire cosa sta succedendo, o lo spazio per le chiamate ... può qualcuno spiegarmi cosa sta realmente accadendo? Presumo che abbia qualcosa a che fare con la virtualizzazione. Ho pensato che forse la virtualizzazione stava riciclando un DataGridRow e, a sua volta, stava aggiornando i miei modelli di visualizzazione su IsSelected = true. Tuttavia, questo accade con entrambe le modalità di Virtualizzazione (Riciclaggio e Standard). Avrei pensato che il DataGridRows sarebbe ricreato ogni volta?

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: sto postando il codice per la mia vista e ViewModel. Sono in grado di riprodurlo in modo coerente richiamando una chiamata "Seleziona tutte le righe" per aggiornare tutti i modelli di vista di riga su IsSelected = true. Dopo questo, scorro un po 'su e giù per vedere tutte le righe selezionate. Quindi invoco "Deseleziona tutte le righe" e tutti i modelli di visualizzazione di riga dovrebbero essere deselezionati. Mentre scorro verso il basso, posso vedere rowviewmodels quindi essere aggiornato a IsSelected = true (tramite messaggi di debug). Se interrompo e vedo come si chiama, ottengo la traccia dello stack sopra riportata.

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

Visualizza

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

Visualizza codice sottostante

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

Strano! Ho provato quello che stai facendo ma senza fortuna! quando imposto la proprietà '' IsSelected' 'a livello di riga dei miei elementi di riga su false (che è bidirezionale alla proprietà IsSelected di DataGridRow), anche lo scorrimento non seleziona alcuna riga indietro. Rimangono tutti non selezionati. : -/ –

+0

Puoi pubblicare il tuo codice? – Rachel

+0

Proverò a rimuovere il mio codice e pubblicarlo. Il problema probabilmente si verifica circa il 20% delle volte. 80% delle volte, deselezionerà tutto proprio bene. Hai provato con oltre 1200 righe e averle tutte selezionate? Questo è quello che sto facendo. – user832747

risposta

0

Potrebbe essere un problema di threading. Vedo che stai acquisendo un blocco sullo stesso oggetto che stai aggiornando all'interno del blocco di blocco, ad esempio oggetto

Devi sempre bloccare con un'istanza di nuovo oggetto separata dichiarata specificatamente per l'uso sulla parola chiave di blocco, ad es.

private readonly object _lockObject = new Object(); 

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

Provare con le modifiche al codice di cui sopra al blocco di blocco e vedere se il problema viene risolto.