オブジェクトが最初に作成され、プロパティが設定されたときに通知が発生しないようにするだけの場合は、プロパティが一度設定されるまで false であるブール フラグを追加できます。フラグが true の場合にのみ通知を実行します。
編集:
すべてのコードを削除した後にそこに機能を取得するきれいな方法はないと思いますがINotifyPropertyChanged
、インスタンスの外部から機能を制御する方法はたくさんあります。
このコードはすべて、VisualStudio ではなくテキスト エディターで記述したことに注意してください。いかなる方法でもテストされていません。
通知を有効にするメソッドを追加します。
public class OptionalNotification : INotifyPropertyChanged
{
public event PropertyChangedEventHandler PropertyChanged;
void OnPropertyChanged(string name) ...
bool _shouldNotify;
public void EnableNotifications()
{
_shouldNotify = true;
}
string _someProperty;
public string SomeProperty
{
get { return _someProperty; }
set
{
if(_someProperty == value) return
_someProperty = value;
if(_shouldNotify) OnPropertyChanged("SomeProperty");
}
}
}
インスタンス化の時点でインスタンスが通知を生成する必要があるかどうかがわかっている場合は、メソッドを使用せずに同じことを行うことができます。その場合、コンストラクターでブール値パラメーターが必要になるだけです。
もう 1 つのバリエーションは、ファクトリ パターンを使用することです。この場合、ファクトリはブール フラグへの内部アクセスを持ち、構築時にそれを設定します。
条件をプロキシにカプセル化します。
public interface IEntity : INotifyPropertyChanged
{
string SomeProperty { get; set; }
}
public class Entity : IEntity
{
public event PropertyChangedEventHandler PropertyChanged;
public void OnPropertyChanged(string name) ...
string _someProperty;
public string SomeProperty
{
get { return _someProperty; }
set
{
if(_someProperty == value) return
_someProperty = value;
OnPropertyChanged("SomeProperty");
}
}
}
public class EntityNotificationProxy : IEntity
{
IEntity _inner;
public EntityNotificationProxy(IEntity entity)
{
_inner = entity;
_inner.PropertyChanged += (o,e) => { if(ShouldNotify) OnPropertyChanged(o,e); }
}
public bool ShouldNotify { get; set; }
public event PropertyChangedEventHandler PropertyChanged;
void OnPropertyChanged(object sender, PropertChangedEventArgs e)
{
PropertyChangedEventHandler handler = PropertyChanged;
if(handler != null) handler(sender, e);
}
public string SomeProperty
{
get { return _inner.SomeProperty; }
set
{
if(_inner.SomeProperty == value) return
_inner.SomeProperty = value;
}
}
}
ここで、消費するクラスは、エンティティ自体ではなくエンティティ プロキシを取得します (ただし、IEntity
インターフェイス/抽象化をプログラムする場合にのみ参照するため、それほど賢明ではありません)。プロキシのラッピングは、ファクトリまたは IoC コンテナー/DI フレームワークを介して行うことができます。
このアプローチの主な利点は、エンティティが純粋なINotifyPropertyChanged
実装を維持し、条件付きの側面が外部から処理されることです。もう 1 つの利点は、プログラミングを抽象化と制御の反転に強制するのに役立つことです。
INotifyPropertyChanged
主な欠点は、この条件付き動作を実現する実装ごとにプロキシを作成する必要があることです。
レジストリを作成して、どのインスタンスが通知を発生させる必要があるか、または発生させないかを追跡します。
public static class PropertyNotificationRegistry
{
static IDictionary<INotifyPropertyChanged, bool> _registeredClasses
= new Dictionary<INotifyPropertyChanged, bool>;
static void Register(INotifyPropertyChanged o, bool shouldNotify)
{
if(!(_registeredClasses.ContainsKey(o)) _registeredClasses.Add(o, shouldNotify);
// could also implement logic to update an existing class in the dictionary
}
public static void ShouldNotifyWhenPropertiesChange(this INotifyPropertyChanged o)
{
Register(o, true);
}
public static void ShouldNotNotifyWhenPropertiesChange(this INotifyPropertyChanged o)
{
Register(o, false);
}
public static void NotifyPropertyChanged(this INotifyPropertyChanged o, Action notificationAction)
{
if(_registeredClasses.ContainsKey(o))
{
bool shouldNotify = _registeredClasses.Where(x => x.Key == o).Single().Value;
if(shouldNotify) notificationAction();
}
}
}
public class EntityUsingNotificationRegistry : INotifyPropertyChanged
{
... // all the standard INotifyPropertyChanged stuff
string _someProperty;
public string SomeProperty
{
get { return _someProperty; }
set
{
if(_someProperty == value) return;
_someProperty = value;
this.NotifyPropertyChanged(() => OnPropertyChanged("SomeProperty"));
}
}
}
public class SomethingInstantiatingOurEntity
{
public void DoSomething()
{
var entity1 = new EntityUsingNotificationRegistry();
entity1.ShouldNotifyWhenPropertiesChange();
var entity2 = new EntityUsingNotificationRegistry();
entity2.ShouldNotNotifyWhenPropertiesChange();
entity1.SomeProperty = "arbitrary string"; // raises event
entity2.SomeProperty = "arbitrary string"; // does not raise event
var entity3 = new EntityUsingNotificationRegistry();
entity3.SomeProperty = "arbitrary string"; // does not raise event
entity3.ShouldNotifyWhenPropertiesChange();
entity3.SomeProperty = "another arbitrary string"; // now raises event
}
}
現在、レジストリには、すべてのインスタンスへの参照が保持されており、これらのインスタンスがガベージ コレクターによって取得されないという明確な欠点があります。s を使用してレジストリを実装することで、これを解決できる可能性がありますがWeakReference
、特定の実装を推奨するために、その使用法について十分に理解しているわけではありません。