Если вы просто пытаетесь предотвратить запуск уведомлений при первом создании объекта и задании свойств, вы можете добавить логический (-ые) флаг (-ы), которые являются ложными, пока свойства не будут установлены один раз. Вы только выполняете уведомление, если флаг установлен в true.
Edit:
Я не думаю, что есть чистый способ получить функциональность после удаления всего кода 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");
}
}
}
Вы можете сделать то же самое без метода, если бы вы знали во время создания экземпляра, должен ли экземпляр генерировать уведомления, и в этом случае вам просто понадобится логический параметр в конструкторе.
Другим вариантом будет использование шаблона «Фабрика», когда ваша Фабрика имеет внутренний доступ к логическому флагу и устанавливает его при создании.
Инкапсулировать условие в прокси:
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
, а условный аспект обрабатывается извне. Другое преимущество состоит в том, что это помогает навязывать программирование абстракциям и инверсии управления.
Основным недостатком является то, что вам нужно создавать прокси для каждой реализации 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
}
}
Теперь у реестра есть особый недостаток, заключающийся в том, что он содержит ссылки на каждый экземпляр и не позволит этим экземплярам быть подобранным сборщиком мусора. Может быть решение этой проблемы путем реализации реестра с WeakReference
s, но я не в курсе их использования, чтобы рекомендовать конкретную реализацию.