MVVM: Transfering data from ViewModel to Model, when and how ? [modified]
-
Jean-Louis Leroy wrote: Ok, let's imagine that we rewrite Notepad the MVVM way. We have a Document object with a string property - say Content. And a DocumentViewModel that holds a reference to a Document. It has a a string Content property too, which is loaded from the underlying Document. And then a DocumentView with a text box that binds to the DocumentViewModel's Content. I know I could bind to Document.Content directly but I've beaten that horse in my original post already. Am I on the right track here ? --- No, that is exactly what you do *NOT* do. In your example, you have DocumentViewModel that holds a reference to a Document(Model). Document(Model) has a string Content property (the content of the text file). So far, so good. The next step is where you head off the reservation a bit :). DocumentViewModel should DEFINITELY NOT have its own string Content property. It should simply *wrap* the one from the Document(Model). I.e. DocumentViewModel.cs: public string Content { get { return _document.Content; } set { if (_document.Content != value) { _document.Content = value; OnPropertyChanged("Content"); } } } The reasons for this are two-fold: 1) you don't have to worry about keeping the VM and M in sync, since they are always by design in sync 2) you are not storing the same data in multiple places. Imagine if the text file is 100 bytes... no biggie right? your design uses up 200 bytes of memory. No biggie. Now imagine if the text file is 1MB or 10MB or 100MB... with your design, you are using up 2MB, 20MB or 200MB of memory. Not good. Rule #1 in good software design, ESPECIALLY in WPF & MVVM... is that you should only have one instance of an object in memory at all times. The reason why its more important in WPF is that imagine this scenario: SomeObject (inst 1) ... somebody binds to it ... all good... anytime SomeObject (inst 1) is modified, the UI is updated Now imagine that somebody went and made a COPY of SomeObject and now you have SomeObject (inst 2)... changes made to one will not update the properties in the other and data binding will be broken :).
(quotes no in the same order as in the original text)
SledgeHammer01 wrote:
DocumentViewModel should DEFINITELY NOT have its own string Content property. It should simply *wrap* the one from the Document(Model).
Here I stumble upon a difficulty. Consider a Person that has a birth date stored in a DateTime. Obviously we don't want to present that in the VM. Instead we want to map the
double Person.BirthDate
to a text input field in the view - or three text boxes for day, month and year, or some Calendar control. So what's the VM between View and Model going to look like ? Does it contain one (or three) string properties ? Hmm, maybe something like this ?public string Day { get { return \_person.BirthDate.Day.ToString(); } set { d = \_person.BirthDate.AddDays(int.Parse(value)).AddDays(-\_person.BirthDate.Day); } } public string Month { get { return \_person.BirthDate.Month.ToString(); } set { d = \_person.BirthDate.AddMonths(int.Parse(value)).AddMonths(-\_person.BirthDate.Month); } } public string Year { get { return \_person.BirthDate.Year.ToString(); } set { d = \_person.BirthDate.AddMonths(int.Parse(value)).AddMonths(-\_person.BirthDate.Year); } }
Or maybe a ValueConverter is used somewhere ? I haven't explored that yet...
SledgeHammer01 wrote:
Rule #1 in good software design, ESPECIALLY in WPF & MVVM... is that you should only have one instance of an object in memory at all times
I do see your point with having only one instance of an object in memory at all times...especially since I have implemented object-relational mappers like Perl's Tangram. If I have two views looking at the same Model at the same time, I'd want the changes made via one view to be reflected in the other view. Right ? And binding to the Model's property instead of copying them gives me just that. Or does it ? Back to my
Person.BirthDate
example, suppose that I have VM1 that exposes the birth date as a single string property (BirthDate) in yyyy/mm/dd format. When the View sets the VM's BirthDate property theModel.BirthDate
is updated. Now VM2 uses three string properties: Day, Month and Year. But changing the birth date via VM1 fires a "BirthDate" event that is significant only to the View that displays VM1. The change notifications are sent as changes to th -
(quotes no in the same order as in the original text)
SledgeHammer01 wrote:
DocumentViewModel should DEFINITELY NOT have its own string Content property. It should simply *wrap* the one from the Document(Model).
Here I stumble upon a difficulty. Consider a Person that has a birth date stored in a DateTime. Obviously we don't want to present that in the VM. Instead we want to map the
double Person.BirthDate
to a text input field in the view - or three text boxes for day, month and year, or some Calendar control. So what's the VM between View and Model going to look like ? Does it contain one (or three) string properties ? Hmm, maybe something like this ?public string Day { get { return \_person.BirthDate.Day.ToString(); } set { d = \_person.BirthDate.AddDays(int.Parse(value)).AddDays(-\_person.BirthDate.Day); } } public string Month { get { return \_person.BirthDate.Month.ToString(); } set { d = \_person.BirthDate.AddMonths(int.Parse(value)).AddMonths(-\_person.BirthDate.Month); } } public string Year { get { return \_person.BirthDate.Year.ToString(); } set { d = \_person.BirthDate.AddMonths(int.Parse(value)).AddMonths(-\_person.BirthDate.Year); } }
Or maybe a ValueConverter is used somewhere ? I haven't explored that yet...
SledgeHammer01 wrote:
Rule #1 in good software design, ESPECIALLY in WPF & MVVM... is that you should only have one instance of an object in memory at all times
I do see your point with having only one instance of an object in memory at all times...especially since I have implemented object-relational mappers like Perl's Tangram. If I have two views looking at the same Model at the same time, I'd want the changes made via one view to be reflected in the other view. Right ? And binding to the Model's property instead of copying them gives me just that. Or does it ? Back to my
Person.BirthDate
example, suppose that I have VM1 that exposes the birth date as a single string property (BirthDate) in yyyy/mm/dd format. When the View sets the VM's BirthDate property theModel.BirthDate
is updated. Now VM2 uses three string properties: Day, Month and Year. But changing the birth date via VM1 fires a "BirthDate" event that is significant only to the View that displays VM1. The change notifications are sent as changes to thJean-Louis Leroy wrote:
I want to support the New/Open/Save/SaveAs cycle for Reports [...] But how do I implement SaveAs ? By the time I know the end user wants to make a new object, the old one has already been messed up.
Thinking about it, one solution out of this problem is to drop the SaveAs command (posterior to changes) and replace it with a Duplicate command (prior changes). Like in Mac OS X Lion :laugh: Then what would Save mean ? Worse, what would it mean if a user changes the data, then never executes Save ? It seems to me that MVVM may end up curbing the way we design UIs. Another example, it discourages modal dialogs, or at least it makes them difficult to implement. I wonder if it's legitimate for a code pattern to have such impact on the end-user experience. Isn't it the tail that wags the dog ?
edit: grammar
-
Jean-Louis Leroy wrote: Ok, let's imagine that we rewrite Notepad the MVVM way. We have a Document object with a string property - say Content. And a DocumentViewModel that holds a reference to a Document. It has a a string Content property too, which is loaded from the underlying Document. And then a DocumentView with a text box that binds to the DocumentViewModel's Content. I know I could bind to Document.Content directly but I've beaten that horse in my original post already. Am I on the right track here ? --- No, that is exactly what you do *NOT* do. In your example, you have DocumentViewModel that holds a reference to a Document(Model). Document(Model) has a string Content property (the content of the text file). So far, so good. The next step is where you head off the reservation a bit :). DocumentViewModel should DEFINITELY NOT have its own string Content property. It should simply *wrap* the one from the Document(Model). I.e. DocumentViewModel.cs: public string Content { get { return _document.Content; } set { if (_document.Content != value) { _document.Content = value; OnPropertyChanged("Content"); } } } The reasons for this are two-fold: 1) you don't have to worry about keeping the VM and M in sync, since they are always by design in sync 2) you are not storing the same data in multiple places. Imagine if the text file is 100 bytes... no biggie right? your design uses up 200 bytes of memory. No biggie. Now imagine if the text file is 1MB or 10MB or 100MB... with your design, you are using up 2MB, 20MB or 200MB of memory. Not good. Rule #1 in good software design, ESPECIALLY in WPF & MVVM... is that you should only have one instance of an object in memory at all times. The reason why its more important in WPF is that imagine this scenario: SomeObject (inst 1) ... somebody binds to it ... all good... anytime SomeObject (inst 1) is modified, the UI is updated Now imagine that somebody went and made a COPY of SomeObject and now you have SomeObject (inst 2)... changes made to one will not update the properties in the other and data binding will be broken :).
Errm. Are you 100% sure about this design approach as an absolute? How do you handle validation in this case or support for IEditableObject? The reality is, a hybrid approach will generally be necessary. In the case where you want to simply forward a property on, then your approach is fine, but where you need to validate something before it can be committed, then you should use an intermediary value. Similarly, if changes can be rolled back then you should support an intermediary value. That's the approach to use. Take the case you mentioned - where you have a huge amount of text in a model, the VM would generally do some clever processing so that only a subset of the text was kept bound to the view, virtualized in other words, and the model would be updated with the subset. In this way, the VM simply presents a snapshot of some of the text at that point in time.
Forgive your enemies - it messes with their heads
My blog | My articles | MoXAML PowerToys | Mole 2010 - debugging made easier - my favourite utility
-
Jean-Louis Leroy wrote:
I want to support the New/Open/Save/SaveAs cycle for Reports [...] But how do I implement SaveAs ? By the time I know the end user wants to make a new object, the old one has already been messed up.
Thinking about it, one solution out of this problem is to drop the SaveAs command (posterior to changes) and replace it with a Duplicate command (prior changes). Like in Mac OS X Lion :laugh: Then what would Save mean ? Worse, what would it mean if a user changes the data, then never executes Save ? It seems to me that MVVM may end up curbing the way we design UIs. Another example, it discourages modal dialogs, or at least it makes them difficult to implement. I wonder if it's legitimate for a code pattern to have such impact on the end-user experience. Isn't it the tail that wags the dog ?
edit: grammar
Jean-Louis Leroy wrote:
it discourages modal dialogs, or at least it makes them difficult to implement.
Why do you think it makes modal dialogs difficult to implement? I've never found that to be the case.
Forgive your enemies - it messes with their heads
My blog | My articles | MoXAML PowerToys | Mole 2010 - debugging made easier - my favourite utility
-
Jean-Louis Leroy wrote:
it discourages modal dialogs, or at least it makes them difficult to implement.
Why do you think it makes modal dialogs difficult to implement? I've never found that to be the case.
Forgive your enemies - it messes with their heads
My blog | My articles | MoXAML PowerToys | Mole 2010 - debugging made easier - my favourite utility
Pete O'Hanlon wrote:
Why do you think it makes modal dialogs difficult to implement? I've never found that to be the case.
I suppose that many get their first exposure to MVVM via Josh Smith's article. But his sample app doesn't contain any modal dialogs. However, it's still one of the first things you need when implementing a real app. So when you turn to Google and search for "MVVM modal dialog" you get flooded with solutions falling roughly in five families: use a control; use a service; overlay a control on your view and disable its controls; what the heck, a bit of code-behind won't hurt; and finally, don't do modal, it's so nineties. It seems that no consensus has emerged on the issue yet (or I've missed it). Not that I care that much about consensus anyway, but, by design or by accident, it's a difficulty for newbies... What solution do you use ?
-
Pete O'Hanlon wrote:
Why do you think it makes modal dialogs difficult to implement? I've never found that to be the case.
I suppose that many get their first exposure to MVVM via Josh Smith's article. But his sample app doesn't contain any modal dialogs. However, it's still one of the first things you need when implementing a real app. So when you turn to Google and search for "MVVM modal dialog" you get flooded with solutions falling roughly in five families: use a control; use a service; overlay a control on your view and disable its controls; what the heck, a bit of code-behind won't hurt; and finally, don't do modal, it's so nineties. It seems that no consensus has emerged on the issue yet (or I've missed it). Not that I care that much about consensus anyway, but, by design or by accident, it's a difficulty for newbies... What solution do you use ?
Over time, I've really settled into using one of two methods (primarily because I contributed to the solutions on them). I originally used the features of Onyx[^] to do the dialogs, but I then moved to using MefedMVVM[^]. I use a modal dialog service that I grab a hold of in my particular VM, and use that to instantiate the dialog. The trick is to use an interface to represent what you want from the dialog, and inject a concrete implementation of this at runtime. It's all very testable, and easy to support.
Forgive your enemies - it messes with their heads
My blog | My articles | MoXAML PowerToys | Mole 2010 - debugging made easier - my favourite utility
-
(quotes no in the same order as in the original text)
SledgeHammer01 wrote:
DocumentViewModel should DEFINITELY NOT have its own string Content property. It should simply *wrap* the one from the Document(Model).
Here I stumble upon a difficulty. Consider a Person that has a birth date stored in a DateTime. Obviously we don't want to present that in the VM. Instead we want to map the
double Person.BirthDate
to a text input field in the view - or three text boxes for day, month and year, or some Calendar control. So what's the VM between View and Model going to look like ? Does it contain one (or three) string properties ? Hmm, maybe something like this ?public string Day { get { return \_person.BirthDate.Day.ToString(); } set { d = \_person.BirthDate.AddDays(int.Parse(value)).AddDays(-\_person.BirthDate.Day); } } public string Month { get { return \_person.BirthDate.Month.ToString(); } set { d = \_person.BirthDate.AddMonths(int.Parse(value)).AddMonths(-\_person.BirthDate.Month); } } public string Year { get { return \_person.BirthDate.Year.ToString(); } set { d = \_person.BirthDate.AddMonths(int.Parse(value)).AddMonths(-\_person.BirthDate.Year); } }
Or maybe a ValueConverter is used somewhere ? I haven't explored that yet...
SledgeHammer01 wrote:
Rule #1 in good software design, ESPECIALLY in WPF & MVVM... is that you should only have one instance of an object in memory at all times
I do see your point with having only one instance of an object in memory at all times...especially since I have implemented object-relational mappers like Perl's Tangram. If I have two views looking at the same Model at the same time, I'd want the changes made via one view to be reflected in the other view. Right ? And binding to the Model's property instead of copying them gives me just that. Or does it ? Back to my
Person.BirthDate
example, suppose that I have VM1 that exposes the birth date as a single string property (BirthDate) in yyyy/mm/dd format. When the View sets the VM's BirthDate property theModel.BirthDate
is updated. Now VM2 uses three string properties: Day, Month and Year. But changing the birth date via VM1 fires a "BirthDate" event that is significant only to the View that displays VM1. The change notifications are sent as changes to thJean-Louis Leroy wrote:
Here I stumble upon a difficulty. Consider a Person that has a birth date stored in a DateTime. Obviously we don't want to present that in the VM. Instead we want to map the
double Person.BirthDate
to a text input field in the view - or three text boxes for day, month and year, or some Calendar control. So what's the VM between View and Model going to look like ? Does it contain one (or three) string properties ?If you have a date and/or time, I would use a date and/or time picker and keep the value as a DateTime. Having 3 (or more) text boxes throws out a ton of functionality that you would have for free and kind of makes your app look "low budget" IMO. I do want to touch on something I said in my original response... IMO, the model should implement INPC so you CAN bind to properties in it directly. Imagine if you have 50 properties, it doesn't really make sense to wrap 50 properties just for the sake of wrapping them. Thats just a practice in typing. Unfortunately, thats not always possible. Web service proxies do not implement INPC, neither does native code, etc. There is absolutely nothing wrong with your VM exposing a Person property and your view binding to "Person.BirthDate".
Jean-Louis Leroy wrote:
Or does it ? Back to my
Person.BirthDate
example, suppose that I have VM1 that exposes the birth date as a single string property (BirthDate) in yyyy/mm/dd format. When the View sets the VM's BirthDate property theModel.BirthDate
is updated. Now VM2 uses three string properties: Day, Month and Year. But changing the birth date via VM1 fires a "BirthDate" event that is significant only to the View that displays VM1. The change notifications are sent as changes to the VM's properties, whereas (if memory serves) in Smalltalk MVC they are sent as updates to the Model's properties.A properly implemented model should handle this case. Updating Day, month, year should send an INPC for Day (or month or year) AND Date. Updating the Date property should send an INPC for day, month, year and Date.
-
Pete O'Hanlon wrote:
Why do you think it makes modal dialogs difficult to implement? I've never found that to be the case.
I suppose that many get their first exposure to MVVM via Josh Smith's article. But his sample app doesn't contain any modal dialogs. However, it's still one of the first things you need when implementing a real app. So when you turn to Google and search for "MVVM modal dialog" you get flooded with solutions falling roughly in five families: use a control; use a service; overlay a control on your view and disable its controls; what the heck, a bit of code-behind won't hurt; and finally, don't do modal, it's so nineties. It seems that no consensus has emerged on the issue yet (or I've missed it). Not that I care that much about consensus anyway, but, by design or by accident, it's a difficulty for newbies... What solution do you use ?
I don't see why people have problems with MVVM modal dialogs. There are many times when a modal dialog is perfectly appropriate. Yes, there are many, many different opinions on how they should be done. There is really only an issue if you plan to unit test your VM. The service solution usually works quite well and keeps the testability.
-
Errm. Are you 100% sure about this design approach as an absolute? How do you handle validation in this case or support for IEditableObject? The reality is, a hybrid approach will generally be necessary. In the case where you want to simply forward a property on, then your approach is fine, but where you need to validate something before it can be committed, then you should use an intermediary value. Similarly, if changes can be rolled back then you should support an intermediary value. That's the approach to use. Take the case you mentioned - where you have a huge amount of text in a model, the VM would generally do some clever processing so that only a subset of the text was kept bound to the view, virtualized in other words, and the model would be updated with the subset. In this way, the VM simply presents a snapshot of some of the text at that point in time.
Forgive your enemies - it messes with their heads
My blog | My articles | MoXAML PowerToys | Mole 2010 - debugging made easier - my favourite utility
The model should often times encapsulate all this functionality so it can be reused. Validation isn't an issue. You can handle that in the setter, or more appropriately have the model implement IDataErrorInfo.
-
The model should often times encapsulate all this functionality so it can be reused. Validation isn't an issue. You can handle that in the setter, or more appropriately have the model implement IDataErrorInfo.
Basically, with this approach, you have virtually completely defeated the whole purpose of MVVM. The point of putting this into the VM is that it is appropriate for the View and the ViewModel to interact, but you are delegating responsibility to the Model. What's the point of the VM anymore with this - it's purely acting to relay data in your approach. It's no longer adding any value.
Forgive your enemies - it messes with their heads
My blog | My articles | MoXAML PowerToys | Mole 2010 - debugging made easier - my favourite utility
-
Basically, with this approach, you have virtually completely defeated the whole purpose of MVVM. The point of putting this into the VM is that it is appropriate for the View and the ViewModel to interact, but you are delegating responsibility to the Model. What's the point of the VM anymore with this - it's purely acting to relay data in your approach. It's no longer adding any value.
Forgive your enemies - it messes with their heads
My blog | My articles | MoXAML PowerToys | Mole 2010 - debugging made easier - my favourite utility
I get your point, and agree to a certain extent, but also disagree to a certain extent :). Do you agree that the model should implement INPC? or do you believe that duty falls to the VM and that the model should just be a simple POCO? I think thats the models job. Why duplicate INPC code in every single VM that uses the model? If the model is already implementing INPC (and INCC), how is that any different from it implementing IEditableObject and IDataErrorInfo? I am not saying the model should handle everything, it most certainly should not. It should however encapsulate everything to do with an object so the code is centralized and not C&P'ed everywhere. The VM still has plenty to do: commands, occasional repacking of data, exposing non-model related properties, etc. The model doesn't really expose anything GUI related, thats the VMs job.
-
I don't see why people have problems with MVVM modal dialogs. There are many times when a modal dialog is perfectly appropriate. Yes, there are many, many different opinions on how they should be done. There is really only an issue if you plan to unit test your VM. The service solution usually works quite well and keeps the testability.
I agree the service approach is the way I do it, and I love it, works great.
Sacha Barber
- Microsoft Visual C# MVP 2008-2011
- Codeproject MVP 2008-2011
Open Source Projects
Cinch SL/WPF MVVM
Your best friend is you. I'm my best friend too. We share the same views, and hardly ever argue My Blog : sachabarber.net -
I get your point, and agree to a certain extent, but also disagree to a certain extent :). Do you agree that the model should implement INPC? or do you believe that duty falls to the VM and that the model should just be a simple POCO? I think thats the models job. Why duplicate INPC code in every single VM that uses the model? If the model is already implementing INPC (and INCC), how is that any different from it implementing IEditableObject and IDataErrorInfo? I am not saying the model should handle everything, it most certainly should not. It should however encapsulate everything to do with an object so the code is centralized and not C&P'ed everywhere. The VM still has plenty to do: commands, occasional repacking of data, exposing non-model related properties, etc. The model doesn't really expose anything GUI related, thats the VMs job.
That's fair enough. We do agree here then. My opinion was not that the VM should reimplement everything the model can do, just that there were certain occasions when it has to do extra work so you have to take it on a case by case basis.
Forgive your enemies - it messes with their heads
My blog | My articles | MoXAML PowerToys | Mole 2010 - debugging made easier - my favourite utility
-
I don't bind the view directly to model properties, and I don't want to change that. For starters, I don't like it. Moreover, in the general case it is not always possible. The ViewModel may contain properties that have no direct counterpart in the Model. Also my Model objects contain collections. So I would need to use ObservableCollections...but they're pulled from a Hibernate session so I don't even have control over the exact type of the collections.
Hmm. I would say that your business level entities coming from your data layer have no business at all in your GUI layer. It seems as if you do not have a layered application and that is your first problem. At the minimum, I'ld say a GUI layer, business layer and data layer is essential. Your mvvm pattern is found in your GUI layer and deals exclusively with GUI stuff. Use dto's to send data from and to your business. Your project will look like a bowl of spaghetti if you don't structure it properly.