How can we implement change notification propagation for WPF and SL in the MVVM pattern?
Here's an example scenario targetting MVVM WPF/SL development:
- View data binds to view model Property "Target"
- "Target" exposes a field of an object called "data" that exists in the local application model, called "Original"
- when "Original" changes, it should raise notification to the view model and then propogate that change notification to the View.
Here are the solutions I've come up with, but I don't like any of them all that much. I'm looking for other ideas, by the time we come up with something rock solid I'm certain Microsoft will have released .NET 5 with WPF/SL extensions for better tools for MVVM development.
For now the question is, "What have you done to solve this problem and how has it worked out for you?"
Option 1.
Proposal:
Attach a handler to data's PropertyChanged event that watches for string values of properties it cares about that might have changed, and raises the appropriate notification.
Why I don't like it:
Changes don't bubble naturally, objects must be explicitly watched, if data changes to a new source, events must be un-registered/registered.
Why I kind of like it:
I get explicit control over propogation of changes, and I don't have to use any types that belong at a higher level of the application such as dependancy properties.
Option 2.
Proposal:
Attach a handler to data's PropertyChanged event that re-raises the event across all properties using the name property name.
Why I don't like it:
This is essentially the same as option 1, but less intelligent, and forces me to never change my property names, as they have to be the same as the property names on data
Why I kind of like it:
It's very easy to set up and I don't have to think about it... Then again if I try to think, and change names to things that make sense, I shoot myself in the foot, and then I have to think about it!
Option 3.
Proposal:
Inheri开发者_C百科t my view model from dependancy object, and notify binding sources of changes directly.
Why I don't like it:
I'm not even 100% sure dependancy properties/objects can DO this, it was just a thought to look into. Also I don't personally feel that WPF/SL types like Dep Obj belong at the view model level.
Why I kind of like it:
IF it has the capability that I'm seeking then it's a good answer! minus that pesky layering issue.
Option 4.
Proposal:
Use a consistant agent messaging system based off of Task Parallels DataFlow Library to propogate everything through linked pipelining.
Why I don't like it:
I've never tried this, and somehow I think it will be lacking, plus it requires me to think about my code completely differently all the way around.
Why I kind of like it:
It has the possiblity of allowing me to do some VERY fun manipulations with a push based data model and using ActionBlocks as validation AND setters to then privately change view model properties and explicitly control PropertyChanged notifications.
I've basically been using Option 4. When the state of the application changes (and therefore, data in the model changes), I send out a notification using the MVVM Foundation Messenger
. ViewModels that need this data for databinding can then subscribe to this message, update local properties accordingly, and call RaisePropertyChanged()
* to notify the binding system that a change has occurred.
Yes, this is a different way of thinking about things, but I think it's worth it. It keeps your ViewModels very loosely coupled--they don't depend on your application's model nor the Views they support.
The biggest downside I've noticed is that debugging code that makes heavy use of the Messenger
is more difficult. Basically, when an exception gets thrown, you get taken to a method in the Messenger
class instead of the actual offending code. I think this is because because Messenger
makes use of weak references. You learn to work around this problem, but it's definitely not ideal.
In a recent app I was working on, I decided to centralize all changes to application data in one large but simple class called AppDataManager
. This class has fields for all the data that needs to be change tracked. After some initialization at startup, data changes are always or almost always initiated by the user. Thanks to two-way data binding, a user actions results in change to a property in a ViewModel. This change is then forwarded to the AppDataManager
using a message. The AppDataManager
then changes it's own reference to this data and sends out a message to notify any interested ViewModels that this data has changed. I usually include the changed data in the message (so the receiver doesn't have to know where to find the data). I'm not sure this is the best architecture, and I don't know how it would scale for large apps, but it seems to have worked well for me so far.
Obviously, an alternative approach to centralization would be a network approach where ViewModels subscribe directly to each other's messages. This very well might work, but I haven't tried it. I found that I could implement an undo system a lot more easily if all changes to the data were routed through one class. My data was also hierarchically arranged, so changes at the top of the hierarchy would need to cascade down to the "leaves" (e.g., if you change the map, you have to change the roads, and if you change the roads, you have to change the waypoints making up the roads). I found it much easier to manage this cascading centrally than choose an arbitrary ViewModel to do it.
*RaisePropertyChanged
is a member of ObservableObject
, which is also part of the MVVM Foundation. All ViewModels in my project inherit from ObservableObject
.
Note: I'm thinking of switching from MVVM Foundation to the MVVM Light Toolkit, which seems to be a further development of the MVVM Foundation.
精彩评论