Childwindows in MVVM - c#

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
}

Related

How to open multiple windows using WPF Prism library?

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.

Opening a second Window from MainWindow following MVVM and loose coupling

At first: This App and Question is for learning purpose
I'm on a new application and facing the problem that I want to open a Window when the user clicks on a Button in the MainView. In the past I'd have designed a Command which just creates the new Window and displays it
new RelayCommand((x)=>new SecondWindow().Show());
Now with this new Project I'm trying to fully decouple all classes from each other. To achieve this my App consists of 4 Assemblies (BL, COM, DAL and UI).
As in each WPF Application, the App starts with the MainWindow.xaml. The MainWindow.cs will create it's instance of MainWindowViewModel:
public ViewModel VM {get; private set;}
public class MainWindow(){
VM = new ViewModel();
InitializeComponent();
}
(which already violates loose coupling) (Any tips on how to make it better?)
My last attempt is to create an instance of my second Window inside my main window
<Window.Resources>
<local:SecondWindow x:Key="sw"/>
</Window.Resources>
and pass it as a CommandParameter to my Command
CommandParameter="{StaticResource sw}"/>
new RelayCommand((x)=> ((Window)x).Show());
This solution works but has one big disadvantage - the second window get's created immediately after the app starts - and so does it's ViewModel which starts some heavy processes (DB Connections etc.)
I've heard something abour IoC principle but I really don't know how to use it with an wpf application.
You are thinking along the right lines.... you basically have to create a List of ViewModels as your application starts up, then you can switch between them as the user presses buttons and pass the name of the ViewModel as a CommandParameter to your Command handler....
You might find this link to Rachel Lim's Blog
https://rachel53461.wordpress.com/2011/12/18/navigation-with-mvvm-2/
Also, I'm not going to post any code here coz it simply gets too complicated. So here is a download to just about the simplest example I could come up with
http://www.mediafire.com/download/3bubiq7s6xw7i73/Navigation1.rar
Download and un-RAR it (with win RAR) You will need to step though the code, figure out what its doing and how its doing it then modify it to suit your needs... Or modify your needs to suit the code.....
The example is a modification of Rachel Lim example. It simply contains Views and ViewModels, there are no Models or data. It demonstrates switching between two different Views.
UPDATE 1
With specific reference to the demo code.... Your VMs are added to a static collection of VMs (see AddViewModel function), each View ( the DataTemplate associates View with ViewModel) is selected when you click a button for example, by calling 'SelectViewCommand' which in turn sets Current_ViewModel to the selected ViewModel... the corrisponding ContentControl is then updated to display that currently selected View...
I know is confusing and very difficult to explain
When you press a button to 'change Views' you are actually changing the value of the property that your ContentControl is bound to, so you have to call the correct SelectViewCommand in the SAME instance of the class that your ContentControl is bound too...
In the demo you'll see that in the 'LogOn_View' I call
Command="{Binding DataContext.SelectViewCommand, ElementName=Base_V}"CommandParameter="Main_ViewModel"
Here I am calling the SelectViewCommand in the Base_ViewModel (x:Name="Base_V" in Base_View XAML), That's because I want to change the View that is displayed in the Base_View's 'ContentControl'
In Main_View I call
Command="{Binding SelectViewCommand}" CommandParameter="MainV1_ViewModel"
Here I am calling the SelectViewCommand in the Main_ViewModel, That's because I want to change the View displayed in the MainView's 'ContentControl'....
I typically create a WindowService class for managing window changes/dialogs in MVVM. Having "View" code in the ViewModel (i.e. Window.Show()) goes against MVVM principles. For example:
public class WindowService : IWindowService
{
public void ShowDialog<T>(ViewModelBase viewModel) where T : IApplicationDialog
{
IApplicationDialog dialog = (IApplicationDialog)Activator.CreateInstance(typeof(T));
dialog.Show();
}
}
And then your call from the ViewModel would look something like:
windowService.ShowDialog<SecondWindow>(new SecondWindowViewModel());
If you're using DI, you can pass a reference to the IoC container to the window service and create the window instances from that rather than using Activator.CreateInstance (i prefer the DI approach personally)

MVVM: How to call method on view from view model?

I am quite new to MVVM, so sorry for probably simple question. However, I can not understand which mechanism from MVVVM (I am using MVVMLight if that is of any consequence) to use in order to program the simple following scenario:
I have textbox TB, where user can fill in URL. Than I have a button B and webview WV. If user clicks on button, the app should take the text from TB and display it in the WV.
I knwo that I can create a property in viewmodel and bound it to TB.Text. I understand probably also that I should create command which will be boudn from button B, but what should I do in the command. How I can call WV.navigate(url), when I do not have reference to WV. Should this be solved by something, which I did not grasp correctly like behaviours? What is the best way to do this?
You should use the messenger pattern for this problem:
http://msdn.microsoft.com/en-us/magazine/dn745866.aspx
http://www.codeproject.com/Tips/696340/Thinking-in-MVVMLight-Messenger
http://mytoolkit.codeplex.com/wikipage?title=Messenger
The idea is that the view can register for specific message classes (in this case for example an own NavigateToUriMessage class) and the view model can send an instance of this message class to whoever listens to the message type. In the command implementation you simply send this message, the view receives the message and changes the URI of the web view.
BTW: The idea of this messenger pattern is that you can better write Unit Tests and use the view model for other platforms (where the reaction to the message may differ).
Another way is to create an attached property for the WebView class where you can bind an Uri property to. The attached property calls Navigate when the bound value changes.
Check out this blog:
http://blogs.msdn.com/b/wsdevsol/archive/2013/09/26/binding-html-to-a-webview-with-attached-properties.aspx

C# WPF userControl to send data to mainWindow textBlock on buttonClick

I am trying to create a simple onscreen keypad created using buttons (currently a User-control), on those buttons i have a click event, when i click/touch a button i want the value of that button sent to a Text-block in my Main-window.
I can't/don't understand how to make the User-control (keypad) see the Text-block (in Main-window) to add in the value that i need.
I have seen solutions that use command Bindings and solutions that use the visual tree traversing but all of them are the main window accessing the user control, not the other way around.
All the examples are the other way around because that is how a UserControl is supposed to work.
A UserControl is a packaged piece of re-usable functionality. It should not know anything about the code that is using it.
Instead you should expose routed events in your UserControl for things like a when number was selected, and subscribe to them in your main window.
There are many ways to achieve what you want. If your MainWindow.xaml has a UserControl and you want to react to a change from the control in the MainWindow.xaml.cs file, then you could add a delegate to the UserControl code behind and register a handler for it in the MainWindow.xaml.cs file. Implementing new delegates are generally somewhat simpler than implementing RoutedEvents, which is another way that you could handle this situation.
Using a delegate like this will enable you to effectively pass a signal to the main view from the child UserControl code behind, which you can react to in any way you want to. Rather than explain the whole story again here, please see my answers from the Passing parameters between viewmodels and How to call functions in a main view model from other view models? posts here on Stack Overflow for full details on how to achieve this.

c# MVVM presentation logic

I'm new the the MVVM pattern.
The view has a login button and a progress bar.
I have a view model called LoginViewModel which exposes the command LoginCommand and is hooked up to my view LoginPage.xaml.
When the login command is executed the Login button should be disabled and the progress bar should become visible.
If login fails the the Login button should become enabled and the progress bar should be hidden.
What I am unsure about is where this presentation logic should happen. Should it happen in the view mode or in the code behind of the page?
Currently I have a boolean property on the view model called LoggingIn which is set to true when the login process begins and then false if it fails. This boolean is the hooked to the IsEnabled and Visibility property of the button and progress bar, respectively.
This felts wrong to me so I tried making a couple of events, OnBeginLogin and OnEndLogin and hooking these up in the code behind of the page which code to control the visual state of the controls. This however required much more code than the previous solution.
I also though that I could expose two properties in the view model which are specific to the controls, LoginButtonEnabled and ProgressBarVisible so that I can control the visual state from the view model. But if I add a cancel button let say, then I would need to add another property called CancelButtonEnabled.
I think that the presentation logic should not be handled in the view model so adding an event seems to be the best solution but I'm wondering what is the best practice or standard/common way of doing it?
Also when login is successful, should returning the user to the previous page or another page be handled in the view model or in the code behind of the page? again I feel that this isn't something for the view model but I'm not sure.
Thanks for your help.
I'm not sure why it feels wrong to you, but your first approach seems the most correct to me. The view model is correctly exposing the application's state to the view, and then you're using data binding to control how this translates to presentation.

Categories

Resources