Ответ 1
Очень быстрый и простой способ заключается в подклассе ObservableCollection и приостановке уведомлений при вызове AddRange. См. после сообщения в блоге для уточнения.
У меня есть приложение WPF, которое использует привязки данных MVVM. Я добавляю элементы к ObservableCollection<...>
, и довольно многие из них действительно.
Теперь мне интересно, что каждый раз, когда я добавляю его в коллекцию, он мгновенно запускает событие и вызывает лишние накладные расходы? Если да, могу ли я каким-то образом временно отключить уведомления о событиях и вручную запустить его один раз в конце моего кода, чтобы, если я добавлю 10k элементов, он будет запускаться только один раз, а не 10k раз?
Обновление: я попробовал этот класс:
using System;
using System.Linq;
using System.Collections.Specialized;
using System.Collections.Generic;
namespace MyProject
{
/// <summary>
/// Represents a dynamic data collection that provides notifications when items get added, removed, or when the whole list is refreshed.
/// </summary>
/// <typeparam name="T"></typeparam>
public class ObservableCollection<T> : System.Collections.ObjectModel.ObservableCollection<T>
{
/// <summary>
/// Adds the elements of the specified collection to the end of the ObservableCollection(Of T).
/// </summary>
public void AddRange(IEnumerable<T> collection)
{
foreach (var i in collection) Items.Add(i);
OnCollectionChanged(new NotifyCollectionChangedEventArgs(NotifyCollectionChangedAction.Add, collection.ToList()));
}
/// <summary>
/// Removes the first occurence of each item in the specified collection from ObservableCollection(Of T).
/// </summary>
public void RemoveRange(IEnumerable<T> collection)
{
foreach (var i in collection) Items.Remove(i);
OnCollectionChanged(new NotifyCollectionChangedEventArgs(NotifyCollectionChangedAction.Remove, collection.ToList()));
}
/// <summary>
/// Clears the current collection and replaces it with the specified item.
/// </summary>
public void Replace(T item)
{
ReplaceRange(new T[] { item });
}
/// <summary>
/// Clears the current collection and replaces it with the specified collection.
/// </summary>
public void ReplaceRange(IEnumerable<T> collection)
{
List<T> old = new List<T>(Items);
Items.Clear();
foreach (var i in collection) Items.Add(i);
OnCollectionChanged(new NotifyCollectionChangedEventArgs(NotifyCollectionChangedAction.Replace, collection.ToList()));
}
/// <summary>
/// Initializes a new instance of the System.Collections.ObjectModel.ObservableCollection(Of T) class.
/// </summary>
public ObservableCollection() : base() { }
/// <summary>
/// Initializes a new instance of the System.Collections.ObjectModel.ObservableCollection(Of T) class that contains elements copied from the specified collection.
/// </summary>
/// <param name="collection">collection: The collection from which the elements are copied.</param>
/// <exception cref="System.ArgumentNullException">The collection parameter cannot be null.</exception>
public ObservableCollection(IEnumerable<T> collection) : base(collection) { }
}
}
Теперь я получаю эту ошибку:
Дополнительная информация: действия диапазона не поддерживаются.
Ошибка здесь:
OnCollectionChanged(new NotifyCollectionChangedEventArgs(NotifyCollectionChangedAction.Add, collection.ToList()));
Очень быстрый и простой способ заключается в подклассе ObservableCollection и приостановке уведомлений при вызове AddRange. См. после сообщения в блоге для уточнения.
Существует своего рода "сложный" способ, но довольно точный, на мой взгляд, для достижения этого.
Необходимо написать собственный ObservableCollection
и реализовать обработку AddRange
.
Таким образом, вы можете добавить все ваши 10k-элементы в какую-нибудь "коллекцию держателей", а после того, как вы закончите, используйте AddRange
вашего ObservableColleciton
для этого.
Подробнее об этом вы можете найти по этой ссылке:
ObservableCollection Не поддерживает метод AddRange....
или этот тоже
Это расширение ObservableCollection легко решает проблему.
Он предоставляет публичное свойство SupressNotification, позволяющее пользователю управлять, когда уведомление CollectionChanged будет подавлено.
Он не предлагает вставку/удаление диапазона, но если уведомление CollectionChanged подавлено, необходимость делать операцию диапазона в коллекции уменьшается в большинстве случаев.
Эта реализация заменяет все подавленные уведомления уведомлением Reset. Это логически разумно. Когда пользователь подавляет уведомление, выполняет массовые изменения и затем повторно активирует его, ему следует отправить уведомление Resent.
public class ObservableCollectionEx<T> : ObservableCollection<T>
{
private bool _notificationSupressed = false;
private bool _supressNotification = false;
public bool SupressNotification
{
get
{
return _supressNotification;
}
set
{
_supressNotification = value;
if (_supressNotification == false && _notificationSupressed)
{
this.OnCollectionChanged(new NotifyCollectionChangedEventArgs(NotifyCollectionChangedAction.Reset));
_notificationSupressed = false;
}
}
}
protected override void OnCollectionChanged(NotifyCollectionChangedEventArgs e)
{
if (SupressNotification)
{
_notificationSupressed = true;
return;
}
base.OnCollectionChanged(e);
}
}