WPF DataGrid(MultiSelector?)多次引发 SelectedItems CollectionChanged 事件

问题描述 投票:0回答:3

我不确定这是否是 DataGrid 控件或 MultiSelector 的问题,但当我在网格中选择多行时,每行都会触发 CollectionChanged 事件。如果我用鼠标“拖动”,这是有意义的,但如果我“按住 Shift 键并单击”选择多行或简单地单击左上角的“选择所有行”按钮,也会发生这种情况。

我在 MultiSelector 上看到有 Begin/EndUpdateSelectedItems 方法以及 IsUpdatingSelectedItems 属性。不幸的是,我的这个系列/活动的消费者不知道它的来源。

有没有办法让DataGrid / SelectedItems集合只在更新完成时发送CollectionChanged通知?

谢谢您。

编辑: 我发现对于 DataGrid,即使更改较大的选择,也不会设置 IsUpdatingSelectedItems 属性。

编辑: 我发现 DataGrid SelectionChanged 事件在完全更改后仅正确触发一次。不幸的是,因为这破坏了简单数据绑定的可能性,但如果您可以控制 SelectedItems 集合的使用者,那么这是一个潜在的解决方法。

c# wpf wpfdatagrid
3个回答
1
投票

为了完整起见,我将“回答”我自己的问题。事实证明,WPF 控件通常只能处理其 CollectionChanged 事件处理程序中的单个元素更改,这意味着“为每个项目调用 CollectionChanged”工作流程对于当前形式的框架来说是“正确”的方式。然而,我个人觉得这是一个可怕的性能问题。


0
投票
视图模型:

private MultiSelector _selectedItems; Public MultiSelector SelectedItems { get {return _selectedItems; set { _selectedItems=value;} }

SelectedItems

属性绑定到 DataGrid 的 SelectedItem 并添加 System.Windows.Controls.Primitives.MultiSelector


0
投票

using Microsoft.Xaml.Behaviors; using System.Collections; using System.Collections.Specialized; using System.Windows; using System.Collections.Generic; using System.Collections.ObjectModel; using System.Diagnostics; using System.ComponentModel; using System.Runtime.CompilerServices; public class MultiSelectMultiSelectorBehavior : Behavior<System.Windows.Controls.Primitives.MultiSelector> { public static readonly DependencyProperty SelectedItemsProperty = DependencyProperty.Register(nameof(SelectedItems), typeof(INotifyCollectionChanged), typeof(MultiSelectMultiSelectorBehavior), new PropertyMetadata(OnSelectedItemsPropertyChanged)); public INotifyCollectionChanged SelectedItems { get { return (INotifyCollectionChanged)GetValue(SelectedItemsProperty); } set { SetValue(SelectedItemsProperty, value); } } private static void OnSelectedItemsPropertyChanged(DependencyObject target, DependencyPropertyChangedEventArgs args) { SubscribeBinding(target, args.OldValue as INotifyCollectionChanged, false); SubscribeBinding(target, args.NewValue as INotifyCollectionChanged, true); } private static void SubscribeBinding(DependencyObject target, INotifyCollectionChanged collection, bool subscribe) { if (collection == null) return; if (subscribe) { collection.CollectionChanged += ((MultiSelectMultiSelectorBehavior)target).ContextSelectedItems_CollectionChanged; } else { collection.CollectionChanged -= ((MultiSelectMultiSelectorBehavior)target).ContextSelectedItems_CollectionChanged; } } private void SubscribeGridChanged(bool subscribe) { if (subscribe) { AssociatedObject.AddHandler(System.Windows.Controls.Primitives.Selector.SelectionChangedEvent, new RoutedEventHandler(Grid_SelectionChangedEvent)); } else { AssociatedObject.RemoveHandler(System.Windows.Controls.Primitives.Selector.SelectionChangedEvent, new RoutedEventHandler(Grid_SelectionChangedEvent)); } } protected override void OnAttached() { base.OnAttached(); SubscribeGridChanged(true); } protected override void OnDetaching() { base.OnDetaching(); SubscribeGridChanged(false); } private void SubscribeToEvents() { SubscribeGridChanged(true); SubscribeBinding(this, SelectedItems, true); } private void UnsubscribeFromEvents() { SubscribeGridChanged(false); SubscribeBinding(this, SelectedItems, false); } private void ContextSelectedItems_CollectionChanged(object sender, NotifyCollectionChangedEventArgs e) { Transfer(SelectedItems as IList, AssociatedObject.SelectedItems); } private void Grid_SelectionChangedEvent(object sender, RoutedEventArgs e) { Transfer(AssociatedObject.SelectedItems, SelectedItems as IList); } public void Transfer(IList source, IList target) { if (source == null || target == null) return; UnsubscribeFromEvents(); (target as IBulkChange)?.BeginBulkOperation(); target.Clear(); foreach (var o in source) { target.Add(o); } (target as IBulkChange)?.EndBulkOperation(); SubscribeToEvents(); } } public interface IBulkChange { void BeginBulkOperation(); void EndBulkOperation(); } [DebuggerDisplay("Count = {Count}")] public class BulkObservableCollection<T> : ObservableCollection<T>, IBulkChange { private bool _collectionChangedDuringRangeOperation; private int _rangeOperationCount; private ReadOnlyObservableCollection<T> _readOnlyAccessor; public BulkObservableCollection() { } public BulkObservableCollection(List<T> list) : base(list) { } public BulkObservableCollection(IEnumerable<T> collection) : base(collection) { } public void SetCollection(IEnumerable<T> collection) { try { BeginBulkOperation(); Clear(); AddRange(collection); } finally { EndBulkOperation(); } } public void AddRange(IEnumerable<T> items) { if (items != null) { try { BeginBulkOperation(); foreach (T local in items) { Add(local); } } finally { EndBulkOperation(); } } } public void RemoveRange(IEnumerable<T> items) { if (items != null) { try { this.BeginBulkOperation(); foreach (T local in items) { base.Remove(local); } } finally { this.EndBulkOperation(); } } } public ReadOnlyObservableCollection<T> AsReadOnly() { if (this._readOnlyAccessor == null) { this._readOnlyAccessor = new ReadOnlyObservableCollection<T>(this); } return this._readOnlyAccessor; } public void BeginBulkOperation() { this._rangeOperationCount++; } public void EndBulkOperation() { if (((this._rangeOperationCount > 0) && (--this._rangeOperationCount == 0)) && this._collectionChangedDuringRangeOperation) { InternalEndBulkOperation(); } } private void InternalEndBulkOperation() { this.OnCollectionChanged(new NotifyCollectionChangedEventArgs(NotifyCollectionChangedAction.Reset)); this._collectionChangedDuringRangeOperation = false; } protected override void OnCollectionChanged(NotifyCollectionChangedEventArgs e) { if (this._rangeOperationCount == 0) { base.OnCollectionChanged(e); } else { this._collectionChangedDuringRangeOperation = true; } } public List<T> ToList() { return new List<T>(this); } }

然后是 XAML 

<Window x:Class="WpfApplication1.MainWindow" xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation" xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" xmlns:local="clr-namespace:WpfApplication1" xmlns:b="http://schemas.microsoft.com/xaml/behaviors" > <DataGrid ItemsSource="{Binding AllItems}"> <b:Interaction.Behaviors> <local:MultiSelectMultiSelectorBehavior SelectedItems="{Binding Path=SelectedItems}" /> </b:Interaction.Behaviors> </DataGrid> </Window>

© www.soinside.com 2019 - 2024. All rights reserved.