WPF: Managing windows (opening, closing, etc...) in MVVM? - wpf

I've read about it in a bunch of places. Most of the people are referring to these two links:
How do I handle opening and closing new Windows with MVVM?
http://waf.codeplex.com/
I don't understand either of them. I am a beginner when it comes to MVVM. Some people are mentioning controllers when it comes to window manipulation in MVVM. What are those and how are they implemented? By book, MVVM is consisted of model, viewmodel and view - where do controllers come in?
If someone could provide a sample of the following use case, that would be terrific (for all those people who are just getting started with this, as I am):
Prerequisite: A window is opened.
User clicks a button.
New window is opened and some data is passed to that window, i.e. some string.
New window is closed (or a button is clicked) and some data is passed to the first
window.
Passed data has changed something on the window.

The ViewModel to ViewModel communication is usually handled by an implementation of the Event Aggregator pattern.
MVVM Light uses the Messenger class, Prism has another implementation but basically that is one way to send messages between View Models without coupling.
There are some examples, and Articles describing the usage.
I would suggest taking a look at it.
Regarding the controllers stuff in WPF, I don't know.
Regarding the example:
-I Have a Windows with its WindowsViewModel. This class should have a Command bound to the Button.
-The user clicks the button. Executes the command.
-The Command opens a new Window.
Here you should create the Dialog View Model and somehow you should create the Window. Or create the Window with a ViewModel, but ViewModel shouldn't know much about the View otherwise is not testable.
We use something like this because we have some requirements, but it
could be much much simplier, it happens it's the only example I have at hand:
bool? ShowDialogImpl<TViewModel>(Action<TViewModel> setup) where TViewModel : ViewModel
{
return (bool?)DispatcherHelper.UIDispatcher.Invoke(
(Func<bool?>)(() =>
{
var viewModel = viewModelFactory.Get<TViewModel>();
viewModel.ViewService = this;
setup(viewModel);
var window = new Window
{
Owner = this,
SizeToContent = SizeToContent.WidthAndHeight,
WindowStartupLocation = WindowStartupLocation.CenterOwner,
Content = ViewFactory.CreateView<TViewModel>(),
DataContext = viewModel,
WindowStyle = WindowStyle.ToolWindow,
ShowInTaskbar = false
};
window.SetBinding(TitleProperty, new Binding("Title"));
openDialogs.Push(window);
window.Activated += (sender, args) => window.SizeToContent = SizeToContent.Manual;
var result = window.ShowDialog();
openDialogs.Pop();
viewModelFactory.Release(viewModel);
return result;
}));
}
Basically: We create a window and with a view model.
The view model is created from a factory using an container.
The setup Action delegate is the entry point for our data.
Communication:
The first Windows is a Grid, and the second Dialog to edit data of the grid.
Inf the Windows we have:
messenger.Register<EntityUpdated<FooClass>>(this, message => UpdateItem(message.Entity));
And in the Dialog:
messenger.Send(new EntityUpdated<FooClass>(subject));
This way, we know when something was updated in the Edit Dialog in order to refresh the grid.
Hope this help you :)

If you aren't planning on allowing the user to switch back and forth between the windows, while both are open (i.e., the first opens the second, and the second must be closed to return to the first), you could simply set the viewmodel for both windows to the same viewmodel instance, and open the 2nd window as modal, and the strings you are passing back and forth, would simply be properties of the view model, with data bindings to something in the view.

Related

Intercept Deactivation Strategy of a Closing window?

I'm using the latest version of Caliburn Micro (4.0.173) in an application leveraging IoC.
I do not have a sample project yet has my sandbox is using nugets from Telerik that are not easily findable. But I think a detailed explanation can get the point across:
I have a ProfileEditorViewModel : Conductor<IScreen>.Collection.OneActive resolved by the container (singleton) through the constructor of my ShellViewModel and I use the I use the WindowsManager to open my ProfileEditorViewModel in a different window:
public class ShellViewModel : IShellViewModel{
private readonly IProfileEditorViewModel _profileEditor;
private readonly IWindowManager _windowManager;
public ShellViewModel(IWindowManager windowManager, IProfileEditorViewModel profileEditor){
_windowManager = windowManager;
_profileEditor = profileEditor;
}
//Method triggered by an Action from the View
public void OpenProfileEditor(){
_windowManager.ShowWindowAsync(_profileEditor, null, null);
}
}
When I close the ProfileEditor window (clicking on the top right red cross), it triggers a closing type deactivation, which is coherent with the implementation of a conductor (not being conducted itself) being shutdown. The Screens collection is being wiped and some more cleaning is being done through the Screen.cs implementation of Caliburn.
However, for this application, I'm facing "the view not being unbound" issue (https://github.com/Caliburn-Micro/Caliburn.Micro/issues/361) and next time I open the ProfileEditor, it'll bind a new view (because the Views Collection of my viewmodel was empty) and it create some issue related to UI components used in the view (basically one event from the viewmodel triggers similar actions, on the view(s) side, that all come back to the viewmodel, which create some identification issue)
Reading through the issue 361, I'm currently able to catch the Unloaded event of my ProfileEditor (once closed), and basically clean the DataContext of any view associated to the viewmodel (before Screen cleans the Screens collections). Next time I open the ProfileEditor, it'll bind a new view and it'll be the only one = it works.
However clearing the DataContext may produce some other issue down the road.
What I would like to do, is to avoid clearing the View collection of the ProfileEditorViewModel upon closing. So Caliburn can use this reference the next time it needs to resolve a window/view for the ProfileEditorViewModel (instead of looking for a new one).
Is it possible to intercept the Deactivation / Closing strategy and change the close parameter to false ?
Another solution may be for my ShellView to be a Conductor<>.Collection.AllActive but I can't wrap my head around teh management of a window closing. How to intercept the ProfileEditor windows closing and proceeding with a deactivation of the ProfileEditorViewModel instead of a closing.
I hope it make sense :)
Thank you in advance for your help

How to open a modal window the MVVM way

I'm still trying to get a grip with WPF and MVVM and stumbled upon questions, where the easy way seems to violate the MVVM rules.
As far as I understood it, in MVVM the view model is not allowed to know about the view. So if I put views and view models in different assemblies, the view model assembly is not allowed to reference the view assembly.
So, here are my questions:
Let's say I have a MainView and a corresponding MainViewModel. The MainView has a button that is supposed to close the window and do some serious stuff before closing (for instance saving data and configurations).
This button is bound to a CloseCommand in the MainViewModel, but how do I know in the MainViewModel which view to close.
My easy (and wrong) ways would be either to give the MainViewModel an instance of the MainView on initialisation or to give the view-to-close as a command parameter.
But both solutions violate the MVVM rule.
Let's say my MainView contains a TextBox bound to a string property of my MainViewModel. If the text, the user types in the TextBox, isn't unique enough, I'd like to open a new modal window in which the user can select his input out of various choices.
To open a modal window in WPF you have to put the MainView in the Owner property of the new window. Therefore you need a reference to the MainView in the MainViewModel.
Same problem exists, if you try to show a modal message box (a message box that is tied to the parent window and can't be put behind the parent window through mouse clicks or other user actions).
So, how is one supposed to do actions in the view model when you need knowledge about the view?
Thanks for your help.
It seems that you wish to close the main window, and that is perfectly fine. As many things, this can be solved with a new level of abstraction. Lets say you have an interface called IApplication with a method Close(). And then lets say you have an implementation of the interface that might look something like:
class ApplicationWrapper : IApplication
{
public void Close()
{
Application.Current.MainWindow.Close();
}
}
Couldn't you inject the IApplication interface in the view model and call its Close method from the command?
I would propose MVVM Dialogs, but I am biased since I also am the author.

Is this a bad way to create a dialog in WPF and MVVM?

I have a WPF application that uses the mvvm pattern and I need to show a dialog from my principal view model.
I have a secondary view with its view model, and I think that I have two options to create the dialog.
OPTION 1
In the principal view model I can do the following:
Create the view of the dialog
Create de view model of the dialog
Assign the view model to the view
ShowDialog
OPTION 2
In the principal view model:
Create the view model of the dialog
In the constructor of the view model of the dialog:
Create the view of the dialog
Assign the view model to the view. In this case is assign "this"
ShowDialog
I know that in MVVM the view model has to know nothing about the view, but in the second option, in practice, how the view model has not any property that link to the view, only is create an showed in the constructor, in the final state the view model does not know nothing about the view.
However, I think that the code in the principal view is clearer, because I only need to create the view model, only one line of code, instead of the option 1 that need 4 lines (create the view, cretate the view model, assign the view model to the view and show the dialog).
Am I wrong in thinking that the second option is not a bad idea if I want to follow the mvvm pattern?
i do this for dialogs in wpf
var result = this.uiDialogService.ShowDialog("Dialogwindow title goes here", dialogwindowVM);
... do anything with the dialog result...
Well as in most cases, there are several approaches to implement a solution.
For larger applications with several dialogs, windows or pages the best approach would be a service e.g. IDialogHandler. Which is given to the VMs via c_tor. You can find an example in this post.
Big advantage is the decoupling which makes the VMs easy to test, because they have no references to the views.
For smaller applications with let's say 2 or 3 different dialogs the following approach would also be proper.
public class DialogView : Window
{
// a method to create easily a dialog
public static void ShowDialog(DialogViewModelBase dialogVm)
{
var dialog = new DialogView { DataContext = dialogVm };
dialog.ShowDialog(); // pls note, that this will create a modal dialog
}
}
So DialogView.ShowDialog(...) could be called from everywhere. But here the VMs have a references to the views.
This is similar to your OPTION 1, well ok the step order is not the same, but it's close enough.
Whatever way you chose there's one trouble.
Frankly speaking it relates not only to dialog but to all windows. And in MVVM this can become a real pain. At least for our team when we use both WinForms and WPF.
Most of the developers forget about parent-child relationships of the windows.
Concerning dialog windows this causes somewhat poor UX: it is possible for the dialog to go beneath the main window which is unresponsive because of the shown dialog. (you can easily repoduce it).
So, in MVVM you need somehow set the parent. And what is important here is tha View is not necessary a Window.
Here how we do it.
We preffered ViewModel-first approach. But we do not inject View in VM. We pass IViewService which is repsonsible for View instantiation, parent-child relationships and listening for some events.
Think about how yor VM can stop its View from closing when user clicks cross at the upper right corner.

MVVM - How do I open a window based on a selected item from a list?

I am quite new to the WPF & MVVM world, and have spent the last few days downloading as many tutorials as I could, and reading as much as possible!
I am however struggling to implement with a very basic and common concept with MVVM and am desperate for some help - and perhaps even an example :-)
I've got the basics of M-V-VM, commanding, and even messenging; but how on earch do I open a new window and set that windows ViewModel to the selected item of a listbox?
Let me explain:
I have a Model called Client and it has some properties
I then have a ViewModel which gets all my Clients and stores them in an ObservableCollection
I have a screen where I display the Name & Surname in a listbox and allow the user to filter and search.
All of the above work perfectly.
When a user selects an item though, I would like to open an editable "detailed client view" screen of that particular client. This detailed screen's ViewModel would need to somehow bind to the selected item (if that makes sense); or I need to be able to pass a paramter to the newly opened screen's ViewModel. In fact, if the user could open several detail screens at the same time and edit multiple clints it would be great!
If anyone can give me a nice example, or point me in the right direction I would truly be greatful!
Many thanks,
Brendan
I would add an event to the ListBox.SelectionChanged which does the following:
Creates a new Dialog and DialogViewModel
Binds the DialogViewModel.EditableContentProperty to the ListBox's SelectedItem
Dialog.DataContext = DialogViewModel
Dialog.ShowDialog()
Simply put:
//Create the Client Detail form
frmClientDetails frm = new frmClientDetails();
frm.Owner = this;
var ViewModel = new ClientDetailViewModel((Client)lstFoundClients.Items.CurrentItem);
frm.DataContext = ViewModel;
frm.Show();

Open dialog in WPF MVVM

I have an application that need to open a dialog from a button where the user enters some information.
At the moment I do it like this (which works fine)
The button click generates a command in the ViewModel.
The ViewModel raises an event which the Controller listens to.
The Controller works out the details of the new window (i.e. View, ViewModel & model) and opens it (ShowDialog)
When the window is closed the Controller adds the result to the eventargs and returns to the ViewModel
The ViewModel passes the information to the Model.
There are a lot of steps but they all make sense and there is not much typing.
The code looks like this (the window asks for the user's name)
ViewModel:
AskUserNameCommand = DelegateCommand(AskUserNameExecute);
...
public event EventHandler<AskUserEventArgs> AskUserName;
void AskUserNameExecute(object arg) {
var e = new AskUserNameEventArgs();
AskUserName(this, e);
mModel.SetUserName(e.UserName);
}
Controller:
mViewModel.AskUserName += (sender,e) => {
var view = container.Resolve<IAskUserNameView>();
var model = container.Resolve<IAskUserNameModel>();
var viewmodel = container.Resolve<IAskUserNameViewModel>(view, model);
if (dlg.ShowDialog() ?? false)
e.UserName = model.UserName;
}
My question is how the horizontal communication works in the MVVM pattern.
Somehow it seems wrong to let the controller be involved in the data transfer between the models.
I have looked at the mediator pattern to let the models communicate directly. Don't like that idea since it makes the model depending on implemetations details of the GUI. (i.e. if the dialog is replaced with a textbox, the model need to change)
I don't like most of the current suggestions for one reason or another, so I thought I would link to a nearly identical question with answers I do like:
Open File Dialog MVVM
Specifically the answer by Cameron MacFarland is exactly what I do. A service provided via an interface to provide IO and/or user interaction is the way to go here, for the following reasons:
It is testable
It abstracts away the implementation of any dialogs so that your strategy for handling these types of things can be changed without affecting constituent code
Does not rely on any communication patterns. A lot of suggestions you see out there rely on a mediator, like the Event Aggregator. These solutions rely on implementing two-way communication with partners on the other side of the mediator, which is both hard to implement and a very loose contract.
ViewModels remain autonomous. I, like you, don't feel right given communication between the controller and the ViewModel. The ViewModel should remain autonomous if for no other reason that this eases testability.
Hope this helps.
i use this approach for dialogs with mvvm.
all i have do do now is call the following from my viewmodel to work with a dialog.
var result = this.uiDialogService.ShowDialog("Dialogwindow title goes here", dialogwindowVM);
I have come across similar problems. Here is how I have solved them, and why I have done what I have done.
My solution:
My MainWindowViewModel has a property of type ModalViewModelBase called Modal.
If my code needs a certain view to be modal, it puts a reference to it in this property. The MainWindowView watches this property through the INotifyPropertyChanged mechanism. If Modal is set to some VM, the MainWindowView class will take the VM and put it in a ModalView window where the appropriate UserControl will be shown through the magic of DataTemplates, the window is shown using ShowDialog. ModalViewModelBase has a property for DialogResult and a property called IsFinished. When IsFinished is set to true by the modal VM, the view closes.
I also have some special tricks for doing interactive things like this from backgroundworker threads that want to ask the user for input.
My reasoning:
The principle of modal views is that other views are disabled, while the modal is shown. This is a part of the logic of the View that is essentially lookless. That's why I have a property for it in the MainWindowViewModel. It I were to take it further, I should make every other property or command for all other VM's in the Main VM throw exceptions, while in modal mode, but I feel this to be excessive.
The View mechanism of actually denying the user any other actions, does not have to be performed with a popup window and showdialog, it could be that you put the modal view in the existing window, but disable all others, or some other thing. This view-related logic belongs in the view itself. (That a typical designer can't code for this logic, seems a secondary concern. We all need help some times.)
So that's how I have done it. I offer it only as a suggestion, there is probably other ways of thinking about it, and I hope you get more replies too.
I've used EventAggregator from Prism v2 in similar scenarios. Good thing about prims is that, you don't have to use entire framework in your MVVM application. You can extract EventAggregator functionality and use it along with your current setup.
You might have a look at this MVVM article. It describes how a controller can communicate with the ViewModel:
http://waf.codeplex.com/wikipage?title=Model-View-ViewModel%20Pattern&ProjectName=waf

Resources