If you are just trying to prevent the notifications from being fired when the object is first created and properties set, you can add boolean flag(s) that is/are false until the properties have been set once. You only execute the notification if the flag is true.
Edit:
I don't think there's a clean way to get the functionality in there after removing all the INotifyPropertyChanged
code, but there are many ways to control the functionality from outside the instance.
Please note that I wrote all this code in the text editor, not in VisualStudio; it has not been tested in any way.
Add a method to enable notifications:
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");
}
}
}
You could do the same thing without the method, if you knew at the time of instantiation whether or not the instance should produce notifications, in which case you'd just need a boolean parameter in the constructor.
Another variation would be to use the Factory pattern, where your Factory has internal access to the boolean flag and sets it upon construction.
Encapsulate the condition in a proxy:
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;
}
}
}
Here your consuming classes get the entity proxy instead of the entity itself (but is none the wiser because it references only IEntity
when you program to interfaces/abstractions). The wrapping of the proxy can happen in a factory or through an IoC container/DI framework.
The main advantage to this approach is that your entity maintains a pure INotifyPropertyChanged
implementation, and the conditional aspect is handled from without. Another advantage is that it helps to enforce programming to abstractions and inversion of control.
The main disadvantage is that you'll need to create proxies for each INotifyPropertyChanged
implementation that you want to have this conditional behaviour.
Create a registry to keep track of what instances should or should not raise notifications:
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
}
}
Now, the registry has a distinct shortcoming in that it holds references to every instance and will prevent those instances from being picked up by the garbage collector. There may be a solution to this by implementing the registry with WeakReference
s, but I'm not up-to-snuff on their usage to recommend a particular implementation.