WPF excessive PropertyChanged events - wpf

Typically in the property setter of an object we may want to raise a PropertyChanged event such as,
public event PropertyChangedEventHandler PropertyChanged;
protected void Notify(string property)
{
if (PropertyChanged != null)
{
PropertyChanged(this, new PropertyChangedEventArgs(property));
}
}
public string UserNote
{
get { return _userNote; }
set
{
_userNote = value;
Notify("UserNote");
}
}
In our existing code base I see instances where PropertyChangedEventArgs is being sent null in order to indicate that all properties of the object have changed. This seems inefficient and seems to lead to far more events being triggered than is needed. It also seems to causes issues where objects update each other in a circular fashion.
Is this ever a good practice?
A comment in the code tries to justify it ...
//The purpose of this method is to wire up clients of NotificationBase that are also
//NotificationBases to *their* clients. Consider the following classes:
public class ClassA : NotificationBase
{
public int Foo
{
get { return 123; }
set { Notify("Foo"); }
}
}
public class ClassB : NotificationBase
{
ClassA A = new ClassA();
public ClassB()
{
A.PropertyChanged += AllChanged;
}
public void SetFoo()
{
A.Foo = 456;
}
}
public class ClassC
{
ClassB B = new ClassB();
public ClassC()
{
B.PropertyChanged += delegate { dosomething(); };
B.SetFoo(); // causes "dosomething" above to be called
}
}
/// ClassB.SetFoo calls ClassA.Foo's setter, which calls ClassA.Notify("Foo").
/// The event registration in ClassB's ctor causes ClassB.AllChanged to be called, which calls
/// ClassB.Notify(null) - implying that ALL of ClassB's properties have changed.
/// The event registration in ClassC's ctor causes the "dosomething" delegate to be called.
/// So a Notify in ClassA is routed to ClassC via ClassB's PropertyChanged event.
protected void AllChanged(Object sender, PropertyChangedEventArgs e)
{
Notify(null);
}
Any thoughts much appreciated.
Regards,
Fzzy

This is actually a problem with the design (or its documentation) of PropertyChangedEventArgs. Setting PropertyName to null means "all properties on this object have changed." But unless the class is sealed, or you're using reflection, you can't actually know that all properties on the object have changed. The most you can say is that all of the properties in the object's base class have changed.
This is reason enough to not use this particular convention, in my book, except in the vanishingly small number of cases where I create sealed classes that implement property-change notification.
As a practical matter, what you're really trying to do is just raise one event that tells listeners "a whole bunch of properties on this object have changed, but I'm not going to bother to tell you about them one by one." When you say:
I see instances where PropertyChangedEventArgs is being sent null in order to indicate that all properties of the object have changed. This seems inefficient and seems to lead to far more events being triggered than is needed.
...the actual intent is the exact opposite. If a method changes the Foo, Bar, Baz, and Bat properties on an object, and the object has only four or five properties, raising one event is probably better than raising four. On the other hand, if the object has sixty properties, raising four events is probably better making every one of the object's listeners - even those that aren't looking at those four properties - do whatever they do when the properties that they care about change, because those properties didn't.
The problem is that the property-change notification system, as designed, isn't a fine-grained enough tool for every single job. It's designed to be completely generic, and has no knowledge of a particular application domain built into it.
And that, it seems to me, is what's missing from your design: application domain knowledge.
In your second example, if a Fixture object has (say) ten properties that depend on the value of FixtureStatus, raising ten property-change events may seem a little excessive. Maybe it is. Maybe the object should raise a FixtureStatusChanged event instead. Then classes with knowledge of your application domain can listen to this one event and ignore the PropertyChanged event. (You still raise the PropertyChanged event on the other properties, so that objects that don't know what a FixtureStatusChanged event means can stay current - that is, if it's still necessary for your class to implement INotifyPropertyChanged once you've implemented FixtureStatusChanged.)
A secondary comment: Most classes in the C# universe, if they implement a method that raises the Foo event, call that method OnFoo. This is an important convention: it makes the relationship between the method and the event explicit, and it makes the fact that the code that's calling the method is raising an event easy to recognize. Notify is a weak name for a method in general - notify who? of what? - and in this case it actually obfuscates something that should be made explicit. Property-change notification is tricky enough without your naming convention concealing the fact that it's happening.

Ignoring the other stuff, I'd say the Notify(null) alone is a bad practice. It's not inherently clear what that means, and to a developer working the code 5 years down the line would probably assume that it meant something else unless they happened upon the comments.

I have come across situations wherein computed properties (without setters) need to fire PropertyChangeNotification when some other property i set via a setter.
eg
double Number
{
get { return num;}
set
{
num=value;
OnPropertyChanged("Number");
OnPropertyChanged("TwiceNumber");
}
}
double TwiceNumber
{
get {return _num * 2.0;}
}
As a rule I only do it with get only properties and I don't see why in this case a property firing a change notification on the other is bad. But I think if I do it for any other case I most likely don't know what I am doing!

Related

List of events in Silverlight?

I have 3 Controls - ContractSelection, ContractInfo and ContractClips
I have an additional class - ContractStructureService. This contains a property - SelectedContract
The contract selection control displays a list of contracts to the user in a combo box
When the user selects a contract, the SelectedContract on the service is changed.
I know how to do all that - the tricky bit is that I want both ContractInfo and ContractClips to be told when the selected contract has been changed
public class FlexContractStructureService : IFlexContractStructureService
{
#region IFlexContractStructureService Members
private Contract _selectedContract;
public ViewModels.Contract SelectedContract
{
get { return _selectedContract; }
set
{
_selectedContract = value;
OnSelectedContractChanged(new SelectedContractChangedEventArgs(SelectedContract));
}
}
public event EventHandler SelectedContractChanged;
protected virtual void OnSelectedContractChanged(SelectedContractChangedEventArgs e)
{
if (SelectedContractChanged != null)
{
SelectedContractChanged(this, e);
}
}
How do I do this with Silverlight?
I would assume that if I have code in both ContractInfo and ContractClipInfo like
service.SelectedContractChanged += ContractChanged
Wouldn't this mean that when I call OnSelectedContract changed, only 1 object is notified?
Paul
There are several questions rolled into one here.
For the last part, an event is meant to be multicast, ie to be able to notify a list of delegates. So, no, not only 1 object notified.
Now, for the main question, in Silverlight, you have several options, depending on what you're already using:
You can use events and delegates as you propose, if you can get direct references to the relevant objects. This causes a coupling that might not be desirable, and also, poses a leak/delegate spam risk. What this mean is: you have to be very careful to unsubscribe each delegate that was added to an event.
You can use a non-coupling communication pattern, for instance a Messenger (MVVM Light), or something similar (broadcaster/subscribers). You have a point of broadcast here: OnSelectedContractChanged where you can send a notification (in MVVM Light again: Messenger.Default.Send(new SelectedContractChangedNotification(_selectedContract));). This notification can be received wherever you need elsewhere in your application (Messenger.Default.Register<SelectedContractChangedNotification>(this, OnSelectedContractChanged);) with as many receivers as you need.

Using a Non-Anemic Domain Model with Wpf MVVM

I am implementing a WPF based application using MVVMfor the UI.
I have a ViewModel that wraps each editable Model that can be edited. The VM contains all the logic for handling error notifications, "is dirty" management and so forth ..
This design supports well CRUD schenarios for simple domain Model objects that are anemic, that is, do not contain any logic.
Now, I am facing a more tricky problem cause I have a domain Model that contains logic and that logic can change the internal state of the domain Model.
Do someone have already faced this scenario ? If so, do you have some advices to handle this correctly ?
Riana
Here is how I usually deal with it:
The ViewModel layer is made of types that belong to this layer, meaning I don't ever directly use my business objects inside of a ViewModel. I map my business objects to ViewModel objects that may or may not be the exact same shape minus the behaviors. It can be argued that this violates Don't Repeat Yourself, but doing so allows you to adhere to the Single Responsibility Principle. In my opinion, SRP should usually trump DRY. The ViewModel exists to serve the view, and the model exists to serve business rules / behavior.
I create a facade/service layer that takes and returns ViewModels as arguments, but maps the ViewModels to-and-from their corresponding business object versions. This, way the non-anemic objects won't impose non view logic on the ViewModel
The dependencies would look like this:
ViewModel <--> Facade/ServiceLayer --> Business Objects
I think it is important to keep this in mind if you want to unleash the full potential of MVVM: The ViewModel is the model/abstraction of the view, not the model presented to the view.
Try using Command pattern. Your screen should be design not to edit an entity but to perform an action (command) on an entity. If you follow that principle when designing your screens, your ViewModel will have properties that should be mapped to a command object. Then, the command will be send to an (remote) facade of the domain model.
ViewModels for displaying the data could be mapped directly to the database (bypassing the domain model altogether) so that you don't need to put nasty getters in the domain model classes.
If the domain model is non-anemic, you will need to use events to communicate internal changes in the Model back to the ViewModel. That way you don't have to worry about keeping track of what operations could potentially make your VM out-of-sync with the model.
Here's a simple example:
First, a sample model:
public class NonAnemicModel
{
private string _name;
public string Name
{
get { return _name; }
set
{
if (_name == value)
return;
_name = value;
OnNameChanged(EventArgs.Empty);
}
}
public event EventHandler NameChanged;
protected virtual void OnNameChanged(EventArgs e)
{
if (NameChanged != null)
NameChanged(this, e);
}
public void PerformNameCalculation(int chars)
{
//example of a complex logic that inadvertently changes the name
this.Name = new String('Z', chars); //makes a name of Z's
}
}
And here's a sample ViewModel:
public class MyViewModel : INotifyPropertyChanged
{
private NonAnemicModel _model;
public NonAnemicModel Model
{
get { return _model; }
set
{
_model = value;
_model.NameChanged += (sender, args) => NotifyPropertyChanged("UserName");
}
}
public string UserName
{
get { return this.Model.Name; }
set { this.Model.Name = value; }
}
//this command would call out to the PerformNameCalculation method on the Model.
public ICommand PerformNameCalculation { get; private set; }
}
Notice that the PropertyChanged event is raised when the Name on the model changes. That way, regardless of whether the UserName setter was used, or the PerformNameCalculation command was used, the ViewModel stays in sync. The big downside to this is that you have to add many more events to your Model, but I've found that having these events in place is usually very helpful in the long run. Just be careful about memory leaks with events!

Changetracking and concurrency - can this fail?

I've been messing around with Expressions - and I may have gone beyond my capabilities - but here goes... I've implemented 'type-safe' INotifyPropertyChanged implementation (an example is here), but gone a bit farther and included changetracking:
public abstract BaseViewModel<TEntity>:INotifyPropertyChanged
{
private readonly IBaseChangeTracker<TEntity> _changeTracker;
protected void OnPropertyChanged<T>(Expression<Func<T>> property, T value)
{
_changeTracker.AddChange(property, value);
OnPropertyChanged(property);
}
protected virtual void OnPropertyChanged<TProperty>(Expression<Func<TProperty>> property)
{
if (PropertyChanged == null) return;
PropertyChanged(this, new PropertyChangedEventArgs(property.GetMemberInfo().Name));
}
public event PropertyChangedEventHandler PropertyChanged;
}
public abstract class BaseChangeTracker<TEntity>:IBaseChangeTracker<TEntity>
{
private readonly IDictionary<Expression, object> _changes = new Dictionary<Expression, object>();
public void AddChange<T>(Expression<Func<T>> expression, T newValue)
{
_changes.Add(expression, newValue);
}
public void ApplyChanges(TEntity entity)
{
foreach (var change in _changes)
{
var property = typeof(TEntity).GetProperty(change.Key.GetMemberInfo().Name);
property.SetValue(entity, change.Value, null);
}
}
public virtual void CopyCurrentState(TEntity entity)
{
_changes.Clear();
}
public virtual void ResetEntity(TEntity entity)
{
_changes.Clear();
}
public bool HasUnsavedChanges
{
get { return _changes.Any(); }
}
}
This may seem a bit excessive - each entity will have it it's own ChangeTracker keeping the original state of the entity when loaded and can reset it back to these, but the idea is that if there is a concurrency conflict when it tries to save the updated entity, I reload the entity from the database and run it through .ApplyChanges and try to save it again. This will remove about 95% of my concurrency problems... if it works. My tests show that for limited entities it works, but that is with simple property-changes.
Known issue:
I have yet to find an elegant way of handling collections.
What else am I missing - or are there obvious flaws in my design?
I realize that this is not a direct answer to your question, but you might consider using the Command Pattern to implement an undo/redo stack.
Encapsulating changes in commands is a very tidy way to cycle/re-cycle through changes, with the added benefits of (1) a nice feature that adds value to the application, (2) you can wrap many actions in any given command, like raising event change notifications for databinding support in both the do and undo directions.
Additionally, managing collection changes is no more or less challenging than simple property updates.
Specific to the code you posted, the OnPropertyChanged implementation will never raise the PropertyChanged event because you call return after the if() statement and then again in bare brackets (these do not correspond to the if condition).
if (PropertyChanged == null) return; // this returns based on if
{
return; // this returns no matter what
}
Additionally, it seems that the user won't ever see any changes in the UI. The values aren't updated until ApplyChanges is called, and when it is there is no PropertyChanged event. (I might not be following your code correctly, but just looking over it this seems to be the case).

EntityFramework EntityState and databinding along with INotifyPropertyChanged

I have a WPF view that displays a Shipment entity. I have a textblock that contains an asterisk which will alert a user that the record is changed but unsaved. I originally hoped to bind the visibility of this (with converter) to the Shipment.EntityState property.
If value = EntityState.Modified Then
Return Visibility.Visible
Else
Return Visibility.Collapsed
End If
The property gets updated just fine, but the view is ignorant of the change. What I need to know is, how can I get the UI to receive notification of the property change. If this cannot be done, is there a good way of writing my own IsDirty property that handles editing retractions (i.e. if I change the value of a property, then change it back to it's original it does not get counted as an edit, and state remains Unchanged).
Any help, as always, will be greatly appreciated.
Cory
After struggling with the same problem for a little bit, here is a solution that is working for me.
Lets say I have an entity called Trip that was generated by EF, I just needed to extend the class by means of partial class as showed below. The RaiseEntityStateChanged method is useful when you need to force a refresh of the EntytyState property, for example after calling the context's SaveChanges method.
partial class Trip
{
bool _forced = false;
System.Data.EntityState _lastState;
public Trip()
{
_lastState = EntityState;
this.PropertyChanged += (s, e) =>
{
if (_lastState != this.EntityState && e.PropertyName != "EntityState" || _forced)
{
_forced = false;
OnPropertyChanged("EntityState");
}
_lastState = this.EntityState;
};
}
public virtual void RaiseEntityStateChanged()
{
_forced = true;
OnPropertyChanged("EntityState");
}
}
I don't see a way to create a XAML binding on an existing property to do what you are trying to do. But you could write your own IsDirty property, based on the EntityState; you could update this value by subscribing to the PropertyChanged event raised by the base EntityObject. Of course, you'll need to also raise a PropertyChanged event for IsDirty (so that the GUI is notified) and ignore this event in your handler (to prevent infinite recursion).
Edit: added the following after question by OP:
This is how I see it, in order to answer the comment.
In the shipment class, one can add:
public bool IsDirty { get { return EntityState == EntityState.Modified; } }
public Shipment() {
...
PropertyChanged += OnShipmentChanged;
}
private void OnShipmentChanged(object sender, PropertyChangedEventArgs pcea) {
if (pcea.PropertyName != "IsDirty") { // prevent recursion
OnPropertyChanged("IsDirty"); // notifies binding listener that the state has changed
}
}
During the night, I thought of another way, which is to create a multi-binding on each Shipment property (which would replace this whole notion of an IsDirty property and would actually answer the original question). This could make sense if there are just a couple of Shipment properties. I'd say if there are more than 3, we should forget about this idea.

.net propertychange notification handlers - strings vs. expressions

Using WPF has made me a fan of INotifyPropertyChanged. I like to use a helper that takes an expression and returns the name as a string (see example code below). In lots of applications I see by very proficient programmers, however, I see code that handles the strings raw (see 2nd example below). By proficient I mean MVP types who know how to use Expressions.
To me, the opportunity for having the compiler catch mistakes in addition to easy refactoring makes the Exression approach better. Is there an argument in favor of using raw strings that I am missing?
Cheers,
Berryl
Expression helper example:
public static string GetPropertyName<T>(Expression<Func<T, object>> propertyExpression)
{
Check.RequireNotNull<object>(propertyExpression, "propertyExpression");
switch (propertyExpression.Body.NodeType)
{
case ExpressionType.MemberAccess:
return (propertyExpression.Body as MemberExpression).Member.Name;
case ExpressionType.Convert:
return ((propertyExpression.Body as UnaryExpression).Operand as MemberExpression).Member.Name;
}
var msg = string.Format("Expression NodeType: '{0}' does not refer to a property and is therefore not supported",
propertyExpression.Body.NodeType);
Check.Require(false, msg);
throw new InvalidOperationException(msg);
}
Raw strings example code (in some ViewModelBase type class):
/// <summary>
/// Warns the developer if this object does not have
/// a public property with the specified name. This
/// method does not exist in a Release build.
/// </summary>
[Conditional("DEBUG"), DebuggerStepThrough]
public void VerifyPropertyName(string propertyName) {
// Verify that the property name matches a real,
// public, instance property on this object.
if (TypeDescriptor.GetProperties(this)[propertyName] == null) {
string msg = "Invalid property name: " + propertyName;
if (ThrowOnInvalidPropertyName) throw new Exception(msg);
else Debug.Fail(msg);
}
}
/// <summary>
/// Returns whether an exception is thrown, or if a Debug.Fail() is used
/// when an invalid property name is passed to the VerifyPropertyName method.
/// The default value is false, but subclasses used by unit tests might
/// override this property's getter to return true.
/// </summary>
protected virtual bool ThrowOnInvalidPropertyName { get; private set; }
To me, the opportunity for having the compiler catch mistakes in addition to easy refactoring makes the Exression approach better. Is there an argument in favor of using raw strings that I am missing?
I agree, and personally, use the expression approach in my own code, in most cases.
However, there are two reasons I know of to avoid this:
It is less obvious, especially to less experienced .NET developers. Writing RaisePropertyChanged(() => this.MyProperty ); is not always as obvious to people as RaisePropertyChanged("MyProperty");, and doesn't match framework samples, etc.
There is some performance overhead to using expressions. Personally, I don't feel this is really that meaningful of a reason, since this is usually used in data binding scenarios (which are already slow due to reflection usage), but it is potentially a valid concern.
The benefit of using the TypeDescriptor approach is that it enables dynamic property scenarios based on ICustomTypeDescriptor implementations where the implementation can effectively create dynamic property metadata on the fly for a type that is being described. Consider a DataSet whose "properties" are determined by the result set it is populated with for example.
This is something that expressions does not provide however because it's based on actual type information (a good thing) as opposed to strings.
I wound up spending more time on this than I expected, but did find a solution that has a nice mix of safety/refactorability and performance. Good background reading and an alternate solution using reflection is here. I like Sacha Barber's solution even better (background here.
The idea is to use an expression helper for a property that will participate in change notification, but only take the hit once for it, by storing the resulting PropertyChangedEventArgs in your view model. For example:
private static PropertyChangedEventArgs mobilePhoneNumberChangeArgs =
ObservableHelper.CreateArgs<CustomerModel>(x => x.MobilePhoneNumber);
HTH,
Berryl
Stack walk is slow and lambda expression is even slower. We have solution similar to well known lambda expression but almost as fast as string literal. See
http://zamboch.blogspot.com/2011/03/raising-property-changed-fast-and-safe.html
A CallerMemberName attribute was introduced in .net 4.5
This attribute can only be attached to optional string parameters and if the parameter is not used by caller in the function call then name of the caller will be passed in the string parameter
This removes the need to specify name of property when raising the PropertyChanged event thus it works with refactoring and because the changes are done at compile time there's no difference in performance.
Below is an example of implementation and more info can be found at http://msdn.microsoft.com/en-us/library/system.runtime.compilerservices.callermembernameattribute.aspx and http://msdn.microsoft.com/en-us/library/hh534540.aspx
public class DemoCustomer : INotifyPropertyChanged
{
string _customerName
public string CustomerName
{
get { return _customerNameValue;}
set
{
if (value != _customerNameValue)
{
_customerNameValue = value;
NotifyPropertyChanged();
}
}
}
public event PropertyChangedEventHandler PropertyChanged;
private void NotifyPropertyChanged([CallerMemberName] String propertyName = "")
{
if (PropertyChanged != null)
{
PropertyChanged(this, new PropertyChangedEventArgs(propertyName));
}
}
}

Resources