2011-10-21 13 views
5

Tengo problemas con la forma en que funciona la virtualización con WPF DataGrid.Problemas de virtualización de Wpf DataGrid al vincular a la propiedad DataGridRow.IsSelected

Estoy usando MVVM y vinculando todos mis modelos de vista de fila a la propiedad IsSelected. Necesito eliminar la selección de todas las filas de vez en cuando, así que hago esto actualizando mis modelos de vista de fila subyacentes a IsSelected = false.

Parece que funciona bien al principio y desancla todo. He verificado que todos los modelos de vista de fila están configurados en falso usando mensajes de depuración y estableciendo puntos de interrupción condicional.

Sin embargo, hay un problema cuando me desplazo por la cuadrícula. Veo algunas filas realmente seleccionadas. Tengo un punto de interrupción condicional en la propiedad IsSelected cuando se establece en true, y esto en realidad no se rompe hasta que me desplazo por la cuadrícula. Entonces, a medida que despliego, algo ocasionalmente actualizará algunos de los modos de vista de la fila nuevamente a IsSelected = true?

No puedo entender lo que está pasando, o la pila de llamadas ... ¿alguien me puede explicar lo que está sucediendo en realidad? Supongo que tiene algo que ver con la virtualización. Pensé que tal vez la virtualización estaba reciclando un DataGridRow y, a su vez, estaba actualizando mis viewmodels a IsSelected = true. Sin embargo, esto sucede con ambos modos de virtualización (Reciclaje y Estándar). ¿Pensaría que los DataGridRows se volverían a crear cada vez?

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 

ACTUALIZACIÓN: les dejo el código de mi vista y ViewModels. Puedo reproducirlo constantemente invocando una llamada "Seleccionar todas las filas" para actualizar todos los modelos de vista de fila a IsSelected = true. Después de esto, me desplazo hacia arriba y hacia abajo un poco para ver todas las filas seleccionadas. Luego invoco "Anular selección de todas las filas" y todos los modelos de vista de fila deben ser deseleccionados. A medida que me desplazo hacia abajo, puedo ver los modos de vista en fila que se actualizan a IsSelected = true (a través de mensajes de depuración). Si rompo y veo lo que está llamando a esto, obtengo el rastro de la pila anterior.

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

Ver

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

Ver código subyacente

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

¡Extraño! ¡Intenté lo que estás haciendo pero no tuve suerte! cuando configuro mi propiedad "IsSelected" de elementos de nivel de fila en false (que está vinculada de dos formas a la propiedad IsSelected de DataGridRow), incluso el desplazamiento no selecciona ninguna fila hacia atrás. Todos ellos permanecen no seleccionados. : -/ –

+0

¿Puedes publicar tu código? – Rachel

+0

Intentaré quitar mi código y publicarlo. El problema probablemente ocurre aproximadamente el 20% del tiempo. 80% de las veces, eliminará la selección de todo muy bien. ¿Has probado con más de 1200 filas y las has seleccionado todas? Esto es lo que estoy haciendo. – user832747

Respuesta

0

Podría ser un problema de rosca. Veo que está adquiriendo un bloqueo en el mismo objeto que está actualizando dentro del bloque de bloqueo, es decir, el objeto de pedido

Siempre debe bloquear con una nueva instancia de objeto específicamente declarada para su uso en la palabra clave de bloqueo, p.

private readonly object _lockObject = new Object(); 

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

Pruebe con los cambios de código anteriores en su bloque de bloqueo y vea si el problema se resuelve.

Cuestiones relacionadas