|
I just solved it by myself:
I had to remove the dispatcher and use an Action<t> instead. That did the job
|
|
|
|
|
Hi folks!
Is it possible to define the targettype of a style to be an interface or an abstract class.
Something like this (in pseudo code)
Style s = new Style();
s.TargetType = class where inherit from baseclass
or
Style s = new Style();
s.TargetType = class which implements given interface
This is needed for creating a generic dragdrop element, where the items in the source can be derived classes.
e.g:
abstract class DDItem<T,Q>
{
public T Identifier {get;set;}
public Q DisplayableContent {get;set;}
}
<ListBox>
<ListBox.Ressources>
<Style TargetType={x:Type local:DDItem}>
<EventSetter Event="MouseLeftButtonDown" Handler="Drag"/>
</Style>
</ListBox.Ressources>
</ListBox>
Would be fine to do this in plain c# as well.
Help would be appreciated!
|
|
|
|
|
Why don't you try inheriting the Style itself?
Silverlight 5 Tutorials : 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9
|
|
|
|
|
Hello,
There's a lot of talk on the net about the View-ViewModel interaction but little about the ViewModel-Model interaction.
I have a Report that has (as a composition) a collection of Parameters. So I have a ReportViewModel that has a collection of ParameterViewModels, a ReportView bound to the ReportViewModel, and the ReportView in turn carries an ItemsControl-full of UserControls bound to the ParameterViewModels. The ReportViewModel's constructor gets passed the Report object and passes the Parameter objects in turn to the sub-viewmodels.
The Views and ViewModels interact nicely, the ViewModels "load" themselves from the underlying Models. More precisely, they keep a reference to the Model and synthesize whatever information the View needs.
Now for what puzzles me: when/how does the data flow back from ViewModel to Model ? When/how does the user's gesturing finally impacts the Model ?
I see that there are at least two possibilities...
1/ Live-update the Model whenever the ViewModel changes. Whenever we want to run operations on the updated Report, there's nothing extra to do.
2/ Implement an explicit "Apply" method in both ViewModels - obviosuly ReportViewModel.Apply will call ParameterViewModel.Apply. Whenever we need an up-to-date Report, first call Apply then save it somewhere, or print it, whatever.
What's your take on this ?
Jean-Louis
modified on Tuesday, July 19, 2011 7:49 AM
|
|
|
|
|
You're right. Too often the interaction from the VM back to the M is glossed over. The answer really depends on what you're trying to achieve though. If you are saving data from the view back to a database, for instance, you may want to batch all the changes from a particular edit and submit them at once. Alternatively, your backing store may be updated whenever a property passes validation. This is so dependent on your requirements that there are very few hard and fast rules.
|
|
|
|
|
When you are setting your binding to two-way, you are updating the model.
Yes, your view is bound to the viewmodel and not the model, but the view-model, but if your binding is two-way, the model is going to be udpated.
Maybe I've understood your question wrong here though.
Too much of heaven can bring you underground
Heaven can always turn around
Too much of heaven, our life is all hell bound
Heaven, the kill that makes no sound
|
|
|
|
|
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.
|
|
|
|
|
I may have misread your post, but it seems to me like you have a fundamental misunderstanding of the ViewModel and Model portions. Yes, your View should only bind to stuff in the ViewModel only. However, the ViewModel is not creating, copying, storing, loading, saving, etc. the data, that is the models job. The ViewModel is simply a thin *wrapper* around the model and has a reference to it. If your model is implemented correctly, you shouldn't need much (if any) repackaging of the data. Your ViewModel generally just exposes a bunch of RelayCommands that manipulate the model. If you are doing a lot of repacking work in the ViewModel, or are copying around data a lot... as they say on teh internetz: "UR DOIN IT WRONG!"
|
|
|
|
|
Hmm, I don't think I have a "fundamental misunderstanding". In my app the ViewModel does not store or load data by itself - it tells a DAO to do so. As for validation, it asks the Model to do it.
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 ?
Now loading the ViewModel from the Model has been dealt with but what about the data flowing back from Mr/Ms User to the View to the ViewModel to the Model ?
My current take is this…
ViewModel exposes commands such as NewCommand, OpenCommand, SaveCommand, SaveAsCommand - and then some more like Copy/Cut/Paste Commands.
NewCommand.Execute checks whether pending changes exist and possibly offers to save. Then it sets the VM's Document to a "new Document()". The VM updates its Content property and fires an event.
SaveCommand.CanExecute returns True if the document has been changed. SaveCommand.Execute copies the text from VM.Content to Document.Content then tells some object to save the document - via an interface so it can be mocked and unit-tested.
SaveAsCommand.Execute…hmmm…I guess it sets the VM's Document to a "new Document()" before transferring the VM.Content to Document.Content. But this assumes that the VM has copied all the Document's information. Or maybe the Document is cloneable and we replace the VM.Document with VM.Document.Clone() ?
Now in my real app, it's a bit messier because the Document is a composition of other objects that have their VM too.
|
|
|
|
|
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 the Model.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.
There is also another possibility: bind the two views to the same ViewModel. This leads to another difficulty: what if the two views are so different that we'd normally use two different ViewModel classes ? We'd be forced to artifically merge them into some sort of Janus VM with four properties: Day, Month, Year, BirthDate.
Back to my real situation...I have a User which contains a collection of Reports. All that is persisted to a DB via NHibernate via a DAO. I want to support the New/Open/Save/SaveAs cycle for Reports. It means that "opening" a report means picking one from the collection in the User object. In the scheme you propose, Save would simply tell the DAO to update the object - no need to copy stuff from VM to M. 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.
edit: grammar
|
|
|
|
|
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
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.
|
|
|
|
|
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.
|
|
|
|
|
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 ProjectsCinch 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
|
|
|
|
|
Jean-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 the
Model.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.
|
|
|
|
|
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.
|
|
|
|
|
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.
|
|
|
|
|
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.
|
|
|
|
|
I don't think you have a misunderstanding either, FWIW.
There are different ways of doing things - and no one way is intrinsically 'the right way'
I tend to get my Model object, use it to create a ViewData object (which I use inside the ViewModel) and don't persist the Model object.
I use a specific user-controlled option to update my repository - which creates a new Model from the ViewData
|
|
|
|
|
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.
|
|
|
|
|