An easier way to manage INotifyPropertyChanged
If you are working with WPF or Silverlight view models, then you know that one of the more annoying things to deal with is implementing INotifyPropertyChanged to support refreshing the UI when the model is changed.
At some point, I got tried of that and wrote this:
public class Observable<T> : INotifyPropertyChanged { private T value; public Observable() { } public Observable(T value) { this.value = value; } public T Value { get{ return value;} set { this.value = value; PropertyChanged(this, new PropertyChangedEventArgs("Value")); } } public static implicit operator T(Observable<T> val) { return val.value; } public event PropertyChangedEventHandler PropertyChanged = delegate { }; }
This doesn’t seem to be very interesting, right? But it has some nice properties. Here is an example of a view model making use of this.
public class Model : IPagingInformation { public Observable<bool> AllowEditing { get; set; } public Observable<int> NumberOfPages { get; set; } public Observable<int> CurrentPage { get; set; } }
One of the nice things about the Observable class is that it is make it easy to work with the stored values. For example:
Model.AllowEditing.Value = !Model.AllowEditing; Model.CurrentPage.Value = Model.CurrentPage +1;
In other words, you can treat the object as if it wasn’t a wrapper for reading purposes. I could make it both ways, but then I would have no way of maintaining the same instance for updates. Overall, I found the it made it quite a bit easier to deal with the task of UI updates.
Comments
Any reason you are not using DependencyObject and DependencyProperty? It is a lot nicer for WPF view models.
Hi, nice one.
Some might argue that for each property of an object there are extra M small objects allocated. But the advantages are clear.
Another idea would be tot store the PropertyChangedEventArgs in a static member and not to create it at each invocation:
private static PropertyChangedEventArgs args = new PropertyChangedEventArgs("Value");
Thinking outside the box pays off I'd say - smart solution :)
The default VS2008 WCF service proxy generator already takes care of the INotifyPropertyChanged on your model objects at least for Silverlight.
I am thinking to augment this so it will also add the IEditableObject like I think the RIA Services does.
Also worth checking out PostSharp ( www.postsharp.org) for a solution to this.
Liviu,
You wouldn't be able to measure that in any meaningful way
Jonathon,
I really hate all the craft that you need to get it working
Yea postsharp is a potential, more and more recently I've been thinking about what I think is one of the lacking areas in '.NET', and thats where we don't have really any control at compilation time (sure we can do pre-processors etc but thats not exactly simple)..
There are tons of places we end up pushing complex work to runtime that could well be evaluated once at compile time, I really think .NET needs a built in system that runs after compilation to IL, and can run transforms.. just as postsharp is allowing.
What about validation?
In WPF it's convinient to run validation in properties. When validation fails exception is thrown and 'ValidatesOnExceptions=True' in binding allows to visually accent wrong field and show tooltip with explanation.
How do you propose to handle validation?
Fallen,
I don't as of yet. I'll deal with that if/when I run into that scenario
And how would you implement validation if it is needed? Extend Observable with 'void Validation( string propertyName )'?
Here's an even easier way. Don't! http://updatecontrols.net
Update Controls automatically discovers dependencies among your properties and updates your WPF, Silverlight, and even Winforms controls.
The problem with INotifyPropertyChanged is not firing events. It's being the middle man. The View Model layer is hard to write because you have to respond to changes in the Data Model, update yourself, and forward events to the View. But when you use Update Controls, there is no data binding code in the View Model at all.
How can you make the wrapper both ways?
I don't understand the question
Sorry, you said "you can treat the object as if it wasn’t a wrapper for reading purposes. I could make it both ways" I understand how this works for reading, but I didn't know you could do the same for writing values to the .Value property.
F.ex: AllowEditing = true would work than?
-Mark
You might want to pick a name other than Observable <t since there actually will be an Observable <t in .net 4 which does something very different.
themechanicalbride.blogspot.com/.../...events.html>
If you haven't seen this yet you should definitely check it out, very brain bending and revelatory. My favorite quote:
"To put things in perspective it’s been 13 years since Design Patterns was published and we’ve only now realized that the Observable pattern and the Iterator pattern are actually the same pattern."
I'm regular reader of your blog but for this one, I don't agree with you. I prefer to have the actual type (e.g. int PersonID ) instead of this (e.g. Observe <int PersonID ). I feel like it makes my Model look ugly. Sorry.
First, I don't think I would ever use something like this. I don't mind the extra work, and using the wrapper classes seems like just as much extra work anyway. You've also introduced .Value all over your usages (no implicit conversion on assignment).
That being said, why did you choose to use a class instead of a struct? I would actually expect to keep value semantics versus reference semantics. It seems very odd to wrap up an integer as a class to me. You also have potential performance improvements (thought doubtful to matter in 99% of the applications out there) considering that many view model type objects contain a significant number of value types.
Mark,
Both ways meaning implementing two way implicit conversion operators.
The problem is that it would generate different instances.
Michael,
Make a distinction between a model and view model
You wouldn't believe how annoying I find the lost instance issue with the reverse implicit operator :)
Implicit operators can be called with null so you should put a check there to return null or you may end up with null reference errors acessing .value.
[)amien
Ayende,
The distinction I draw between Model (or more precisely Data Model) and View Model is this. A Data Model is persistent. A View Model is dependent.
What you've proposed here works for the Data Model because it has storage. But it does not work for a View Model. A View Model has no storage; all of its properties are calculations that bring together properties of the Data Model.
For example:
class ContactListViewModel
{
public string Title
{
}
}
Navigation and Person both persist data (Navigation only temporarily, but Person persistently). ContactListViewModel stores nothing. It's properties are dependent on others.
You can also pick Obtics ( http://obtics.codeplex.com/), and you'll get real-time updates for calculated values (including Linq). I really love real-time Linq with WPF -- it completely removes the need to think what changed when and what was affected.
I have done this for our WPF based projects for ~1 year. I have been calling this PropertyModel. We have also included other presentation specific per-property metadata in our property model types.
IPropertyModel <t : INotifyPropertyChanged {
T Value
bool IsEditable
string CustomEditorName
void SignalChanged()
...etc
}
This has made many of our binding scenarios much cleaner. We have also made a series of policies that toggle propertymodel settings. This has given us generally more reuse of common screens. For example our New and Edit screens are the same. The edit screens let us flip certain IsEditable values to false and so on.
'Serial related properties' often manifest in classes like:
class X {
Foo a;
bool isADirty;
bool wasAUserEntered;
}
This initial smell + the ugly repetitive INotifyPropertyChanged business helped us to normalise our property related data into 1 PropertyModel.
PropertyModel + ViewModel + CommandModels (ala Dan Crevier) is our essential WPF model.
-Chris-
Ayende nice code.
Chris Donnan how to do some additional validation on properties then?
Thanks
Michael,
Then we don't share the same definition at all.
The model in the post is my view model.
Radenko;
Validation is a separate concern. We have CommandModels ala this blogs.msdn.com/.../756095.aspx
Each CommandModel has a Validator on it. Each validator is essentially a Func <t,validationresult> where the result has potential messages and a bool on it. This lets us compose validations together using a CompositeValidator (list of those Funcs basically). Each command is bound to the UI, we bind all our validators to enable the commands and to show messages (eg; we put tooltips over disabled buttons with the errors etc).
That is about it.
Chris
Ayende,
Fair enough. Could you demonstrate then how you would update your view model from the data model? For example, set AllowEditing when certain conditions are true?
The reason I define a view model to have no storage is because it allows you to write what AllowEditing means in just one place: the getter. If it has storage, you have to set that storage from someplace else, usually from a property changed event handler. That's seems like extra work to me. I'm curious to see how you solve that.
Thanks.
Micahel,
In about 12 hours there is going to be a post about Conditions and Facts, take a look at this
"Jonathon,
I really hate all the craft that you need to get it working"
I would argue that there is more craft here. Also Dependency Properties are implemented in a way which actually makes them perform better in bindings because the framework doesn't have to use reflection.
Here's another alternative http://www.codeplex.com/ActiveSharp
Properties have their real types (no .Value) .
Comment preview