Driving a MVVM application - c#

Given any intermediate MVVM application which has more than 5 views and viewmodels, are there any recommend design patterns of how to do the scaffolding of such an application?
Right now I usually have a controller which is created in App.OnStartup which:
sets up the main view
injects subviews (typically I have a MainWindow with status bar and navigation, which has "inner windows")
handles the marriage of views and viewmodels.
handles navigation (going from view A to view B)
supports breadcrumb navigation (and things like the typical NavigationService.GoBack())
I believe there are already good design patterns, but non of them I have heard of or read about.
So the question is:
Is there any commonly accepted pattern of how to handle the coupling of viewmodel and view (setting the datacontext) and the navigation between views?
In my opinion both view-first (setting the DataContext in XAML) and ViewModel-First (let the viewmodel get the view injected via DI/IOC) are not that good because they have make up dependencies between view and viewmodel.
Plain MVVM makes no assumptions on how to set up the whole MVVM machine.
I'm just wondering that this quite common problem has no "of-the-shelf" solution.
Controllers are widely used I believe. How do others solve that?

Some design patterns to consider are Inversion of Control (IoC) and Event Aggregator.
For C# / MVVM, the Caliburn Micro Framework (is one of a couple that) makes IoC and Event Aggregator much easier.
You correctly identified a core concern of MVVM in that there is no off-the-shelf solution to truely decouple the ViewModel from the View. It is a core concept that ViewModels are purpose built to be pared with Views. The issue comes down to how to manage instances of ViewModel / View pairings.
View first approach assumes that the View knows about and can instantiate ViewModels as needed - this is a problem for SoC because any View class now has multiple responsibilities; spinning up a ViewModel, and handling UI.
View Model first is difficult because it often leads to breaking one of the main tennants of MVVM - that the VM should be testable without any associated views.
This is where IoC comes in, typically. The IoC typically resides in the View layer (this is to allow it to have access to all View and ViewModel classes as needed) it need not be a View itself. It's often better to think of your IoC manager as a Controller - which kind of leads to a pseudo pattern of MVCVM. The sole purpose of this "controler" becomes providing View and ViewModel instance pairings to whoever needs it.
Event Aggregator pattern really helps with this because classes in the ViewModel and View no longer need to worry about who they are paired with, and can interract only with other classes in their own level. A particular View Model need not care who sent the event "Update Load Progress" all it needs to be responsible for processing the results of the event by setting it's progress property.

Regarding the "link" between the View and the ViewModel, I found the concept of the DataTemplateManager in this post really interesting. Basically, it allows you to do things like
DataTemplateManager.Register<TViewModel1,TView1>();
DataTemplateManager.Register<TViewModel2,TView2>();
DataTemplateManager.Register<TViewModel3,TView3>();
it might not be the best solution, admittedly, but is pretty handy. I already incorporated that into my own homemade MVVM framework.

I have a smallish project that contains a singleton class called ViewFinder that has a couple static methods called MakeWindowFor(vm) and MakeDialogFor(vm), that both take the viewmodel as a parameter. ViewFinder has a Dictionary that I fill that links viewmodels with the windows I've set to correspond to them. More information could be added, because perhaps the view lives inside another instead of simply being a window.
This may not be the best way to accomplish the task, but works for my needs on this project, and keeps the viewmodels unaware of the actual view implementation. The ancestor of all my viewmodels contains events for things like displaying message boxes, and all my windows are descended from a base class that know how to subscribe and react to these common events.
public class ViewFinder {
private static ViewFinder m_Instance;
public static ViewFinder Instance {
get {
if (m_Instance == null)
m_Instance = new ViewFinder();
return (m_Instance);
}
}
/// Maps viewmodels to windows/dialogs. The key is the type of the viewmodel, the value is the type of the window.
private Dictionary<Type, Type> ViewDictionary = new Dictionary<Type, Type>();
/// Private constructor because this is a singleton class.
///
/// Registers the viewmodels/views.
private ViewFinder() {
Register(typeof(SomeViewModel), typeof(SomeWindowsForViewModel));
Register(typeof(SomeViewModel2), typeof(SomeWindowsForViewModel2));
}
/// Registers a window with a viewmodel for later lookup.
/// <param name="viewModelType">The Type of the viewmodel. Must descend from ViewModelBase.</param>
/// <param name="windowType">The Type of the view. Must descend from WindowBase.</param>
public void Register(Type viewModelType, Type windowType) {
if (viewModelType == null)
throw new ArgumentNullException("viewModelType");
if (windowType == null)
throw new ArgumentNullException("windowType");
if (!viewModelType.IsSubclassOf(typeof(ViewModelBase)))
throw new ArgumentException("viewModelType must derive from ViewModelBase.");
if (!windowType.IsSubclassOf(typeof(WindowBase)))
throw new ArgumentException("windowType must derive from WindowBase.");
ViewDictionary.Add(viewModelType, windowType);
}
/// Finds the window registered for the viewmodel and shows it in a non-modal way.
public void MakeWindowFor(ViewModelBase viewModel) {
Window win = CreateWindow(viewModel);
win.Show();
}
/// Finds a window for a viewmodel and shows it with ShowDialog().
public bool? MakeDialogFor(ViewModelBase viewModel) {
Window win = CreateWindow(viewModel);
return (win.ShowDialog());
}
/// Helper function that searches through the ViewDictionary and finds a window. The window is not shown here,
/// because it might be a regular non-modal window or a dialog.
private Window CreateWindow(ViewModelBase viewModel) {
Type viewType = ViewDictionary[viewModel.GetType()] as Type;
if (viewType == null)
throw new Exception(String.Format("ViewFinder can't find a view for type '{0}'.", viewModel.GetType().Name));
Window win = Activator.CreateInstance(viewType) as Window;
if (win == null)
throw new Exception(String.Format("Activator returned null while trying to create instance of '{0}'.", viewType.Name));
win.DataContext = viewModel;
return win;
}
}

Related

Startup Sequence for C# WPF MVVM Applications

I'm currently going back to designing some GUI Applications using WPF + MVVM, I now find it much easier to understand than when I first encountered it.
One question that troubles me however is the startup of the application. From my view, there are two approaches:
Start from the Main Window View, have its ViewModel be instantiated by some means which again instantiates the Model it represents. This puts the View / GUI in the "operating" position.
The other way would be to override the OnStartup routine in the Application class (John Smith does it this way in his The MVVM-Design Pattern MSDN Article) and start by creating the model, passing it to the ViewModel Contructor and assign the newly created ViewModel to the DataContext of a separately created View / Window.
Is either way fine (in this case, what may be reasons to prefer one over the other) or is one violating MVVM rules?
Your first approach is correct:
Start from the Main Window View, have its ViewModel be instantiated by
some means which again instantiates the Model it represents. This puts
the View / GUI in the "operating" position.
The viewmodel then becomes the DC for the View. There should be a 1:1 of View to ViewModel.
You want each class to be able to be instantiated with as few dependencies as possible.
My ViewModel ctors really only consist of an argument for passing a class containing View-Specific callbacks, based on an interface.
Model m = null;
IViewCallbacks cb;
public MainViewModel(IViewCallbacks mainViewCallbacks)
{
this.cb = mainViewCallbacks;
m = new Model();
}
The ViewModel instance has instances of the model(s) that I need to be able to access. The vm backing the view should be the in charge of instantiating these, otherwise your unit testing is going to suffer because of external dependencies (that instance of the model that you need to pass in via the ctor).

How does one manage object lifetime in MVVM?

I'm working on an application built using C# and WPF, which (badly) implements MVVM. The breakdown of work is something like:
View
Presentation, bitmaps, colors, etc.
Animations (if used)
Communicates with the ViewModel by being data bound to it
Communicates with the ViewModel by raising commands on it
ViewModel
Exposes commands to be invoked by view(s)
Delegates data processing functionality to the model
Defines UI behavior
No direct (named) dependency on the view
Communicates with the Model by calling methods in the model
May be notified of changes in the model by subscribing to events exposed by the model
Model
Disk persistence, data analysis, etc
Everything else
No dependency on the ViewModel
Unfortunately, this has resulted in circular references, because view(s) need references to viewmodels to raise events and fire commands, viewmodels need references to views in order to update view state (typically this reference is by a viewmodel being WPF's DataContext), viewmodels need a reference to models to delegate work, and models often need to notify viewmodels of some external change in state.
So we have two circular reference problems, with the viewmodel sitting in the middle. As a result, this application is running into memory consumption problems because WPF entities are being created and associated with some piece of data in the model, and these entities are never cleaned up (until the program is terminated).
How is this supposed to be handled?
It seems like an ownership graph needs to be defined, such that one or more of these components is responsible for disconnecting event handlers when they are no longer relevant, so that things can be GC'd.
Your question does not provide enough information about what gets instantiated and I doubt that it will be possible to solve this without digging through all the code.
First: This is a really bad MVVM as separation of layers and concerns is violated here.
I would suggest to fix this first.
Your issue might be a poor understanding of XAML and instantiation at all (no offence)
Without touching the architecture you will need to profile your application to see what objects gets instantiated how many times (memory hotspots) It might also be interesting to see in which GC generation those objects are as this might indicate some serious (manual) memory management issue.
Event Handlers tend to create leaks. Therefore Weak reference patterns may be applied: http://msdn.microsoft.com/en-us/library/aa970850.aspx
A good MVVM has some sort of lifetime management that also servers as DI and IoC container through which complex lifetime scenarios should be handled
Ok, I thought you've used all of them together directly
If you have not any cached view, then why you've high memory usage ?
You can not have memory problem in a MVVM application ( even non well architectured ones )
What static items do you have ?
Have you ever used this code to investigate the current situation of bindings of your wpf application ?
private static IList<BindingInfo> getReflectPropertyDescriptorInfo()
{
var results = new List<BindingInfo>();
var reflectTypeDescriptionProvider = typeof(PropertyDescriptor).Module.GetType("System.ComponentModel.ReflectTypeDescriptionProvider");
var propertyCacheField = reflectTypeDescriptionProvider.GetField("_propertyCache",
BindingFlags.Static | BindingFlags.NonPublic);
if (propertyCacheField == null)
throw new NullReferenceException("`ReflectTypeDescriptionProvider._propertyCache` not found");
var propertyCacheItems = propertyCacheField.GetValue(null) as Hashtable;
if (propertyCacheItems == null)
return results;
var valueChangedHandlersField = typeof(PropertyDescriptor).GetField("valueChangedHandlers",
BindingFlags.Instance | BindingFlags.NonPublic);
if (valueChangedHandlersField == null)
return results;
foreach (DictionaryEntry entry in propertyCacheItems)
{
var properties = entry.Value as PropertyDescriptor[];
if (properties == null)
continue;
foreach (var property in properties)
{
var valueChangedHandlers = valueChangedHandlersField.GetValue(property) as Hashtable;
if (valueChangedHandlers != null && valueChangedHandlers.Count != 0)
results.Add(new BindingInfo
{
TypeName = entry.Key.ToString(),
PropertyName = property.Name,
HandlerCount = valueChangedHandlers.Count
});
}
}
return results;
}
With this code you can find out what binding are in memory ?
" viewmodels need references to views in order to update view state (typically this reference is by a viewmodel being WPF's DataContext), " - At this point there is no dependency as View does not know what is in the DataContext and ViewModel does not really know anything about View, so it is not deffinitly Devependency of the in ViewModel of the View.
You got dependency when you need to assign ViewModel to DataContext, and View is dependent of ViewModel (it does not need to be as IViewModel can be used instead of ViewModel)
Even with the ViewModel first approach you don't have actually View, you have IView contract that is implemented by View.
class ContentViewModel{
IView view;
public ContentViewModel(IContentAView view)
{
View = view;
View.ViewModel = this;
If you using container as Unity and View Model or View is instanciated every time but old instance is not GC because of some reference( maybe event handlers) you will have memory leak. You need to profile memory and find out when new instance is created and find references to the old instance by using some tool such as RedGate memory profiler of Microsoft WinDbg.

Is it OK to instantiate presenters within Main using MVP approach?

I wanted to use a "presenter first" approach (the presenter attaches itself to it's view and model in the Constructor using dependency injection via the constructor call).
My form is a single MainForm containing two user controls which each have their own presenters, but share the model, so I create all the presenters in Main, passing in the relevant user control to the presenter by having a property which exposes these controls from FormMain, and passing in a single instance of the model to all presenters.
static void Main()
{
Application.EnableVisualStyles();
Application.SetCompatibleTextRenderingDefault(false);
IDocumotiveCaptureView view = new DocumotiveCaptureView(); //this is the MainForm
IDocumentModel model = new DocumentModel(); //single model
IDocumotiveCapturePresenter Presenter = new DocumotiveCapturePresenter(view, model); //MainForm's presenter
IControlsPresenter ControlsPresenter = new ControlsPresenter(view.ControlsView, model); //ControlsPresenter - attached to the user control via view.ControlsView
IDocumentPresenter DocumentPresenter = new DocumentPresenter(view.DocumentView, model);//DocumentPresenter - attached to the user control via view.DocumentView)
Application.Run((Form)view);
}
Can anyone see anything inherently bad or wrong in this approach? I realise this MAY be subjective, but I'm very new to MVP and would like some feedback.
If it works for you, then of course it's ok. MVP isn't a religion. It's not even considered a single pattern anymore. The two most popular interpretations seems now to be Passive View and Supervising controller.
The Main method in your case is your composition root (search for any answers by Mark Seeman) and it's an excellent place to do what you do. But it's not the only place to do it.
For more MVP in WinForms, please see Model-View-Presenter in WinForms

MVVM: Binding to Model while keeping Model in sync with a server version

I've spent quite some time to try and find an elegant solution for the following challenge. I've been unable to find a solution that's more than a hack around the problem.
I've got a simple setup of a View, ViewModel and a Model. I will keep it very simple for the sake of explanation.
The Model has a single property called Title of type String.
The Model is the DataContext for the View.
The View has a TextBlock thats databound to Title on the Model.
The ViewModel has a method called Save() that will save the Model to a Server
The Server can push changes made to the Model
So far so good. Now there are two adjustments I need to make in order to keep the Model in sync with a Server. The type of server is not important. Just know that I need to call Save() in order to push the Model to the Server.
Adjustment 1:
The Model.Title property will need to call RaisePropertyChanged() in order to translate changes made to the Model by the Server to the View. This works nicely since the Model is the DataContext for the View
Not too bad.
Adjustment 2:
Next step is to call Save() to save changes made from the View to the Model on the Server. This is where I get stuck. I can handle the Model.PropertyChanged event on the ViewModel that calls Save() when the Model gets changed but this makes it echo changes made by the Server.
I'm looking for an elegant and logical solution and am willing to change my architecture if it makes sense.
In the past I 've written an application that supports "live" editing of data objects from multiple locations: many instances of the app can edit the same object at the same time, and when someone pushes changes to the server everyone else gets notified and (in the simplest scenario) sees those changes immediately. Here's a summary of how it was designed.
Setup
Views always bind to ViewModels. I know it's a lot of boilerplate, but binding directly to Models is not acceptable in any but the simplest scenarios; it's also not in the spirit of MVVM.
ViewModels have sole responsibility for pushing changes. This obviously includes pushing changes to the server, but it could also include pushing changes to other components of the application.
To do this, ViewModels might want to clone the Models they wrap so that they can provide transaction semantics to the rest of the app as they provide to the server (i.e. you can choose when to push changes to the rest of the app, which you cannot do if everyone directly binds to the same Model instance). Isolating changes like this requires still more work, but it also opens up powerful possibilities (e.g. undoing changes is trivial: just don't push them).
ViewModels have a dependency on some kind of Data Service. The Data Service is an application component that sits between the data store and the consumers and handles all communication between them. Whenever a ViewModel clones its Model it also subscribes to appropriate "data store changed" events that the Data Service exposes.
This allows ViewModels to be notified of changes to "their" model that other ViewModels have pushed to the data store and react appropriately. With proper abstraction, the data store can also be anything at all (e.g. a WCF service in that specific application).
Workflow
A ViewModel is created and assigned ownership of a Model. It immediately clones the Model and exposes this clone to the View. Having a dependency on the Data Service, it tells the DS that it wants to subscribe to notifications for updates this specific Model. The ViewModel does not know what it is that identifies its Model (the "primary key"), but it doesn't need to because that's a responsibility of the DS.
When the user finishes editing they interact with the View which invokes a Command on the VM. The VM then calls into the DS, pushing the changes made to its cloned Model.
The DS persists the changes and additionally raises an event that notifies all other interested VMs that changes to Model X have been made; the new version of the Model is supplied as part of the event arguments.
Other VMs that have been assigned ownership of the same Model now know that external changes have arrived. They can now decide how to update the View having all pieces of the puzzle at hand (the "previous" version of the Model, which was cloned; the "dirty" version, which is the clone; and the "current" version, which was pushed as part of the event arguments).
Notes
The Model's INotifyPropertyChanged is used only by the View; if the ViewModel wants to know whether the Model is "dirty", it can always compare the clone to the original version (if it has been kept around, which I recommend if possible).
The ViewModel pushes changes to the Server atomically, which is good because it ensures that the data store is always in a consistent state. This is a design choice, and if you want to do things differently another design would be more appropriate.
The Server can opt to not raise the "Model changed" event for the ViewModel that was responsible for this change if the ViewModel passes this as a parameter to the "push changes" call. Even if it does not, the ViewModel can choose to do nothing if it sees that the "current" version of the Model is identical to its own clone.
With enough abstraction, changes can be pushed to other processes running on other machines as easily as they can be pushed to other Views in your shell.
Hope this helps; I can offer more clarification if required.
I would suggest adding Controllers to the MVVM mix (MVCVM?) to simplify the update pattern.
The controller listens for changes at a higher level and propagates changes between the Model and ViewModel.
The basic rules to keep things clean are:
ViewModels are just dumb containers that hold a certain shape of data. They do not know where the data comes from or where it is displayed.
Views display a certain shape of data (via bindings to a view model).
They do not know where the data comes from, only how to display it.
Models supply real data. They do not know where it is consumed.
Controllers implement logic. Things like supplying the code for ICommands in VMs, listening for changes to data etc. They populate VMs from Models. It makes sense to have them listen for VM changes and update the Model.
As mentioned in another answer your DataContext should be the VM (or property of it), not the model. Pointing at a DataModel makes it hard to separate concerns (e.g. for Test Driven Development).
Most other solutions put logic in ViewModels which is "not right", but I see the benefits of controllers overlooked all the time. Darn that MVVM acronym! :)
binding model to view directly only works if the model implement INotifyPropertyChanged interface. (eg. your Model generated by Entity Framework)
Model implement INotifyPropertyChanged
you can do this.
public interface IModel : INotifyPropertyChanged //just sample model
{
public string Title { get; set; }
}
public class ViewModel : NotificationObject //prism's ViewModel
{
private IModel model;
//construct
public ViewModel(IModel model)
{
this.model = model;
this.model.PropertyChanged += new PropertyChangedEventHandler(model_PropertyChanged);
}
private void model_PropertyChanged(object sender, PropertyChangedEventArgs e)
{
if (e.PropertyName == "Title")
{
//Do something if model has changed by external service.
RaisePropertyChanged(e.PropertyName);
}
}
//....more properties
}
ViewModel as DTO
if Model implements INotifyPropertyChanged(it depends) you may use it as DataContext in most cases. but in DDD most MVVM model will be considered as EntityObject not a real Domain's Model.
more efficient way is to use ViewModel as DTO
//Option 1.ViewModel act as DTO / expose some Model's property and responsible for UI logic.
public string Title
{
get
{
// some getter logic
return string.Format("{0}", this.model.Title);
}
set
{
// if(Validate(value)) add some setter logic
this.model.Title = value;
RaisePropertyChanged(() => Title);
}
}
//Option 2.expose the Model (have self validation and implement INotifyPropertyChanged).
public IModel Model
{
get { return this.model; }
set
{
this.model = value;
RaisePropertyChanged(() => Model);
}
}
both of ViewModel's properties above can be used for Binding while not breaking MVVM pattern (pattern != rule) it really depends.
One more thing..
ViewModel has dependency on Model. if Model can be changed by external service/environment. it's "global state" that make things complicate.
If your only problem is that changes from the server get immediately re-saved, why not do something like the following:
//WARNING: typed in SO window
public class ViewModel
{
private string _title;
public string Title
{
get { return _title; }
set
{
if (value != _title)
{
_title = value;
this.OnPropertyChanged("Title");
this.BeginSaveToServer();
}
}
}
public void UpdateTitleFromServer(string newTitle)
{
_title = newTitle;
this.OnPropertyChanged("Title"); //alert the view of the change
}
}
This code manually alerts the view of the property change from the server without going through the property setter and therefore without invoking the "save to server" code.
The reason you have this problem is because your model doesn't know whether it is dirty or not.
string Title {
set {
this._title = value;
this._isDirty = true; // ??!!
}
}}
The solution is to copy the server changes via a separate method:
public void CopyFromServer(Model serverCopy)
{
this._title = serverCopy.Title;
}

The best approach to create new window in WPF using MVVM

In the neighbour post: How should the ViewModel close the form?
I've posted my vision how to close windows with MVVM usage. And now I have a question: how to open them.
I have a main window (main view). If user clicks on the "Show" button then "Demo" window (modal dialog) should be displayed. What is a preferable way to create and open windows using MVVM pattern? I see two general approaches:
The 1st one (probably the simplest). Event handler "ShowButton_Click" should be implemented in the code behind of the main window in way like this:
private void ModifyButton_Click(object sender, RoutedEventArgs e)
{
ShowWindow wnd = new ShowWindow(anyKindOfData);
bool? res = wnd.ShowDialog();
if (res != null && res.Value)
{
// ... store changes if neecssary
}
}
If we "Show" button state should be changed (enabled/disabled) we will need to add logic that will manage button state;
The source code is very similar to "old-style" WinForms and MFC sources - I not sure if this is good or bad, please advise.
Something else that I've missed?
Another approach:
In the MainWindowViewModel we will implement "ShowCommand" property that will return ICommand interface of the command. Comman in turn:
will raise "ShowDialogEvent";
will manage button state.
This approach will be more suitable for the MVVM but will require additional coding: ViewModel class can't "show dialog" so MainWindowViewModel will only raise "ShowDialogEvent", the MainWindowView we will need to add event handler in its MainWindow_Loaded method, something like this:
((MainWindowViewModel)DataContext).ShowDialogEvent += ShowDialog;
(ShowDialog - similar to the 'ModifyButton_Click' method.)
So my questions are:
1. Do you see any other approach?
2. Do you think one of the listed is good or bad? (why?)
Any other thoughts are welcome.
Thanks.
Some MVVM frameworks (e.g. MVVM Light) make use of the Mediator pattern.
So to open a new Window (or create any View) some View-specific code will subscribe to messages from the mediator and the ViewModel will send those messages.
Like this:
Subsription
Messenger.Default.Register<DialogMessage>(this, ProcessDialogMessage);
...
private void ProcessDialogMessage(DialogMessage message)
{
// Instantiate new view depending on the message details
}
In ViewModel
Messenger.Default.Send(new DialogMessage(...));
I prefer to do the subscription in a singleton class, which "lives" as long as the UI part of the application does.
To sum up: ViewModel passes messages like "I need to create a view" and the UI listens to those messages and acts on them.
There's no "ideal" approach though, for sure.
I was thinking about this issue recently too. Here's an idea I had if you use Unity in your project as a 'container' or whatever for dependency injection. I guess normally you'd override App.OnStartup() and create your model, view model, and view there, and give each the appropriate references. Using Unity, you give the container a reference to the model, then use the container to 'resolve' the view. The Unity container injects your view model, so you never directly instantiate it. Once your view is resolved, you call Show() on it.
In an example video I watched, the Unity container was created as a local variable in OnStartup. What if you created it as a public static readonly property in your App class? You could then use it in your main view model to create your new windows, automatically injecting whatever resources the new view needs. Something like App.Container.Resolve<MyChildView>().ShowDialog();.
I suppose you could somehow mock the result of that call to the Unity container in your tests. Alternatively, perhaps you could write methods like ShowMyChildView() in the App class, which basically just does what I described above. It might be easy to mock a call to App.ShowMyChildView() since it would just return a bool?, eh?
Well, that might not really be any better than just using new MyChildView(), but it's a little idea I had. I thought I'd share it. =)
I'm a bit late, but I find existing answers insufficient. I will explain why. In general:
it's ok to access ViewModels from View,
it's wrong to access Views from ViewModels, because it introduces circular dependency and makes the ViewModels hard to test.
Benny Jobigan's anwer:
App.Container.Resolve<MyChildView>().ShowDialog();
this actually does not solve anything. You are accessing your View from ViewModel in a tigtly coupled fashion. The only difference from new MyChildView().ShowDialog() is that you went trough a layer of indirection. I don't see any advantage over directly calling the MyChildView ctor.
It would be cleaner if you used interface for the view:
App.Container.Resolve<IMyChildView>().ShowDialog();`
Now the ViewModel is not tigtly coupled to the view. However I find it quite impractical to create interface for each view.
arconaut's anwer:
Messenger.Default.Send(new DialogMessage(...));
it's better. It seems like Messenger or EventAggregator or another pub/sub patterns are universal solution for everyhing in MVVM :) The disadvantage is that it's harder to debug or to navigate to DialogMessageHandler. It's too indirect imho. For example, how would you read output form the Dialog? by modifying DialogMessage?
My Solution:
you can open window from MainWindowViewModel like this:
var childWindowViewModel = new MyChildWindowViewModel(); //you can set parameters here if necessary
var dialogResult = DialogService.ShowModal(childWindowViewModel);
if (dialogResult == true) {
//you can read user input from childWindowViewModel
}
DialogService takes just dialog's ViewModel, so your viewmodels are totally independent from Views. At runtime, DialogService can find appropriate view (using naming convention for example) and shows it, or it can be easily mocked in unit tests.
in my case I use this interfaces:
interface IDialogService
{
void Show(IDialogViewModel dialog);
void Close(IDialogViewModel dialog);
bool? ShowModal(IDialogViewModel dialog);
MessageBoxResult ShowMessageBox(string message, string caption = null, MessageBoxImage icon = MessageBoxImage.No...);
}
interface IDialogViewModel
{
string Caption {get;}
IEnumerable<DialogButton> Buttons {get;}
}
where DialogButton specifies DialogResult or ICommand or both.
Take a look at my current MVVM solution for showing Modal Dialogs in Silverlight.
It solves most of the issues you mentioned yet its completely abstracted from platform specific things and can be reused. Also i used no code-behind only binding with DelegateCommands that implement ICommand. Dialog is basically a View - a separate control that has its own ViewModel and it is shown from the ViewModel of the main screen but triggered from the UI via DelagateCommand binding.
See full Silverlight 4 solution here Modal dialogs with MVVM and Silverlight 4
I use a controller which handles all information passing between views. All viewmodels use methods in the controller to request more information which can be implemented as dialogs, other views etc.
It looks something like this:
class MainViewModel {
public MainViewModel(IView view, IModel model, IController controller) {
mModel = model;
mController = controller;
mView = view;
view.DataContext = this;
}
public ICommand ShowCommand = new DelegateCommand(o=> {
mResult = controller.GetSomeData(mSomeData);
});
}
class Controller : IController {
public void OpenMainView() {
IView view = new MainView();
new MainViewModel(view, somemodel, this);
}
public int GetSomeData(object anyKindOfData) {
ShowWindow wnd = new ShowWindow(anyKindOfData);
bool? res = wnd.ShowDialog();
...
}
}
My approach is similar to adrianm’s. However, in my case the Controller never works with the concrete View types. The Controller is completely decoupled of the View - in the same way as the ViewModel.
How this works can be seen in the ViewModel example of WPF Application Framework (WAF).
.
Best Regards,
jbe

Categories