なぜReadOnlyObservableCollection.CollectionChanged
保護されて公開されていないのObservableCollection.CollectionChanged
ですか(対応するもののように)?
イベントにINotifyCollectionChanged
アクセスできない場合、コレクションを実装するとCollectionChanged
どうなりますか?
なぜReadOnlyObservableCollection.CollectionChanged
保護されて公開されていないのObservableCollection.CollectionChanged
ですか(対応するもののように)?
イベントにINotifyCollectionChanged
アクセスできない場合、コレクションを実装するとCollectionChanged
どうなりますか?
回答:
解決策は次のとおりです。ReadOnlyObservableCollectionのCollectionChangedイベント
コレクションをINotifyCollectionChangedにキャストする必要があります。
私はあなたのためにこれを行う方法を見つけました:
ObservableCollection<string> obsCollection = new ObservableCollection<string>();
INotifyCollectionChanged collection = new ReadOnlyObservableCollection<string>(obsCollection);
collection.CollectionChanged += new NotifyCollectionChangedEventHandler(collection_CollectionChanged);
INotifyCollectionChangedインターフェイスによってコレクションを明示的に参照する必要があります。
ReadOnlyObservableCollection
あなたの.Netコードを閲覧すると、これが見つかります:event NotifyCollectionChangedEventHandler INotifyCollectionChanged.CollectionChanged
。イベントの明示的なインターフェース実装。
この投稿は古いことは知っていますが、コメントする前に、時間をかけて.NETで使用されているパターンを理解する必要があります。読み取り専用コレクションは、既存のコレクションのラッパーであり、コンシューマーがコレクションを直接変更できないようにReadOnlyCollection
します。見てみると、変更IList<T>
可能かどうかわからないラッパーであることがわかります。不変のコレクションは別の問題であり、新しい不変のコレクションライブラリでカバーされています
言い換えれば、読み取り専用は不変と同じではありません!!!!
それはさておき、ReadOnlyObservableCollection
暗黙的に実装する必要がありますINotifyCollectionChanged
。
ReadOnlyObservableCollectionでコレクション変更通知をサブスクライブするのには間違いなく正当な理由があります。したがって、単にコレクションをINotifyCollectionChangedとしてキャストする代わりに、ReadOnlyObservableCollectionをサブクラス化する場合、以下は、CollectionChangedイベントにアクセスするためのより構文的に便利な方法を提供します。
public class ReadOnlyObservableCollectionWithCollectionChangeNotifications<T> : ReadOnlyObservableCollection<T>
{
public ReadOnlyObservableCollectionWithCollectionChangeNotifications(ObservableCollection<T> list)
: base(list)
{
}
event System.Collections.Specialized.NotifyCollectionChangedEventHandler CollectionChanged2
{
add { CollectionChanged += value; }
remove { CollectionChanged -= value; }
}
}
これは以前私にとってはうまくいきました。
この問題を説明するMicrosoftConnectのバグエントリに投票することができます:https://connect.microsoft.com/VisualStudio/feedback/details/641395/readonlyobservablecollection-t-collectionchanged-event-should-be-public
更新:
ConnectポータルはMicrosoftによってシャットダウンされました。したがって、上記のリンクは機能しなくなります。
My Win Application Framework(WAF)ライブラリは、次のソリューションを提供します。ReadOnlyObservableListクラス:
public class ReadOnlyObservableList<T>
: ReadOnlyObservableCollection<T>, IReadOnlyObservableList<T>
{
public ReadOnlyObservableList(ObservableCollection<T> list)
: base(list)
{
}
public new event NotifyCollectionChangedEventHandler CollectionChanged
{
add { base.CollectionChanged += value; }
remove { base.CollectionChanged -= value; }
}
public new event PropertyChangedEventHandler PropertyChanged
{
add { base.PropertyChanged += value; }
remove { base.PropertyChanged -= value; }
}
}
すでに回答したように、2つのオプションがあります。明示的に実装されたイベントにアクセスReadOnlyObservableCollection<T>
するためにインターフェイスINotifyCollectionChanged
にキャストするかCollectionChanged
、コンストラクターで一度それを実行し、ラップされたのイベントをフックするだけの独自のラッパークラスを作成できますReadOnlyObservableCollection<T>
。
この問題がまだ修正されていない理由に関するいくつかの追加の洞察:
あなたから見ることができるように、ソースコード、ReadOnlyObservableCollection<T>
イベントがマークされている公共、非密封された(すなわち継承)クラス、ですprotected virtual
。
つまり、から派生したクラスを使用してコンパイルされたプログラムがありReadOnlyObservableCollection<T>
、イベント定義はオーバーライドされますが、protected
可視性があります。public
派生クラスでのイベントの可視性を制限することは許可されていないため、基本クラスでイベントの可視性が変更されると、これらのプログラムには無効なコードが含まれます。
残念ながら、後でprotected virtual
イベントを作成することpublic
はバイナリを壊す変更であり、したがって、「ハンドラーをアタッチするためにオブジェクトを1回キャストする必要がある」という非常に正当な理由がなければ、それは実行されません。
これはグーグルでトップヒットだったので、他の人がこれを調べた場合に備えて自分のソリューションを追加すると思いました。
上記の情報(INotifyCollectionChangedへのキャストの必要性について)を使用して、登録と登録解除の2つの拡張メソッドを作成しました。
私の解決策-拡張メソッド
public static void RegisterCollectionChanged(this INotifyCollectionChanged collection, NotifyCollectionChangedEventHandler handler)
{
collection.CollectionChanged += handler;
}
public static void UnregisterCollectionChanged(this INotifyCollectionChanged collection, NotifyCollectionChangedEventHandler handler)
{
collection.CollectionChanged -= handler;
}
例
IThing.cs
public interface IThing
{
string Name { get; }
ReadOnlyObservableCollection<int> Values { get; }
}
拡張メソッドの使用
public void AddThing(IThing thing)
{
//...
thing.Values.RegisterCollectionChanged(this.HandleThingCollectionChanged);
}
public void RemoveThing(IThing thing)
{
//...
thing.Values.UnregisterCollectionChanged(this.HandleThingCollectionChanged);
}
OPのソリューション
public void AddThing(IThing thing)
{
//...
INotifyCollectionChanged thingCollection = thing.Values;
thingCollection.CollectionChanged += this.HandleThingCollectionChanged;
}
public void RemoveThing(IThing thing)
{
//...
INotifyCollectionChanged thingCollection = thing.Values;
thingCollection.CollectionChanged -= this.HandleThingCollectionChanged;
}
代替案2
public void AddThing(IThing thing)
{
//...
(thing.Values as INotifyCollectionChanged).CollectionChanged += this.HandleThingCollectionChanged;
}
public void RemoveThing(IThing thing)
{
//...
(thing.Values as INotifyCollectionChanged).CollectionChanged -= this.HandleThingCollectionChanged;
}
ReadOnlyObservableCollection.CollectionChanged
(他の回答で概説されている正当な理由のために)公開されていないので、それを公開する独自のラッパークラスを作成しましょう:
/// <summary>A wrapped <see cref="ReadOnlyObservableCollection{T}"/> that exposes the internal <see cref="CollectionChanged"/>"/>.</summary>
public class ObservableReadOnlyCollection<T> : ReadOnlyObservableCollection<T>
{
public new NotifyCollectionChangedEventHandler CollectionChanged;
public ObservableReadOnlyCollection(ObservableCollection<T> list) : base(list) { /* nada */ }
protected override void OnCollectionChanged(NotifyCollectionChangedEventArgs args) =>
CollectionChanged?.Invoke(this, args);
}
読み取り専用コレクションへの変更を観察する理由を尋ねられたので、多くの有効な状況の1つを説明します。読み取り専用コレクションが、変更可能なプライベート内部コレクションをラップする場合。
そのようなシナリオの1つを次に示します。
サービスの外部から内部コレクションにアイテムを追加および削除できるサービスがあるとします。ここで、コレクションの値を公開したいが、コンシューマーがコレクションを直接操作したくないとします。したがって、内部コレクションをでラップしますReadOnlyObservableCollection
。
内部コレクションを内部コレクションでラップするために、のコンストラクターによって
ReadOnlyObservableCollection
派生するように強制されることに注意してください。ObservableCollection
ReadOnlyObservableCollection
ここで、内部コレクションが変更されたとき(したがって、公開されたReadOnlyObservableCollection
変更が変更されたとき)にサービスをコンシューマーに通知するとします。むしろ独自の実装をロールよりもあなただけ公開するCollectionChanged
のをReadOnlyObservableCollection
。消費者にの実装についての仮定を強制するのではなく、ReadOnlyObservableCollection
単にReadOnlyObservableCollection
このカスタムと交換するだけでObservableReadOnlyCollection
完了です。
ObservableReadOnlyCollection
皮革ReadOnlyObservableCollection.CollectionChanged
それには自分だ、と単純に接続されているすべてのイベントハンドラにイベントを変更し、すべてのコレクションを渡します。