Modal dialogs with MVVM and Silverlight 4
In the previous post we covered how to show Views and how to wire them up with their ViewModels while preserving ability to correctly see their preview in Visual Studio and Blend design mode.
In this post i would like to tackle another problem that is very common in MVVM and yet very rarely done correctly.
I will try to provide simple and platform agnostic way of showing Modal Dialogs in MVVM manner without any code in View (later in the post we will reuse same concept and provide way to show simple message dialogs in the same way).
For those of you that have no patience here is the demo application that shows dialogs in action. On the main page there are two identical views each of them showing a list of users. For each user if you click the Edit button you will see the modal dialog to edit users properties.
Del button shows the message dialog to confirm deleting of the user n or to cancel it.
So lets begin: Actual implementation of the concept will be done in Silverlight 4 but it can be easily and without any pain converted to any other UI platform like WPF etc.
Our main design goals are:
- Platform agnostic solution to show modal dialogs that could be easily reused in WPF (or any other platform that supports concept of dialogs)
- We want our modal dialogs to be triggered by some UI event in our Views (like click on the button etc) but not from codebehind of the view
- Actual logic of showing dialogs must reside in ViewModel and NEVER (oh never) in code behind of the dialog (View)
- Dialogs must support MVVM pattern themselves so they should be able to bind to their ViewModel if needed
- Dialogs should be able to return result to the caller (maybe we wont need this always but there must be a way if needed)
Nice thing that could help us a lot is that Silverlight 3/4 comes with very handy ChildWindow control that is perfect fit for what we are trying to do, so we will try to reuse it in smart way.
The question remains how to use Silverlight ChildWindow class with MVVM pattern and to remain decoupled from anything that is platform specific about it.
We will first take the most important things that each Modal Dialog should have and extract this to separate interface IModalWindow:
public interface IModalWindow { bool? DialogResult { get; set; } event EventHandler Closed; void Show(); object DataContext { get; set; } void Close(); }
As you can see IModalWindow is almost like an 1:1 abstraction of ChildWindow class 🙂
But its important to note here that IModalWindow contains only pure .NET constructs so it does not have anything Silverlight specific so that means we can implement this interface and create modal dialog controls in any .NET platform (WPF, WinForms etc).
IModalWindow has Show and Close methods, then it has DataContext so we can bind it to its ViewModel, then there is DialogResult boolean property to tell us if user confirmed the action or not, and it has event handler so we can hook to it when dialog is closed to pickup some results or do other actions (if we need it).
In order to be able to actually show dialogs that implement IModalWindow to our user we will introduce another layer of abstraction – it will be service that implements IModalDialogService interface that will have generic methods for showing the dialogs and completely hide the details of the implementation – as we said this should be platform agnostic so interface will contain nothing specific to Silverlight.
here is how the IModalDialogService interface looks:
public interface IModalDialogService { void ShowDialog<TViewModel>(IModalWindow view, TViewModel viewModel, Action<TViewModel> onDialogClose); void ShowDialog<TDialogViewModel>(IModalWindow view, TDialogViewModel viewModel); }
Our service interface defines two methods, first is one when when we want to specify OnClose handler so that we can do some action when dialog is closed and second overload is for showing modal dialogs in ‘Fire and forget’ mode without knowing when or how the dialog is closed.
Both ShowDialog methods are generic, accepting TViewModel type where we specify type of the ViewModel for our dialog (usually this is ViewModel interface). If we don’t need ViewModel for the dialog we can always set this to null.
So when showing dialog we pass the actual dialog – the View (i will explain later how we will get it) and its ViewModel and optionally we define an Action that is called when the dialog is closed (with the ViewModel instance of the dialog as the parameter – this is how we can get the results of the dialog or data user entered on the dialog etc).
And finally lets see how the Silverlight version of the ModalDialogService is implemented:
public class ModalDialogService : IModalDialogService { public void ShowDialog<TDialogViewModel>(IModalWindow view, TDialogViewModel viewModel, Action<TDialogViewModel> onDialogClose) { view.DataContext = viewModel; if (onDialogClose != null) { view.Closed += (sender, e) => onDialogClose(viewModel); } view.Show(); } public void ShowDialog<TDialogViewModel>(IModalWindow view, TDialogViewModel viewModel) { this.ShowDialog(view, viewModel, null); } }
Implementation as you can see is very simple.
Service just sets the passed ViewModel to the DataContext of the passed dialog view, attaches the OnClose handler if we provided it and finally shows the view.
Now lets see the XAML and codebehind of one simple dialog we defined in sample application – dialog to edit user detials:
<controls:ChildWindow x:Class="MvvmModalDialogs.Views.EditUserModalDialogView" xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation" xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006" xmlns:d="http://schemas.microsoft.com/expression/blend/2008" mc:Ignorable="d" xmlns:controls="clr-namespace:System.Windows.Controls;assembly=System.Windows.Controls" xmlns:DummyViewModels="clr-namespace:MvvmModalDialogs.ViewModels.DummyViewModels" Width="400" Height="300" Title="Edit User" d:DataContext="{Binding Source={StaticResource viewModel}}" > <controls:ChildWindow.Resources> <DummyViewModels:DummyEditUserModalDialogViewModel x:Key="viewModel" ></DummyViewModels:DummyEditUserModalDialogViewModel> </controls:ChildWindow.Resources> <Grid x:Name="LayoutRoot" Margin="2"> <Grid.RowDefinitions> <RowDefinition /> <RowDefinition Height="Auto" /> </Grid.RowDefinitions> <TextBlock Text="{Binding Path=User.FullName}" HorizontalAlignment="Center" VerticalAlignment="Center" /> <StackPanel Orientation="Horizontal" Grid.Row="1" HorizontalAlignment="Right" VerticalAlignment="Center"> <Button x:Name="CancelButton" Content="Cancel" Click="CancelButton_Click" Width="75" Height="23" HorizontalAlignment="Right" Margin="0,5,20,5" /> <Button x:Name="OKButton" Content="OK" Click="OKButton_Click" Width="75" Height="23" HorizontalAlignment="Right" Margin="0,5,20,5" /> </StackPanel> <Grid Height="142" HorizontalAlignment="Center" Margin="30,17,0,0" Name="grid1" VerticalAlignment="Center" Width="310"> <Grid.RowDefinitions> <RowDefinition Height="32*" /> <RowDefinition Height="33*" /> <RowDefinition Height="32*" /> </Grid.RowDefinitions> <Grid.ColumnDefinitions> <ColumnDefinition Width="84*" /> <ColumnDefinition Width="31*" /> <ColumnDefinition Width="195*" /> </Grid.ColumnDefinitions> <TextBlock Grid.Row="0" Grid.Column="0" Height="23" HorizontalAlignment="Right" Text="User Id:" VerticalAlignment="Center" /> <TextBlock Height="23" HorizontalAlignment="Stretch" Text="{Binding User.Id}" VerticalAlignment="Center" Grid.Row="0" Grid.Column="2" /> <TextBlock Grid.Row="1" Grid.Column="0" Height="23" HorizontalAlignment="Right" Text="Username:" VerticalAlignment="Center" /> <TextBox Height="23" HorizontalAlignment="Stretch" Text="{Binding User.Username, Mode=TwoWay}" VerticalAlignment="Center" Grid.Row="1" Grid.Column="2" /> <TextBlock Grid.Row="2" Grid.Column="0" Height="23" HorizontalAlignment="Right" Text="Is Admin:" VerticalAlignment="Center" /> <CheckBox Height="23" HorizontalAlignment="Stretch" IsChecked="{Binding User.IsAdmin, Mode=TwoWay}" VerticalAlignment="Center" Grid.Row="2" Grid.Column="2" /> </Grid> </Grid> </controls:ChildWindow>
So its just simple ChildWindow control, that has few TextBoxes to show and edit details of one User instance.
And in the codebehind of the ChildWindow control we just mark it as implementor of our IChildWindow interface (and we set the DialogResult to true if user clicked OK button, but this can be done in ‘pure’ way via DelegateCommand i just used codebehind here for simplicity and because its not part of the business logic, its concern of the UI to tell us if the user closed dialog by clicking on OK button or not).
public partial class EditUserModalDialogView : IModalWindow { public EditUserModalDialogView() { InitializeComponent(); } private void OKButton_Click(object sender, RoutedEventArgs e) { this.DialogResult = true; } private void CancelButton_Click(object sender, RoutedEventArgs e) { this.DialogResult = false; } }
So all this gives us a completely platform agnostic solution to modal dialogs, and yet we used the powerful ChildWindow control that comes with Silverlight.
If we later decide to implement our modal dialogs in a different way we can do this easily just by creating a different control that implements IChildWindow and we are cool, nothing needs to be changed in our code that shows the dialogs.
So now how would we use this dialog in code?
There is one more peace of the puzzle to solve. We need to register our dialog controls somehow so we can easily pass them to our IModalService to show them so i will use ServiceLocator pattern for this.
We will create a Bootstrapper class that will be called in App.xaml.cs on application startup and this class will have task to register all common dialogs that we will be using in our application:
public class Bootstrapper { public static void InitializeIoc() { SimpleServiceLocator.SetServiceLocatorProvider(new UnityServiceLocator()); SimpleServiceLocator.Instance.Register<IModalDialogService, ModalDialogService>(); SimpleServiceLocator.Instance.Register<IMessageBoxService, MessageBoxService>(); SimpleServiceLocator.Instance.Register<IMainPageViewModel, MainPageViewModel>(); SimpleServiceLocator.Instance.Register<IModalWindow, EditUserModalDialogView>(Constants.EditUserModalDialog); } }
As you can see we are registering (among other things) EditUserModalDialogView that is in fact ChildWindow control as implementor of IModalWindow interface under a name from constants (Constants is the class holding distinct string names for each of the dialog types we want to use in application).
So now that everything is set, and our ServiceLocator knows of our dialogs, we can retrieve new instance of any of our dialogs to show them from the ViewModel of some view (when some button is clicked inside a DelegateCommand etc).
In this example i have only one dialog type that is implementing IModalWindow interface – EditUserModalDialogView, but i could have had many different types of dialogs that i will be showing to users so i would register them all in the Boostrapper as implementors of IModalWindow but under different named constants.
Then i would just retrieve the one i need by asking my service locator for a type that implements IModalWindow and supplying a string key that would determine which dialog exactly i need.
For example to retrieve instance of EditUserModalDialogView i would type this:
var dialog = SimpleServiceLocator.Instance.Get<IModalWindow>(Constants.EditUserModalDialog);
In order to just show dialog without any ViewModel or OnClose callback i would do this:
var dialog = SimpleServiceLocator.Instance.Get<IModalWindow>(Constants.EditUserModalDialog); this.modalDialogService.ShowDialog(dialog, null);
or to set ViewModel for the dialog i could do this:
var dialog = SimpleServiceLocator.Instance.Get<IModalWindow>(Constants.EditUserModalDialog); this.modalDialogService.ShowDialog(dialog, new EditUserModalDialogViewModel { User = userInstanceToEdit });
These were all simple cases. Now lets see how we can show dialog from DelegateCommand in ViewModal when user clicks on some Button in View, and also let’s set a callback function to be called when dialog is closed so we can do something meaningful like updating user details:
First we crate button on View to trigger the action and bind its Command property to a DelegateCommand in ViewModel and use binding for CommandParameter (in this case binding is the actual User instance):
<Button Content="Edit" Command="{Binding Source={StaticResource viewModelLocator}, Path=ViewModel.ShowUserCommand}" CommandParameter="{Binding}" />
And now in the ViewModel in the ShowUserCommand DelegateCommand we place the code to show the dialog and update Users collection afterwards if needed:
this.ShowUserCommand = new DelegateCommand<User>(userInstanceToEdit => { var dialog = SimpleServiceLocator.Instance.Get<IModalWindow>(Constants.EditUserModalDialog); this.modalDialogService.ShowDialog(dialog, new EditUserModalDialogViewModel { User = userInstanceToEdit }, returnedViewModelInstance => { if (dialog.DialogResult.HasValue && dialog.DialogResult.Value) { var oldPos = this.Users.IndexOf(userInstanceToEdit); this.Users.RemoveAt(oldPos); this.Users.Insert(oldPos, returnedViewModelInstance.User); } }); });
So basically im setting the DelegateCommand (see details of the DelegateCommand implementation in the project attached to the post) with code to retrieve the Edit User dialog from the ServiceLocator.
Then im calling services ShowDialog method, passing the new instance of EditUserModalDialogViewModel as the ViewModel of the dialog (containing the user instance i want to edit) and im passing lamda for onDialogClose callback to remove old instance of the User from Users collection on ViewModel and replace it with the new one we received from the dialog.
Off course im first checking if user clicked on Ok button (using dialog.DialogResult property to check that).
And there you have it: simple MVVM solution for modal dialogs that you can port to any platform and implement with any kind of control or popup you want.
To show this concept in more detail i created another similar service to show message boxes to the user.
Again we have the service interface – IMessageBoxService that offers two ways of showing message boxes, first one more complex where we specify message, caption and buttons displayed on the message box and second one, simpler where we just specify message and caption, so only OK button is shown to the user:
public interface IMessageBoxService { GenericMessageBoxResult Show(string message, string caption, GenericMessageBoxButton buttons); void Show(string message, string caption); }
I also created abstractions for the common button types and return results so we don’t depend on Silverlight MessageBox class (that we will use underneath) is using:
public enum GenericMessageBoxButton { Ok, OkCancel } public enum GenericMessageBoxResult { Ok, Cancel }
So lets see how the actual Silverlight implementation of IMessageBoxService interface would look like:
public class MessageBoxService : IMessageBoxService { public GenericMessageBoxResult Show(string message, string caption, GenericMessageBoxButton buttons) { var slButtons = buttons == GenericMessageBoxButton.Ok ? MessageBoxButton.OK : MessageBoxButton.OKCancel; var result = MessageBox.Show(message, caption, slButtons); return result == MessageBoxResult.OK ? GenericMessageBoxResult.Ok : GenericMessageBoxResult.Cancel; } public void Show(string message, string caption) { MessageBox.Show(message, caption, MessageBoxButton.OK); } }
Again its very simple, we are using the built in Silverlight’s MessageBox class to show the actual message box but later we can decide its not good enough and implement it in another way (maybe with 3rd party message box component etc) yet our code in ViewModel wont change since it will be talking only to our interface.
Here is the example of how i use this MessageBoxService in the demo project for this post:
this.DeleteUserCommand = new DelegateCommand<User>(p => { var result = this.messageBoxService.Show(string.Format("Are you sure you want to delete user {0} ???", p.Username), "Please Confirm", GenericMessageBoxButton.OkCancel); if (result == GenericMessageBoxResult.Ok) { this.Users.Remove(p); } });
What i do here is just set the DelegateCommand in ViewModel to first show the message box with Ok and Cancel buttons and if user pressed Ok do some action – delete from the list.
So i think this two solutions covered all design goals we set on the start: we have decoupled way of showing modal dialogs and message boxes currently done in Silverlight but they could be easily converted to another platform.
Also approach is MVVM friendly without any hacks so any pattern purist can be satisfied with it.
If you have some concerns or suggestions on this approach i would be happy to hear it!
Here is the attached VS 2010 solution with full source code from the post.
Live demo of the application showing modal dialogs and message boxes flying around the screen.
How much of this could be done in VS2008 / SL3, and which bits are VS2010 / SL4 specific?
I think there is nothing VS 2010 specific in this code. I used VS 2010 because it has much better design time support for Silverlight.
So feel free to try to convert this to VS 2008 i guess you should not have any problems (if you bump into some issues let me know so i can post warnings here).
Thanks.
Hello! You assert, that it is a platform agnostic decision, but as far as I know, WPF window has method ShowDialog intended for showing itself as modal dialog. While you’re using usual Show method. This would not lead to modal behavior, would it?
Thanks
Hi wazzzuup,
in my opinion it would be weird to name the method on IModalDialogService called ShowModal. Instead i find the naming IModalDialogService.Show() more natural.
From the name of the interface you can get the context (Modal) and Show is the actual command. This code has nothing to do with the WPF ShowDialog window’s method. Its completely different, new platform agnostic implementation so i have chosen my own naming convention. If you don’t like it feel free to rename it to be as you prefer.
Thanks for your feedback!
Dude,
This doesn’t run from your link within your article. Nor does it compile in Vs2k10rc using Silt4rc.
What gives?
I have rebuilt the solution in Visual Studio 2010 RC and uploaded it instead of the old version (for Beta2).
Download again and it will work in RC. (if VS complains about missing references just use Rebuild Solution and it will recompile all the projects).
Same goes for the online demo app, its updated to use latest Silverlight 4 version from the RC.
Thanks for notifying me that it was not working!
Cheers!
Any chance for a VB.NET version of this? 🙂 Would have been nice..
Hardly because i don’t do VB so much…
You can always use of the C# => VB translators 🙂
I’m on it 🙂
Thanx btw, your example is really clean and well documented with the text.
Roboblob, I runned in to a problem with the differences in C# -> VB.NET. In C# you have a view that implements an interface (IModalWindow) like this:
public partial class EditUserModalDialogView : IModalWindow
The interface IModalWindow has a few methods in it, but the EditUserModalDialogView is not forced by VS to implement these (?). So when I try doing the similar thing in VB.NET like this:
Partial Public Class DummyModalDialogView
Implements IModalWindow
I’m beeing forced to implement all methods from IModalWindow in my codebehind for the view, which breaks the MVVM pattern 🙁 So, what am I doing wrong her? 🙂
Wow that is strange.
Can you send me zipped VB solution for VS 2010 so i can look that up.
send to webmaster [at] roboblob [dot] com
thanks!
Solution sent!
Thanx for your effort on this, I’m beginning to regret starting of in VB insted of C# 😉
I’ve encountered the exact same problem as @Mcad010. Did you find a solution or workaround?
Thanks
I´ve spend the last few days looking into the MVVM and dialogs like this example. What puzzles me is that the ViewModel starts opening up GUI elements.
1) How will one unit test the e.g. DeleteUserCmd when this now requires a response from an MessageBox?
2) Another implementation of a View on top of the ViewModel will be forced to inherit different popups when binding to the commands, which seems unintended.
To me it seems that e.g. a delete user confirmation dialog should reside in the View, since it really has no ViewModel relation. Something like the ability to cancel/suppres the Command if the user regrets this.
I just have no idea at the moment on how to do this.
Any comments?
Hi S10,
in my opinion ViewModel should definitely start opening the GUI elements. BUT they need to be abstracted by some interface. So in fact, ViewModel knows not about what kind of GUI element it is or how it works but triggers its showing and waits for response.
To me this is a natural approach. Its application logic to show some question to the user so ViewModel should trigger it and get the response.
In my example real GUI modal window is abstracted by IModalWindow and IModalDialogService and therefore it is decoupled and can be easily tested using RhinoMocks or some stub implementations that would just invoke the Action onDialogClose that ViewModel supplies with some mock value for the purpouse of testing.
If you need realword example on how to test this let me know i can create some post on this subject…
let me know how it went…
Thanks for your feedback its appreciated!
Testing this should be done by mocking the dialog. This can be easily done, since the dialogs are abstracted by an interface. So you can still test the code of the manager and the viewmodels, just not the actual “ok” click on the dialog button.
Yes, my approach on unit testing modal dialogs in Silverlight MVVM can be found in one of my later posts on this blog:
http://blog.roboblob.com/2010/04/21/unit-testing-modal-dialogs-in-mvvm-and-silverlight-4/
Hi
Just wanted to inform you, that the sample (live demo) does not work any longer – generates some kind of ‘beta expired’ message,
cheers
Christian
Hi Christian,
i have rebuilt the live demo application and Visual Studio solution attached to the post in the final version of Visual Studio 2010 and Silverlight 4 so its should be ok now.
Thanks for letting me know!
Hi
Thanks for you reply.
Firstly it´s great to finally read a blogg that actually shows the implementation of the abstacted dialogs that you hear people speek about in different places 🙂
It would be great if you had the time to demonstrate a real world example of how to to unit test these dialogs using RhinoMocs
Its done, you can see real-world example of Unit Testing my Modal Dialogs on this my latest blog post:
http://blog.roboblob.com/2010/04/21/unit-testing-modal-dialogs-in-mvvm-and-silverlight-4/
enjoy!
Hello,
I found your post helpful which explains MVVM pattern usage in dialog box.
I recently posted a similar article, but based on our framework, which we believe provide a more elegant solution to the existing solution.
Our framework includes full-featured routed event, routed command, delegate command, command reference that complies with WPF specification, and a set of UI toolset that sits on the top of that architecture (which is the missing puzzle in most of MVVM libraries out there).
You can check out my in-depth MVVM discussion at http://intersoftpt.wordpress.com/2010/04/24/clientui-part-3-comprehensive-mvvm-framework-for-silverlight-development/
Our objective is to spread this good news as much as possible to Silverlight developers and community. I hope you can help us by mentioning/ping back to my article from your blog.
Let me know if you have any thoughts or feedback.
Many thanks in advanced,
Jimmy.
Yes i see you have some nice things in the ClientUI.
Any idea when will this available to the public?
thanks for sharing!
Hey
Thanks for posting the Unit test example. Nice streamlined solution 😀
Dude,
I’m going to up the ante by asking you to re-do this thing using MEF.
Hi. I just wanted to know if you could provide some guidance on how to implement this solution with the MVVM Light Toolkit. This framework is being very used in a lot of Silverlight applications and it would be great if you could provide some guidance on how to integrate it on your solution.
Thanks a lot for this great article!
Hi,
there is plenty of information on MVVM Light Toolkit on the web so i don’t think i will be doing that.
I’m trying to build my own framework so i have no time or wish to integrate my solution with other frameworks.
But it should be straight forward to adjust it so give it a try!
Good luck,
Slobo
Hi Slobo,
I modified dialog service whenever moved from CAB to Prism and MVP and MVVM. Your idea looks clean and free from wpf and silverlight.
Thank you so much.
Thank you for your feedback,
im glad it works good for you.
Its always good to abstract the framework (Sl, WPF) and work purely with interfaces, whatever there is behind them…
Cheers!
Hello,
First thanks for the sample. Next, I took what you have and modified it utilize MEF instead of needing the Unity container.
I can send the source. I may post it on my website when I have time.
Thanks
Hi frosty,
i would love to see this example with MEF when you post it somewhere definitely post here a link to the article/code.
Thanks for your feedback!
Hi thanks for the post… I’m using PRISM with MVVM. My question is, can we refer another viewmodel from the parent viewmodel like in the following code.
this.modalDialogService.ShowDialog(dialog, new EditUserModalDialogViewModel
i know this can be done using viewmodel interface and unity. i have modal dialogs showing upo 5 levels (one dialog showing another). so is it ok to have this approach/whats the best solution to tackle the problem. i’m using a controller class to navigate/inject the main views.
Hi Saheer,
in my opinion there is nothing wrong with showing multiple levels of modal dialogs from architecture perspective but it is little weird from usability standpoint (but thats another question).
Your code to show dialog from other dialog looks ok except of the part where you are manually creating EditUserModalDialogViewModel (using the new keyword).
I think this should be avoided and you should not manually create instances of classes (except for builtin .net classes) and you should instead always use Service Locator or Dependency Injection patterns to get new instances of your ViewModels.
If you need to manually create a ViewModel then at least ‘ask’ the Service Locator for the instance of the ViewModel and then set it up and use it how you want. Something Like this:
var viewModel = ServiceLocator.Instance.Get();
this.modalDialogService.ShowDialog(dialog, viewModel);
this way you delegate the responsibility of creating Viewmodel to the centralized service (in this case its ServiceLocator) and then if later you have some changes in the constructor of that ViewModel its not your problem, ServiceLocator will handle this for you and you dont need to change your code.
If you want to see how Service Locator works check my code examples im always using it (underneath the covers of Service Locator im using Microsoft Unity IOC to create those instances).
Hope this helps…
Thanks for your feedback.
Cheers!
You’ve got a bug in your ModalDialogService class.
You continual subscribe to the Close event and never unsubscribe.
Set a breakpoint in your MainPageViewModel at this point.
this.ShowUserCommand =
new DelegateCommand(userInstanceToEdit =>
{
this.modalDialogService.ShowDialog(_editUserDialog, new EditUserModalDialogViewModel
{
User = userInstanceToEdit
},
returnedViewModelInstance =>
{
if (_editUserDialog.DialogResult.HasValue && _editUserDialog.DialogResult.Value)
{
//var oldPos = this.Users.IndexOf(userInstanceToEdit);
//this.Users.RemoveAt(oldPos);
//this.Users.Insert(oldPos, returnedViewModelInstance.User);
}
});
});
Click the Edit button on an item. Each time you click the edit button, it registers for close and this code will fire ever how many times a user has clicked the edit button.
For now, I’ve created a List in my IModalWindow interface that I make the View implement. The code in the ShowDialog function that takes in onDialogClose looks like this.
public void ShowDialog(IModalWindow view, TDialogViewModel viewModel, Action onDialogClose)
{
view.DataContext = viewModel;
if (onDialogClose != null)
{
foreach (var evt in view.ClosedSubscribers)
{
view.Closed -= evt;
}
view.ClosedSubscribers.Clear();
EventHandler ev = (sender, e) => onDialogClose(viewModel);
view.Closed += ev;
view.ClosedSubscribers.Add(ev);
}
view.Show();
}
Hi Frosty,
i dont think this is really bug, since in my implementation im always creating new instance of EditUserModalDialogViewModel when im showing the dialog.
So for me Closed event never fires twice since its always different instance (while old one is garbage-collected).
If you are using always same instance of ViewModel then this could be an issue.
Nicer way to handle this would be like this:
public void ShowDialog(IModalWindow view, TDialogViewModel viewModel, Action onDialogClose)
{
view.DataContext = viewModel;
if (onDialogClose != null)
{
EventHandler closedHandler = (sender, e) => onDialogClose(viewModel);
view.Closed += (sender, e) =>
{
view.Closed -= closedHandler;
onDialogClose(viewModel);
};
}
view.Show();
}
Hope it helps!
good luck with your coding.
Here’s a link to the sample I worked with. Again note, the views don’t show up properly in the designer as the code is. But it does use MEF versus Unity. I’ll let somebody else solve the blendability problem. Or send me suggestions and I’ll add it to this.
Also, I have a fix in this sample for the close event I mentioned above.
There’s got to be a better way but this cost me some time tracking it down and had to come up with something and move on.
Thanks.
Thanks Robo,
I tried implementing the code to handle the event situation and no go. Same issue.
I consider it a bug because if used as part of a framework, I think it should account for all situations. I was in a hurry when I created the MEF version. But it doesn’t matter if I use shared or nonshared for exporting the viewModel, the same problem exists.
It cost me about 3 hours yesterday trying to figure out why my app was having all kinds of quircky problems and I finally remembered adding this code and it was the culprit.
Great idea but just wanted to save others the hassle I went through yesterday.
The clean up you posted, the Event handlers are different and i think that’s why it doesn’t work. Everything I’ve seen is you have to keep a reference to the handlers you add so you can remove them.
Hi Frosty,
can you send me on email webmaster [at] roboblob dot com some small example of this bug since i cannot reproduce it in my code it always fires once.
thanks!
Hello Frosty,
Where can I find the fix to the MEF solution for the close event issue.
Best.
Forgot to add the link to the MEF version.
http://www.dotnetpatterns.net/entries/15-MVVM-Modal-window-using-MEF
Thanks for posting, nice work.
Hello frosty,
Can you tell me how to fix the close event, as I have it fired as much as the window open (using MEF).
Kind regards
Wouldn’t it be more appropriate to rename the interfaces from IModalWindow to IChildWindow and from IModalDialogService to IChildWindowService? Technically I don’t see anything that would prevent this to be used for non-modal (modeless) dialogs as well.
well i disagree that this should be called IChildWindow since purpose of this entity and the service IModalDialogService is deliberately determined by its name: to show modal dialogs that block UI until user chooses some options or clicks Cancel. I like to call entities in my code with strict names, its a good practice because when entity is called with precise name then he does exactly that and you don’t have temptation then to add other features to it. If you start calling classes with general names then its easy to add more functionality to them and then they soon become ‘Swiss knife’ kind of classes that do everything.
If i would need non-modal dialog then i would introduce IDialog and IDialogService same as i did introduce IMessageBoxService etc.
All this may sound too strict but practice learned me that strict naming conventions lead to better code.
Hope this clarifies why i named the classes as i did.
thanks for your feedback!
I find it interesting from a MVVM perspective that the ChildWindow has a Closing and Closed event, but not a Showing or Showed/Shown event. I needed to do some behind the scenes book keeping for child dialogs and at first was using an attached behavior in the xaml to quietly do the work, but was eventually forced to write a modal dialog base class to do the work, and then update all the modal dialog code and xaml files for the subclassing.
Hi!
Thank you for the post and the ideas (and the implementation)!
I am trying to work my way through calling non-modal dialogs though service classes using your approach. I am using the FloatableWindow (http://floatablewindow.codeplex.com/) and there is a problem: Basically you cannot show a FloatableWindow without setting theproperty public Panel ParentLayoutRoot { set; get; } to the parent of the Dialog. I am using MVVM and so would like to avoid references to GUI elements in my ViewModels and service classes. I would really appreciate it if you can make a suggestion how to solve the problem!
I never used FloatableWindow but it looks like useful control.
Now if you want to avoid depending on Gui elements in ViewModels what you could do is to make your View implement some interface and and then pass reference of the
View in your view model.
View interface can have property FloatableWindowParent of object type and you could use it when showing FloatableWindow.
So something like this in your ViewModel:
var fWindow = new FloatableWindow();
fWindow.ParentLayoutRoot = this.View.FloatableWindowParent;
fWindow.Show();
this way there are no UI dependencies in your ViewModel and still you have somethig to set for windows ParentLayoutRoot property.
let me know if that helps.
Thanks for sharing your good work. I downloaded the project and opened MainPageVIew.xaml in VS2010 IDE but got message “Could not load file or assembly ‘System.Windows.Interactivity, Version=2.0.5.0, Culture=neutral, PublicKeyToken=31bf3856ad364e35’ or one of its dependencies. Operation is not supported. (Exception from HRESULT: 0x80131515)” Can you tell me how to fix it?
Hi Jim,
its probably some local configuration problem on your machine.
I just checked the solution in VS2010 and it works perfectly for me.
The System.Windows.Interactivity.dll that you have problems with is located in \Libs\Blend\ folder so maybe try to remove and add it again to the projects that need it from that folder.
Good luck!
Hi, I’m silverlight novice. Can we add ria service to your project?
Hi Jim,
probably you could add RIA to this sample, important is to somehow keep it hidden behind an interfaces so it does not leak into your ViewModels.
RIA is not really my favorite choice so i dont plan currently adding it to my posts but who knows.
If you manage to make it working please share the code so we can all learn from it.
Good luck!
Be hornest. I don’t I have the ability to do that at the moment. I just start to explore these new stuff and I’m still not in a clear direction to go. Luckily I found your posting and I just converted in to vb line by line to get a better understaning how it work. Unfortunately compilation is okay but no data is showing up. I guess I still have a long way to go.
hm, VB does not support implicit interface implementation like C# so this could be the source of your problems:
http://forums.silverlight.net/forums/p/176250/396838.aspx
if you have questions regarding my code feel free to ask.
Hi roboblob,
Quick questions. If I don’t use RIA, where do I put metadata (generated by RIA) in your sample? Do I put it in Models folder?
Yes Models folder sounds like good idea.
This is working great for me except for one item. I am using Silverlight 4. Please excuse my wording. In my xaml, I have a combo box that uses a trigger (calling the invokecommand) to fire the selection changed event.
The error I am getting is that DelegateCommand requires 1 type argument. Not sure how I would pass in the type in the trigger.
Hi Alphalima,
can you paste some code? how do you define your DelegateCommand in your ViewModel and how you use it in XAML?
its hard to help if i dont see how it looks in code.
If you want send the zipped solution to my email webmaster aT roboblob.com
So how would you implement this in WPF? Since it does not have the ChildWindow class? What would the Type of your View be?
It could be easily done with a WPF Window class instance used as ChildWindowClass implementation.
We would use windowInstance.ShowDialog() to display it.
Most of the code from my Silverlight example could stay same.
Maybe in the future i will build small post with example on how to do this in WPF.
If one were to remove the code from the dialog’s codebhind as you suggest is possible, how would one replace the OKButton_Click with an ICommand in the VM AND get the VM to physically close the dialog window?
I’m struggling to see how the VM can reference the view to instruct it to close.
If you look at the code you can see that problems you mention are already solved by possibilities of IModalDialogService implemented for this tutorial.
When developer wants to show the dialog, he passes the ViewModel instance that will be used by the dialog.
So that ViewModel could hold ICommand for closing the dialog – but i see nothing wrong with the code behind closing the dialog, this code is irrelevant
and UI driven, ViewModel does not care how dialog closed, but just when did it closed.
Also ViewModel instance from the View is returned to the caller when dialog is closed – in the callback that user passes, so that way user has all the control.
Its kind of Fire and Forget approach, you show the dialog, and get notified when its closed with all the information needed (ViewModel in this case).
So this should solve most of the problems you mentioned in your question.
Feel free to ask further if i failed to understand the problem properly.
Thanks for your feedback!
Thanks for the reply Rob and I fully understand your approach and so far it is the nicest I’ve used IMO. Other approaches of decoupling modal windows I’ve resorted to in the past are far more heavyweight e.g. using the mediator pattern and passing ShowDialog messages or using Prism’s event aggregator.
For the simple task of showing a modal dialog this is the simplest and cleanest, whilst still remaining testable.
I was wondering from a purist sense, if it is possible to completely remove all code-behind from the View including the OK and Cancel button click event handlers. Even with the Close ICommand, the view must be told to close. The VM has no reference to the view apart from indirectly through bindings.
In order to close the view, one needs to set the DialogResult property to something.
So I guess to achieve this one would need to somehow create a binding from a property in the VM to the view’s DialogResult property. Unfortunately DialogResult is not a dependency property so I don’t think it’s actually possible.
Hi Rob, excellent example – really helped me out. I’m in a similar position with Simon (above). I’m not a purist or anything and believe that code-behind is ok if it gets the job done. However I’m trying to find a way to close a modal window from the modal window’s view model.
The case example for this is the modal window has a datagrid of search results, when the user double-clicks a result I fire a message on the event aggregator for some other section of code to pick up and then need to close the dialog.
Hi Jordan,
one way of solving this would be to have ViewModel of the Dialog to have reference to its View via the interface IModalWindow (just add to the ViewModel interface View property of type IModalWindow).
You could then assign the value of that View property in ModalDialogService when you bind View and ViewModel:
view.DataContext = viewModel;
viewModel.View = view;
After that, in your ViewModel when you need to close the Dialog just call view.Close();
Close method is already part of the IModalWindow interface that ChildWindow implicitly implements so no need to implement it explicitly in each dialog.
Let me know if this helps and thanks for your feedback!
In this case I might even consider having the implementation of IModalWindow also implement something like an IClosable interface. That way, in a purist way, the viewmodel is passed an IClosable type (which is, let’s face it, still a UI concept in this case) instead of an IModalWindow type.
To think out loud a bit more on this…
What we’re suggesting here is that the viewmodel has some need to cancel, stop, close, or otherwise end some communication channel with an external entity. In this case that entity is wetware (human). In a test it would be some mock implementation. It could also be another service or even a robot for all it cares. The point is that the “interface” defined by the viewmodel requires a “closable” communication channel. This is interesting to think about but many times, from a higher point of view, the architecture may be changed so that this requirement or notion is no longer necessary or desired.
And, this is a fantastic article! Thanks for sharing!
How do you validate the values ? With the red box surrounding the ones which are invalid. Something like which is mentioned here :
http://msdn.microsoft.com/en-us/library/system.windows.data.binding.validatesondataerrors.aspx
I think wiring up interface from the view model is so WRONG.
Please read dicussion from
http://compositewpf.codeplex.com/discussions/69088
Hi,
I love your Modal dialog sample. I would love to see a WPF version of this code. Can you create one for us?
Regards,
John
Simon and Jordan: I found a funny hacky way of removing the event handlers in the view and have them in the ViewModel, here you go 😉
public ICommand CloseCommand
{
get
{
return new DelegateCommand(
sender => Application.Current.Windows.OfType().First().Close());
}
}
Enjoy
great job, thanks
I’m trying to use this in WPF. However I’m running into the problem that the Parent must be set for it to work correctly. Having access to the parent view means delegating the responsibility of window creation to the parent, not the child, so that would require a considerable design change to make it work.