コレクションに対してなにかしらの操作(追加や削除など)をしたときに、なにかしらの処理を行いたいことがあります。
そんなときはObservableCollection<T>を使用すると便利です。
ちなみにObservableCollection<T>はCollection<T>と同じ名前空間に用意されている.netの標準ライブラリなので、外部ライブラリと比べて気軽に(?)使えます。
ObservableCollection とは?
ObservableCollection<T>は、Collection<T>に以下のようなさまざまな機能を追加したクラスです。
- コレクションに対する操作(追加・削除など)が行われたときにCollectionChangedというイベントを発火する。
- Collection<T>にはない「要素の移動」を提供するメソッド(Move())がある。
実際に、ObsevableCollection<T>はCollection<T>を継承したクラスになっています。

上記のクラス図を見るとわかるように2つのインターフェースを実装しています。これらのインターフェースはイベントを提供しています。「○○Changed」という名前から推測できるように、以下のような「変更時に発火する」イベントを提供しています。
- INotifyCollectionChangedインターフェース
CollectionChangeイベント:コレクションに対する操作(追加・削除など)が行われたときに発火するイベント - INotifyPropertyChangedインターフェース
PropertyChangedイベント:ObsevableCollection<T>型のプロパティの値が変わった時(参照先のインスタンスが変わったとき)に発火するイベント
ObsevableCollectionはこれら2つのインターフェースを実装することで、コレクションに対して変更・操作が起こった時になにかしら任意の処理が行えるようになっています。
なお、当然ながらCollection<T>よりも基本的に高性能であるぶんパフォーマンスが低くなります。前述したようにObsevableCollection<T>は要素が追加・削除されたときにイベントが発火します。そのため変更を監視する必要が無いような場合は普通にCollection<T>使う方がパフォーマンスが高くなります。
ObservableCollection<T>の基本的な使い方
コレクションの操作
ObsevableCollection<T>はCollectioin<T>と同じ使用感で、要素の追加・削除が行えます。
public class Program
{
/// <summary>
/// エントリポイント
/// </summary>
/// <param name="args"></param>
static void Main(string[] args)
{
var obsevableCollection = new ObservableCollection<int>() { 1, 2, 3 };
Console.WriteLine("初期状態");
ViewCollectionInfo(obsevableCollection); // 1,2,3
obsevableCollection.Add(4);
obsevableCollection.Add(5);
obsevableCollection.Add(6);
Console.WriteLine("追加後");
ViewCollectionInfo(obsevableCollection); // 1,2,3,4,5,6
obsevableCollection.Remove(2);
Console.WriteLine("削除後");
ViewCollectionInfo(obsevableCollection); // 1,3,4,5,6
obsevableCollection.Move(0, 3);
Console.WriteLine("移動後");
ViewCollectionInfo(obsevableCollection); // 3,4,5,1,6
obsevableCollection[0] = 9;
Console.WriteLine("置き換え後");
ViewCollectionInfo(obsevableCollection); // 9,4,5,1,6
obsevableCollection.Clear();
Console.WriteLine("クリアー後");
ViewCollectionInfo(obsevableCollection); // (出力なし)
}
/// <summary>
/// コレクションの確認用メソッド
/// </summary>
/// <param name="collection"></param>
static void ViewCollectionInfo(ObservableCollection<int> collection)
{
Console.WriteLine(String.Join(
',',
collection.Select(x => $"{x}")));
Console.WriteLine();
}
}
リストの変更を監視する(CollectionChanged のイベントハンドリング)
ObsevableCollection<T>に対してなにかしらの操作(追加、削除など)をしたとき、CollectioinChangedイベントが発火します。
CollectionChangedイベントのイベント引数は「NotifyCollectionChangedEventArgs」クラス型であり、このクラスが持つ以下の5つのプロパティを使うことで、コレクションに対する操作の内容を取得できます。
これらのプロパティを使いこなせれば9割なんとかなるのかなと思っています。
プロパティ | 内容 |
---|---|
Action | イベントの原因となったアクションを取得します。 |
NewItems | 変更に関連する新しい項目の一覧を取得します。 |
NewStartingIndex | 変更が発生したインデックスを取得します。 |
OldItems | Replace、Remove、または Move アクションの影響を受ける項目の一覧を取得します。 |
OldStartingIndex | Move、Remove、または Replace アクションが発生したインデックスを取得します。 |

具体例を以下に示します(少し処理が雑なところがありますがそこは…うん)。
public class Program
{
/// <summary>
/// エントリポイント
/// </summary>
/// <param name="args"></param>
static void Main(string[] args)
{
var obsevableCollection = new ObservableCollection<int>() { 1, 2, 3 };
// イベントハンドラの登録
obsevableCollection.CollectionChanged += (sender, e) => OnCollectionChanged(sender, e);
ViewCollectionInfo(obsevableCollection); // 1,2,3
obsevableCollection.Add(4);
obsevableCollection.Add(5);
obsevableCollection.Add(6);
ViewCollectionInfo(obsevableCollection); // 1,2,3,4,5,6
obsevableCollection.Remove(2);
ViewCollectionInfo(obsevableCollection); // 1,3,4,5,6
obsevableCollection.Move(0, 3);
ViewCollectionInfo(obsevableCollection); // 3,4,5,1,6
obsevableCollection[0] = 9;
ViewCollectionInfo(obsevableCollection); // 9,4,5,1,6
obsevableCollection.Clear();
ViewCollectionInfo(obsevableCollection); // (出力なし)
}
/// <summary>
/// コレクションの確認用メソッド
/// </summary>
/// <param name="collection"></param>
static void ViewCollectionInfo(ObservableCollection<int> collection)
{
Console.WriteLine(String.Join(
',',
collection.Select(x => $"{x}")));
Console.WriteLine();
}
/// <summary>
/// コレクションに対してなにかしらの操作が発生した時の処理
/// </summary>
/// <param name="sender"></param>
/// <param name="notifyCollectionChangedEventArgs"></param>
static void OnCollectionChanged(Object? sender, NotifyCollectionChangedEventArgs notifyCollectionChangedE
{
// コレクションに対する操作の種類
var action = notifyCollectionChangedEventArgs.Action;
switch (action)
{
case NotifyCollectionChangedAction.Add:
{
Console.Write("要素が追加されました:");
if (notifyCollectionChangedEventArgs.NewItems == null) { return; }
var addItems = notifyCollectionChangedEventArgs.NewItems;
var addStartingIndex = notifyCollectionChangedEventArgs.NewStartingIndex;
Console.Write($"index:{addStartingIndex}, ");
Console.Write($"value:");
foreach (var newItem in addItems)
{
Console.Write($"{newItem} ");
}
Console.WriteLine();
break;
}
case NotifyCollectionChangedAction.Remove:
{
Console.Write("要素が削除されました:");
if (notifyCollectionChangedEventArgs.OldItems == null) { return; }
var removeItems = notifyCollectionChangedEventArgs.OldItems;
var removeStartingIndex = notifyCollectionChangedEventArgs.OldStartingIndex;
Console.Write($"index:{removeStartingIndex}, ");
Console.Write($"value:");
foreach (var oldItem in removeItems)
{
Console.Write($"{oldItem} ");
}
Console.WriteLine();
break;
}
case NotifyCollectionChangedAction.Move:
{
// 要素が削除されたとき
if (notifyCollectionChangedEventArgs.NewItems == null) { return; }
var moveItems = notifyCollectionChangedEventArgs.NewItems;
var fromIndex = notifyCollectionChangedEventArgs.OldStartingIndex;
var toIndex = notifyCollectionChangedEventArgs.NewStartingIndex;
Console.Write("要素が移動されました:");
Console.Write($"index:[元]{fromIndex}->[先]{toIndex}, ");
Console.Write($"value:");
foreach (var oldItem in moveItems)
{
Console.Write($"{oldItem} ");
}
Console.WriteLine();
break;
}
case NotifyCollectionChangedAction.Replace:
{
Console.Write("要素が置き換えられました:");
if (notifyCollectionChangedEventArgs.OldItems == null) { return; }
if (notifyCollectionChangedEventArgs.NewItems == null) { return; }
var targetIndex = notifyCollectionChangedEventArgs.NewStartingIndex;
var beforeValue = notifyCollectionChangedEventArgs.OldItems;
var afterValue = notifyCollectionChangedEventArgs.NewItems;
Console.Write($"index:{targetIndex}, ");
Console.Write($"value:[前]{beforeValue[0]}->[後]{afterValue[0]}");
Console.WriteLine();
break;
}
case NotifyCollectionChangedAction.Reset:
{
Console.Write("コレクションがクリアーされました:");
break;
}
}
}
}
1,2,3
要素が追加されました:index:3, value:4
要素が追加されました:index:4, value:5
要素が追加されました:index:5, value:6
1,2,3,4,5,6
要素が削除されました:index:1, value:2
1,3,4,5,6
要素が移動されました:index:[元]0->[先]3, value:1
3,4,5,1,6
要素が置き換えられました:index:0, value:[前]3->[後]9
9,4,5,1,6
コレクションがクリアーされました:
コメント