私のビューのリストコントロールにバインドされているObservableCollection
のアイテムがあります。
コレクションの先頭に値のチャンクを追加する必要がある状況があります。 _Collection<T>.Insert
_のドキュメントでは、各挿入をO(n)操作として指定しており、各挿入はCollectionChanged
通知も生成します。
したがって、理想的には、全範囲のアイテムを1回の移動で挿入します。つまり、基になるリストの1つのシャッフルと、できれば1つのCollectionChanged
通知(おそらく「リセット」)を挿入します。
_Collection<T>
_は、これを行うためのメソッドを公開していません。 _List<T>
_にはInsertRange()
がありますが、_IList<T>
_は、Items
プロパティを介して公開する_Collection<T>
_にはありません。
これを行う方法はありますか?
ObservableCollectionは保護されたItems
プロパティを公開します。これは、通知セマンティクスなしで基礎となるコレクションです。これは、ObservableCollectionを継承することで、希望どおりのコレクションを構築できることを意味します。
class RangeEnabledObservableCollection<T> : ObservableCollection<T>
{
public void InsertRange(IEnumerable<T> items)
{
this.CheckReentrancy();
foreach(var item in items)
this.Items.Add(item);
this.OnCollectionChanged(new NotifyCollectionChangedEventArgs(NotifyCollectionChangedAction.Reset));
}
}
使用法:
void Main()
{
var collection = new RangeEnabledObservableCollection<int>();
collection.CollectionChanged += (s,e) => Console.WriteLine("Collection changed");
collection.InsertRange(Enumerable.Range(0,100));
Console.WriteLine("Collection contains {0} items.", collection.Count);
}
上記の答えをリフレクションを使用して新しい基本クラスを導出せずに役立つようにするために、以下に例を示します。
public static void InsertRange<T>(this ObservableCollection<T> collection, IEnumerable<T> items)
{
var enumerable = items as List<T> ?? items.ToList();
if (collection == null || items == null || !enumerable.Any())
{
return;
}
Type type = collection.GetType();
type.InvokeMember("CheckReentrancy", BindingFlags.Instance | BindingFlags.InvokeMethod | BindingFlags.NonPublic, null, collection, null);
var itemsProp = type.BaseType.GetProperty("Items", BindingFlags.NonPublic | BindingFlags.FlattenHierarchy | BindingFlags.Instance);
var privateItems = itemsProp.GetValue(collection) as IList<T>;
foreach (var item in enumerable)
{
privateItems.Add(item);
}
type.InvokeMember("OnPropertyChanged", BindingFlags.Instance | BindingFlags.InvokeMethod | BindingFlags.NonPublic, null,
collection, new object[] { new PropertyChangedEventArgs("Count") });
type.InvokeMember("OnPropertyChanged", BindingFlags.Instance | BindingFlags.InvokeMethod | BindingFlags.NonPublic, null,
collection, new object[] { new PropertyChangedEventArgs("Item[]") });
type.InvokeMember("OnCollectionChanged", BindingFlags.Instance | BindingFlags.InvokeMethod | BindingFlags.NonPublic, null,
collection, new object[]{ new NotifyCollectionChangedEventArgs(NotifyCollectionChangedAction.Reset)});
}
この answer では、DataGridの新しいエントリは表示されませんでした。このOnCollectionChangedは私にとってはうまくいきます:
public class SilentObservableCollection<T> : ObservableCollection<T>
{
public void AddRange(IEnumerable<T> enumerable)
{
CheckReentrancy();
int startIndex = Count;
foreach (var item in enumerable)
Items.Add(item);
OnCollectionChanged(new NotifyCollectionChangedEventArgs(NotifyCollectionChangedAction.Add, new List<T>(enumerable), startIndex));
OnPropertyChanged(new PropertyChangedEventArgs("Count"));
OnPropertyChanged(new PropertyChangedEventArgs("Item[]"));
}
}