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
Related
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 do I call a function on a user control from a separate view model?
In my scenario, I have a "main" view with a ScoreDisplay UserControl:
<local:ScoreDisplay/>
This control will display the score of n number of players in my game. The main view model is hooked up to a game controller that feeds it the score updates. I need to get these updated scores to the UserControl (and run some animations, non-trivial logic, etc.)
I see a few options:
Create a "Scores" dependency property and bind it to the view models collection of scores. The only problem I see with this approach is that whenever it is modified, I need to go look and see what changed so I can run the appropriate animations. Doing this is certainly possible, but doesn't seem "right".
Have the ViewModel invoke a "UpdateScore" function on the UserControl. The only problem, of course, is that the ViewModel shouldn't know anything about the View and so shouldn't have the reference necessary to do this.
Have the UserControl register for a "ScoreUpdated" event on the view model. This seems like the best option, but I have no idea on how to get the reference to the ViewModel in order to register for the event.
Which option, if any, is the correct approach? If (2) or (3), how can I implement this?
EDIT:
To be clear, the values in the scores collection are changing (the collection itself remains the same). I could put a wrapper around the score int and listen to PropertyChanged, but again, this seems like an overcomplicated approach to a simple problem. If that is the best solution though, please let me know!
EDIT 2:
UpdateScore is a function that (in theory) accepts the index of the updated score and the value to add to that player's score (it could accept the whole score). It then causes the player's peg to move along a cribbage track to its new position.
It gets called whenever a player gets points (this is a Cribbage game, so this happens a lot). The view model is attached to a game controller that raises an event to notify the VM that a player has been awarded points. The view model basically just needs to pass this information to ScoreDisplay for display/animation etc.
In this case we can apply the Mediator pattern, if this is succeed, it will not need the event.
There are several embodiments of the Mediator pattern, but I like the most the implementation by XAML Guy, it is simple and clear - The Mediator Pattern.
Implementation code
public static class Mediator
{
static IDictionary<string, List<Action<object>>> pl_dict = new Dictionary<string, List<Action<object>>>();
static public void Register(string token, Action<object> callback)
{
if (!pl_dict.ContainsKey(token))
{
var list = new List<Action<object>>();
list.Add(callback);
pl_dict.Add(token, list);
}
else
{
bool found = false;
foreach (var item in pl_dict[token])
if (item.Method.ToString() == callback.Method.ToString())
found = true;
if (!found)
pl_dict[token].Add(callback);
}
}
static public void Unregister(string token, Action<object> callback)
{
if (pl_dict.ContainsKey(token))
{
pl_dict[token].Remove(callback);
}
}
static public void NotifyColleagues(string token, object args)
{
if (pl_dict.ContainsKey(token))
{
foreach (var callback in pl_dict[token])
callback(args);
}
}
}
Communication via a Mediator is carried out as follows:
Mediator.NotifyColleagues("NameOfYourAction", ObjectValue);
In this case, we notify NameOfYourAction that you need to pass the ObjectValue for him. In order to NameOfYourAction successfully received the data, it is necessary to register it in the class or in the ViewModel like this:
private void NameOfYourAction_Mediator(object args)
{
MyViewModel viewModel = args as MyViewModel;
if (viewModel != null)
viewModel.PropertyA = someValue;
}
// Somewhere, may be in constructor of class
Mediator.Register("NameOfYourAction", NameOfYourAction_Mediator);
In your case, the value is passed ScoreData in ViewModel, where which will be changes.
For more example of using pattern Mediator, please see this answer:
One ViewModel for UserControl and Window or separate ViewModels
Anatoliy Nikolaev's answer above is a good one.
As another option I would also suggest looking into the Event Aggregator. This is a great pattern and has many uses. They would both get a reference to the Event Aggregator and then one could publish an event and the other would receive it and could take action. If you enable something like this in your application it becomes trivial for multiple ViewModels to communicate in a completely decoupled way. And as an added bonus testing becomes simple as you can easily mock out your Event Aggregator to supply any data needed.
I found a way to do this:
I made a dependency property of my view model type:
public GameViewModel BaseViewModel
{
get { return (GameViewModel)GetValue(baseViewModelProperty); }
set { SetValue(baseViewModelProperty, value); }
}
public static readonly DependencyProperty baseViewModelProperty =
DependencyProperty.Register("BaseViewModel", typeof(GameViewModel), typeof(ScoreDisplay), new PropertyMetadata(null, RegisterForScoreChange));
and changed my XAML to:
<local:ScoreDisplay BaseViewModel="{Binding}"/>
Then in the PropertyChanged event handler of the dependency property, I was able to wire up my event.
(e.NewValue as GameViewModel).ScoreUpdated += (d as ScoreDisplay).UpdateScore;
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;
}
}
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
Is anything really wrong with a ViewModel opening additonal dialogues? Lets say I have a MainView and a MainViewModel. The MainViewModel is the datacontext for the MainView and does not, in fact, know or have any dependency on the mainview itself.
However, there are cases when the main view need to open dialogues that will affect the ViewModel data. For example, I may want show a dialogue and diplay some items to allow the users to select from. So, what I have settled on is this:
In my ViewModel, I have the following methods: AddItem, EditItem, and DeleteItem. However, in order to supply the items to add or edit, I need to present a list in some dialogue for the user to to choose from. Right now I have the ViewModel doing this only because I don't want to implement additional levels of abstraction for such simple tasks. Having the ViewModel do this means it can provide the list to be displayed to the user and, when the user finishes editing or selecting, it can easily update its member collections/properties.
Should I be shot for settling with this approach?
Shot? No. But there are good reasons for not doing this.
First, it kills testability of your ViewModel, as there's now a visual component in place. When you try to write automated unit tests against it, you'll still have to interact with it. You could mock it out, but it becomes more difficult to do so when you're calling UI methods.
Second, your viewmodel shouldn't care about what gets displayed. There's a real "separation of concern" issue when you start combining these things.
Third, it just has a "code smell."
There are a few things you can do to circumvent this issue. The first thing I would suggest is Don't use dialogs. Dialogs have their place, but programmers tend to overuse them. Rethink your design, and try to figure out how you can get the job done without interrupting the user.
Second, consider using a messaging framework to send messages between your viewmodel and view to do the navigation to the dialogs (if you absolutely have to use them). Messages are very easy to mock out and/or write unit tests around.
the easy way to do this: use a dialogservice - easy to use, easy to unittest!
see this.
I don't see any problems with ViewModels communicating with each other. The problem is if they start accessing the Views or other Dialogs since that will affect the systems testability.
If you really want a more loosely coupled system you could use some sort of messaging system for communication, but I doubt you need that here :-)
I always use a Seelctor service(just a basic dialog service) to do this - it's testable and mockable and keeps the code very SOLID.
class ViewModel
{
public ICommand ShowListSelectorForCounterparties { get; set; }
public IListSelectorService ListSelector { get; set; }
public void OnExecuteShowCounterpartySelector()
{
this.Counterparty = this.ListSelector.Select<Counterparty>();
}
}
where IListSelectorService can, at runtime, instantiate your dialog, present your list and return the selected item. The main good thing about running it this way is that your unit tests can mock the IListSelectorService.
I'm not sure if you are still looking for any help, but the approach that I have taken when it comes to dialogs is to have the view model raise an event that the view can then handle. The view can now do whatever it wants to get the data to the view model, so you can disply the dialog in the view without a problem. You pass the response from the dialog to the EventArgs of your event so that the view model has the data it is looking for in order to proceed.
For example:
Public Class View
Private WithEvents _VM AS new ViewModel()
Private Sub _VM_AddingItem(Sender AS Object, E AS ViewModel.ItemEventArgs)
Dim Dialog As new SomeDialog()
If Dialog.ShowDialog then
E.Item = Dialog.Item
Else
E.Cancel = True
End If
End Sub
End Class
Public Class ViewModel
Public Sub AddItem(Item AS Object)
Do Some Work here
End Sub
Private Sub _AddItem()
Dim Args AS New ItemEventArgs()
OnAddingItem(Args)
If not Args.Cancel Then AddItem(Args.Item)
End Sub
Protected Sub OnAddingItem()
RaiseEvent AddingItem(me, ItemEventArgs)
End Sub
Public Event AddingItem(Sender AS Object, E As ItemEventArgs)
Public Class ItemEventArgs
Public Property Item AS Object
Public Property Cancel AS Boolean = false
End Class
End Class
Then just wire up your command to the private _AddItem method which just raises the event to collect the necessary data for the AddItem method. I hope this helps :)