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();
}
}
}
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. : -/ –
Czy możesz napisać swój kod? – Rachel
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