Reputation: 155360
I'm working on a WPF project that's a mishmash of code-behind xaml/xaml.cs and a few not-quite ViewModels as well.
(Disclaimer: Until recently I've had very little in the way of WPF experience. I can design and lay-out a Window or UserControl fairly proficiently, and I think I get the hang of separating an MVVM ViewModel from the View and doing binding wire-ups, but that's the limit of my experience with WPF at present.)
I've been tasked with adding some new features to the program, such that it looks like converting it to use MVVM properly first is going to be necessary.
I'll demonstrate a specific problem I'm facing:
There is a View called SettingsWindow.xaml
that I'm working with. It's a set of textboxes, labels and whatnot. I've stripped-out all of the View data into a ViewModel
class which resembles something like this:
class SettingsViewModel : ViewModelBase {
private String _outputDirectory;
public String OutputDirectory {
get { return _outputDirectory; }
set { SetValue( () => this.OutputDirectory, ref _outputDirectory, value) ); }
}
// `SetValue` calls `PropertyChanged` and does other common-tasks.
// Repeat for other properties, like "Int32 Timeout" and "Color FontColor"
}
In the original ViewModel class there were 2 methods: ReadFromRegistry
and SaveToRegistry
. The ReadFromRegistry
method was called by the ViewModel's constructor, and the SaveToRegistry
method was called by MainWindow.xaml.cs
's code-behind like so:
private void Settings_Click(Object sender, RoutedEventArgs e) {
SettingsViewModel model = new SettingsViewModel(); // loads from registry via constructor
SettingsWindow window = new SettingsWindow();
window.Owner = this;
window.DataContext = model;
if( dialog.ShowDialog() == true ) {
model.SaveToRegistry();
}
}
...but this seems wrong to me. I thought a ViewModel should consist only of an observable data bag for binding purposes, it should not be responsible for self-population or persistence, which is the responsibility of the controller or some other orchestrator.
I've done a few days' worth of reading about MVVM, and none of the articles I've read mention a controller or where the logic for opening child-windows or saving state should go. I've seen some articles that do put that code in the ViewModels, others continue to use code-behind for this, others abstract away everything and use IService
-based solutions, which is OTT for me.
Given this is a conversion project where I'll convert each Window/View individually over-time I can't really overhaul it, but where can I go from here? What does a Controller in MVVM look-like, exactly? (My apologies for the vague terminology, it's 3am :) ).
My aim with the refactoring is to separate concerns; testability is not an objective nor would it be implemented.
Upvotes: 5
Views: 5942
Reputation: 21
From what I see in the codebases it doesn't seem that Controller for MVVM has gone mainstream. Without Controller MVVM is a recurring and inevitable nightmare. If you understand single responsibility principle (SRP), then consider this:
ViewModel's responsibility is to provide DataContext. It has no business communicating with the outside world. Communication is an additional responsibility; it should be delegated to a dedicated entity. That entity is the Controller.
Controller sets ViewModel's properties and listens for its events. ViewModel is not aware of the Controller, in that respect it is dumb. Also, that makes it trivially testable and reusable.
Controller takes care of calling outside services and bringing data back to ViewModel. Controller is responsible for synchronization, coordination, and all other wiring tasks.
The argument may be that adding Controller to the picture makes things more complicated. No, mixing concerns in one place complicates things. Separate concerns should live in separate places. That makes things easier to manage and more powerful.
Good luck with your journey folks. SOLID all the way.
Upvotes: 0
Reputation: 3595
I personally disagree with putting much in my ViewModels beyond the stuff that is pertinent to the View (it is, after all, a model of a View!)
So I use a Controller paradigm whereby when the View tells the ViewModel to perform some action (via a Command usually) and the ViewModel uses a Command class to perfrom actions, such as saving the data, instantiating new View/Viewmodel pairs etc.
I also actually separate my ViewModel and ViewData (the ViewModel 'contains' the ViewData) so the ViewData is puirely dealing with the data, the ViewModel with some logic and command handling etc.
Upvotes: 8
Reputation: 8791
What you need is called Commanding in WPF.
Basically you bind Button.Command
to a ICommand property in your ViewModel and when Button
is clicked you get a notification in ViewModel
without using code behind and casing DataContext
or whathever hacks you tried.
http://msdn.microsoft.com/en-us/library/ms752308.aspx
Upvotes: 3