When using code behind, the code looks like this:
AnotherWindow x = new AnotherWindow();
x.Show() ;
// or x.ShowDialog()
But how can I achieve this using MVVM? Specifically Prism?
In case you need to build a dialog for asking user login input or progressing dialog, MahApps.Metro can be a useful toolkit as it provides you with some built-in dialog UI/functionalities with MVVM pattern. For more information, check some examples here:
https://mahapps.com/controls/dialogs.html
In Prism, there's the InteractionRequest for short-lived dialogs. If you're looking for a long living dialog, like a second application window or shell, you're stuck with new Window ... Show.
To make your dialog service mvvm-friendly, you should hide it behind an interface and make it as generic as possible. Using view model first here eliminates the need to specify a window type, because you can provide a default window that just contains one large ContentControl, and the view can be mapped as DataTemplate.
Related
I'm new to the PRISM library. Although it updated quickly, the documentation is confusing, incomplete, and mixes many versions. Currently, I'm using PRISM 8.
I would like some help with an example of how to open multiple windows (modal and non-modal) from main window buttons, sending parameters, and receiving messages, because I was only able to found examples of UserControl View injected into a Window View.
The Prism library does not limit you in any way to use windows like you would in WPF without it. You can write your own window service that suits your requirements to manage instantiating and showing windows. How you implement communication between them is up to you. You could communicate between view models using Prism's event aggregator.
The IDialogService is a feature introduced in Prism 7.2.0.1367, which is a built-it implementation of such a service. The documentation for it is up-to-date and there is not much to add, as it covers everything from creating dialogs, passing parameters, showing dialogs, as well as registering custom windows and styling.
The only thing that changes in Prism 8 is that you can now register multiple dialog windows .
// Default dialog window
containerRegistry.RegisterDialogWindow<MyDialogWindow>();
// Another dialog window that can be accessed by name
containerRegistry.RegisterDialogWindow<MyOtherDialogWindow>(nameof(MyOtherDialogWindow));
You can refer to them by name when showing a dialog with the dialog service.
// Shows the dialog view in them default dialog window
dialogService.Show(nameof(MyView), new DialogParameters(), result => { /* ...handle result.*/ });
// Shows the dialog view in the "MyOtherDialogWindow"
dialogService.Show(nameof(MyView), new DialogParameters(), result => { /* ...handle result.*/ }, nameof(MyOtherDialogWindow));
I would like some help with an example of how to open multiple windows (modal and non-modal) from a main window buttons, [...]
You need to access the IDialogService in your view model. Pass it in the constructor and store it in a field. The dependency container will inject it automatically.
private readonly IDialogService _dialogService;
public MyViewModel(IDialogService dialogService)
{
_dialogService = dialogService;
}
Create a ICommand property in your view model that a button in your view binds to.
OpenDialogCommand = new DelegateCommand(ExceuteOpenDialog);
In the execute method, create the dialog paramaters and show the dialog with Show or ShowDialog (modal).
private void ExceuteOpenDialog()
{
dialogService.Show(nameof(MyOtherView), new DialogParameters(), result => { /* ...handle result.*/ });
}
[...] sending parameters and receiving messages, [...]
That depends on your requirements, but you can have a look at the event aggregator. The documentation is still valid and comprehensive, so nothing to add.
[...] I was only able to found examples of UserControl View injected into a Window View.
That is how the dialog service works. You can use any UserControl to be displayed in a dialog. The dialog service automatically places it in a dialog host window. This way you can reuse your view and make it easier to change and maintain. If you would define your view as Window, you would lose the flexibility of applying different dialog windows without changing the view type and its XAML, as well as the ability to use it anywhere else within other views as component.
I'm currently very stuck with this, my designer wants to have our app with WindowStyle.None to remove the borders and default ugly controls, he has then add custom controls, usually to allow dragging in the past we have used a rectangle and monitored the mousedown event to allow for dragmove.
However with Caliburn micro we lose control of the window because windowmanager create this for us, I'm aware you can override the create window method, but this still doesn't give access to adding UI elements to the window itself and binding to those events. Or at least i can work out a way to do this.
Basically what we are trying to achieve is the "mainwindow" with a WindowStyle.None and that ability to drag and move the window. My googling has failed to give a solid answer on this, and im hoping someone here has an idea.
Caliburn.Micro doesn't force you to make the all your views UserControls. The main view or the one your showing as the main window can be a Window control and you can set properties directly on that Window such as "WindowStyle.None". When Caliburn.Micro sees that the view behind your main view model (the view model you are using as the root, then one you are creating first) is actually a Window and not a UserControl then it will honor this and show that window, It Will Not create a new Window. So you can set your properties directly on that Window and everything shall work fine.
The Caliburn.Micro WindowManager provides overrides to its Show methods that allow you to set the settings of the window that is created.
Have a look here for an example.
Alternatively, you can use a Window directly as your view type (in XAML and the generated code behind file), and set the properties declaratively in the XAML.
If you wish to enable all of your dialogs etc to have common UI components, then you could create a derived WindowManager type that delegates the call to the CM WindowManager but wraps the passed in view model with your common view model. Then register this custom window manager in the bootstrapper rather than the default CM window manager.
Is it possible to go to next window or go back to a window using just <window> tag. I searched through internet and didn't find it anywhere. Whenever navigation is needed, I found <Window.Navigation> is in use. But using but this browser like tab on top I want window to navigate when I click m Ok or cancel button on my <Window>.
P.S. I am new to WPF. So I don't know much about it.
If NavigationWindow would work for you except for the navigation tab on top, the simplest thing to do is set ShowsNavigationUI=false. Alternatively (a bigger hammer, but more flexible) you can replace the Template on your window and that will also remove it.
It is possible to navigate by using the Window class, but it depends what you want to achieve. You can assign your newly created Windows to the applications MainWindow.
EDIT: I did some tests: Other than the documentation states you cannot assign a new Window to the MainWindow property like this:
Application.Current.MainWindow = new MyWindow(); // does not work!
The main window is special, when it is closed the application will normally be closed.
However it might be easier to use the class as it has a NavigationService property which makes navigation a easier. You finde some documentation under http://msdn.microsoft.com/en-us/library/ms750478.aspx
In WPF we have Window.ShowDialog() which allows showing a modal dialog box.
In WinForms there is similar functionality but it also has an overload Form.ShowDialog(IWin32Window) which allows an IWin32Window owner to be passed in. That way the new dialog is not modal, and always maintains a z-order directly above its owner.
How would I get this same functionality using WPF?
Use the Owner property on a Window.
To expand on #Jonathan.Peppers's answer:
Say you had a Window you named FooWindow, and in BarWindow.cs you wanted to create and execute an instance. You can create a modal version of FooWindow as simple as this:
new FooWindow(){ Owner = this }.ShowDialog();
That would assume you didn't need a reference to you instance, obviously, but you get the idea?
I'm having a problem understanding something about MVVM. My application relies on dialogs for certain things. The question is, where should these childwindows originate from? According to MVVM, viewmodels should contain only businesslogic and have zero actual knowledge about UI. However, what other place should I call my childwindows from, considering they're UI elements?
Doesn't this create tight coupling between elements?
Since you tagged the question with Prism, I'll suggest the way I've done it in the past using Prism. Have the IEventAggregator injected into your ViewModel, and then when you want to pop open the dialog, publish a "ShowDialogEvent" or something like that. Then, have another Module called "DialogModule" or whatever, which upon initialization subscribes to that event, and shows the dialog. Furthermore, if you want to pass data back to the original ViewModel, have the ViewModel of the dialog publish a "DialogCloseEvent" or something like that with a payload of the data you need. You can then subscribe to that event back in your main ViewModel.
See Handling Dialogs in WPF with MVVM
In the past, I have accomplished this by using Unity to resolve a custom interface that has a Show() method and a completed event. Then in the ViewModel I would call IScreen screen = container.Resolve<IScreen>(Resources.EditorWindowKey); and then just call screen.Show();.
The big advantage of this is that I can then just simply change my Unity configuration to remove the view when I'm testing my VM's.
The primary route I've been using to do this is to create a command inside your View layer. That command object accepts a parameter that is the ViewModel object that you want to display. The command then finds the appropriate ChildWindow, creates it and displays it with the parameter set as the content or however you will set it up. This way you can just bind a button's command property to that command, and its commandparameter to the object you want to show in the popup and your ViewModel objects never have to care how it's being displayed.
Prompting for user input (like saving a dirty file or something) doesn't work in this scheme. But for simple popups where you manipulate some data and then move on, this works very well.
The ViewModel sample application of the WPF Application Framework (WAF) demonstrates how to show a Modal Dialog.
I would suggest to use a controller in this scenario, say DI'ed dialogController backed up with a dialog shell. The source viewmodel(ie from where the request to open a dialog is originating) will make a call to dialogController.ShowDialog(<<ViewNameToHostInRegion>>,<<RegionName>>).
In Order to transfer the data to and from the dialog and sourceview you can use MessageBus. So essentially when you invoke the ShowDialog() you populate the messagebus, and when the close command of target View(The view hosted in Dialog shell) invoked - say in "Select" button -- Let the target view add/update the messagebus. So that source view model can work with updated data.
It has got many advantages :
1) Your source view works with dialog controller as BlackBox. ie it doesnt aware of what exactly the Dialog view is doing.
2) The view is being hosted in Dialog Shell -- so you can reuse the dialog again and again
3) Unit testing of source view is limited to test the actual functionality of the current viewmodel, and not to test the dialog view\view model.
One heads-up which I came across with this one is, while creating the test cases you may need to write testable Dialog controller which do not show the actual dialog while running the testcases in bunch. So you will need to write a TestableDialogController in which ShowDialog does nothing (Apart from deriving from IDialogController and provide blank implementation of ShowDialog().
Following is the psudeo code :
LocalMessageBus.AddMessage(<MessageKey>,<MessageActualContentAsObject>);
dialogController.ShowDialog(<TargetViewName_SayEmployeeList>);
Employee selectedEmployee = LocalMessageBus.GetMessage(<MessageKey>) as Employee;
if (selectedEmployee != null)
{
//doSomework with selected employee
}