Tôi đang gặp vấn đề về cách ảo hóa hoạt động với WPF DataGrid.Vấn đề ảo hóa Wpf DataGrid khi liên kết với thuộc tính DataGridRow.IsSelected
Tôi đang sử dụng MVVM và ràng buộc tất cả các chế độ xem hàng của tôi vào thuộc tính được chọn. Tôi cần phải bỏ chọn tất cả các hàng đôi khi, vì vậy tôi làm điều này bằng cách cập nhật các mô hình chế độ xem hàng bên dưới của tôi thành IsSelected = false.
Điều này dường như hoạt động OK lúc đầu và nó bỏ chọn mọi thứ. Tôi đã xác minh rằng tất cả các chế độ xem hàng được đặt thành false bằng cách sử dụng thông báo gỡ lỗi và đặt các điểm ngắt có điều kiện.
Tuy nhiên, có sự cố khi tôi cuộn qua lưới. Tôi thấy một số hàng thực sự được chọn. Tôi có một breakpoint có điều kiện trên thuộc tính IsSelected khi nó được thiết lập là true, và điều này không thực sự phá vỡ cho đến khi tôi di chuyển qua lưới. Vì vậy, khi tôi cuộn xuống, một số thứ sẽ thỉnh thoảng cập nhật một số chế độ xem hàng trở lại IsSelected = true?
Tôi hoàn toàn không thể hiểu chuyện gì đang xảy ra, hoặc gọi điện thoại cho nó ... ai đó có thể giải thích cho tôi điều gì đang xảy ra không? Tôi cho rằng đó là điều cần làm với ảo hóa. Tôi nghĩ rằng có lẽ ảo hóa đã tái chế một DataGridRow, và lần lượt cập nhật các kiểu xem của tôi trở lại IsSelected = true. Tuy nhiên, điều này xảy ra với cả hai chế độ Ảo hóa (Tái chế và Chuẩn). Tôi đã nghĩ rằng DataGridRows sẽ được tái tạo mỗi lần?
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: tôi gửi bài mã cho Xem và ViewModels của tôi. Tôi có thể tái tạo nó một cách nhất quán bằng cách gọi một cuộc gọi "Chọn tất cả các hàng" để cập nhật tất cả các mô hình chế độ xem hàng thành IsSelected = true. Sau đó, tôi di chuyển lên xuống một chút để xem tất cả các hàng đã chọn. Sau đó, tôi gọi "Bỏ chọn tất cả các hàng" và tất cả các mô hình chế độ xem hàng nên được bỏ chọn. Khi tôi di chuyển xuống, tôi có thể thấy rowviewmodels sau đó được cập nhật thành IsSelected = true (thông qua thông báo gỡ lỗi). Nếu tôi phá vỡ và xem những gì đang gọi này, tôi nhận được dấu vết stack ở trên.
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);
}
}
}
}
Xem
<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>
Xem 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();
}
}
}
Lạ! Tôi đã thử những gì bạn đang làm nhưng không may mắn! khi tôi đặt thuộc tính mức hàng 'IsSelected' của mình thành false (là hai cách liên kết với thuộc tính IsSelected của DataGridRow), thậm chí cuộn không chọn bất kỳ hàng nào trở lại. Tất cả chúng đều không được chọn. : -/ –
Bạn có thể đăng mã của mình không? – Rachel
Tôi sẽ cố gắng loại bỏ mã của tôi và đăng nó. Vấn đề có thể xảy ra khoảng 20% thời gian. 80% thời gian, nó sẽ bỏ chọn tất cả mọi thứ tốt. Bạn đã thử với hơn 1200 hàng và có tất cả các hàng đã chọn chưa? Đây là những gì tôi đang làm. – user832747