如果您只是想防止在首次创建对象并设置属性时触发通知,则可以添加布尔标志,该标志为假,直到属性设置一次。仅当标志为真时才执行通知。
编辑:
我认为在删除所有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 实现注册表来解决此问题,但我不知道它们的用法以推荐特定的实现。