I have used ValueConverters
in some cases and put the logic in the ViewModel
in others. My feeling is that a ValueConverter
becomes part of the View
layer, so if the logic is really part of the View
then put it there, otherwise put it in the ViewModel
.
Personally I don't see a problem with a ViewModel
dealing with View
-specific concepts like Brush
es because in my applications a ViewModel
only exists as a testable and bindable surface for the View
. However, some people put a lot of business logic in the ViewModel
(I do not) and in that case the ViewModel
is more like a part of their business layer, so in that case I wouldn't want WPF-specific stuff in there.
I prefer a different separation:
View
- WPF stuff, sometimes untestable (like XAML and code-behind) but also ValueConverter
s
ViewModel
- testable and bindable class that is also WPF-specific
EditModel
- part of the business layer that represents my model during manipulation
EntityModel
- part of the business layer that represents my model as persisted
Repository
- responsible for persistence of the EntityModel
to the database
So, the way I do it, I have little use for ValueConverter
s
The way I got away from some of your "Con's" is to make my ViewModel
's very generic. For instance, one ViewModel
I have, called ChangeValueViewModel
implements a Label property and a Value property. On the View
there's a Label
that binds to the Label property and a TextBox
that binds to the Value property.
I then have a ChangeValueView
which is a DataTemplate
keyed off of the ChangeValueViewModel
type. Whenever WPF sees that ViewModel
it applies that View
. The constructor of my ChangeValueViewModel
takes the interaction logic it needs to refresh its state from the EditModel
(usually just passing in a Func<string>
) and the action it needs to take when the user edits the Value (just an Action
that executes some logic in the EditModel
).
The parent ViewModel
(for the screen) takes an EditModel
in its constructor and just instantiates the appropriate elementary ViewModel
s such as ChangeValueViewModel
. Since the parent ViewModel
is injecting the action to take when the user makes any change, it can intercept all of these actions and take other actions. Therefore, the injected edit action for a ChangeValueViewModel
might look like:
(string newValue) =>
{
editModel.SomeField = newValue;
foreach(var childViewModel in this.childViewModels)
{
childViewModel.RefreshStateFromEditModel();
}
}
Obviously the foreach
loop can be refactored elsewhere, but what this does is take the action, apply it to the model, then (assuming the model has updated its state in some unknown way), tells all the child ViewModel
s to go and get their state from the model again. If the state has changed, they are responsible for executing their PropertyChanged
events, as necessary.
That handles the interaction between, say, a list box and a details panel quite nicely. When the user selects a new choice, it updates the EditModel
with the choice, and the EditModel
changes the values of the properties exposed for the detail panel. The ViewModel
children that are responsible for displaying the detail panel information automatically get notified that they need to check for new values, and if they've changed, they fire their PropertyChanged
events.
Navigation should always be handled in the ViewModel.
You're on the right track with thinking that the perfect implementation of the MVVM design pattern would mean you could run your application entirely without Views, and you can't do that if your Views control your Navigation.
I usually have an ApplicationViewModel
, or ShellViewModel
, which handles the overall state of my application. This includes the CurrentPage
(which is a ViewModel) and the code for handling ChangePageEvents
. (It's often also used for other application-wide objects such as the CurrentUser, or ErrorMessages too)
So if any ViewModel, anywhere, broadcasts a ChangePageEvent(new SomePageViewModel)
, the ShellViewModel
will pickup that message and switch the CurrentPage
to whatever page was specified in the message.
I actually wrote a blog post about Navigation with MVVM if you're interested
Best Answer
Yes, both ways are valid. There are pros and cons to each approach.
If the
Person
can be displayed in several different ways depending on the context - whether it's in aListBox
or on a "page" then using your technique with anItemTemplate
to control how it's displayed in the list is probably the better way to go.I think you need to look at your overall design and see which fits best. Changing the implementation part way through is a bad idea though. Some "pages" will be the old style and others will be the new, leading to inconsistencies in the user experience and potentially increased effort in maintaining and extending the system. If nothing else, you will have to go through the existing code to make it conform to the new format.