How to implement dialog architecture in MVVM - c#

I am developing a WPF 4.0 - MVVM application based on PRISM framework (Unity Container).
I was wondering what is the best way to implement dialogs in the mvvm pattern.
I am planning to use quite a few in my application so I want something reusable.

Since you are using Prism/Unity implement the mediator pattern for your View Models.
Add a DialogService (IDialogService) module to your project.
Modules containing dialogs register them with the IDialogService.
Don't forget to declare DialogServiceModule as a ModuleDependency.
ViewModels now use the IDialogService to show the required dialog.
public interface IDialogService
{
void RegisterDialog (string dialogID, Type type);
bool? ShowDialog (string dialogID);
}
public class DialogService : IDialogService
{
private IUnityContainer m_unityContainer;
private DialogServiceRegistry m_dialogServiceRegistry;
public DialogService(IUnityContainer unityContainer)
{
m_unityContainer = unityContainer;
m_dialogServiceRegistry = new DialogServiceRegistry();
}
public void RegisterDialog(string dialogID, Type type)
{
m_dialogServiceRegistry.RegisterDialog(dialogID, type);
}
public bool? ShowDialog(string dialogID)
{
Type type = m_dialogServiceRegistry[dialogID];
Window window = m_unityContainer.Resolve(type) as Window;
bool? dialogResult = window.ShowDialog();
return dialogResult;
}
}
If you use ViewModel events & handlers in the View, use the WeakEventHandler pattern to eliminate a potential resource leak.
Also, it is possible for multiple Views to be attached to the same ViewModel.
I've worked on projects with one ViewModel -> one View. But also one ViewModel -> multiple Views.
Just something to consider when making your design decisions.

This article about dialogs with MVVM you might find useful: http://www.codeproject.com/Articles/36745/Showing-Dialogs-When-Using-the-MVVM-Pattern

I let the ViewModel raise events when it needs to get user information. It is then up to the View how to supply it. This does mean that the code behind file will get Event handlers though, something real MVVM adepts will shudder at...

Related

When and how to create presenters using windows forms with MVP and dependency injection

This is a question regarding dependency injection (DI) using Lamar, with windows forms (C#) using the model-view-presenter (MVP) pattern and how (and when) to initialize presenter objects.
I have a solution split up into three projects:
Infrastructure
Domain
Presentation
In the presentation project, I have my forms and user controls, which are separated using the MVP-pattern.
In the Program.cs file for the presentation project, I define my container using Lamar and create my main view as:
var container = new Container(x =>
{
x.AddSingleton<IInterfaceFromDomainProject, ClassFromDomainProject>();
x.AddSingleton<IMainView, MainView>();
x.AddSingleton<IMainPresenter, MainPresenter>();
x.AddSingleton<ISubView, SubView>();
x.AddSingleton<ISubPresenter, SubPresenter>();
});
Application.EnableVisualStyles();
Application.SetCompatibleTextRenderingDefault(false);
var mainView = new MainView();
Application.Run(mainView);
This will resolve any dependencies my presenters and views may have.
For the MVP-pattern I am using the observing presenter style, which is explained here. I like this specific style because it completely decouples knowledge of the presenters from the views. An example of this can be found here.
This means that the constructors for my views (forms and user controls) doesn't take any paramaters. This allows me to drag and drop user controls into my forms when designing it.
For example, if my MainView (which is a form) has a tab control in it, I can drag and drop the SubView (which is an user control) into a tab page of the tab control.
All the logic (what data to present etc.) are handled in the presenters. This means that my presenters constructors takes interfaces from the domain project as parameters, as well as the interface of the concrete view.
My main view:
public interface IMainView
{
event EventHandler MyCustomEvent;
void ShowMessage();
}
public partial class MainView : Form, IMainView
{
public event EventHandler MyCustomEvent;
public MainView()
{
InitializeComponent();
}
private void button_Click(object sender, EventArgs e)
{
MyCustomEvent.Invoke(sender, EventArgs.Empty);
}
public void ShowMessage()
{
MessageBox.Show("Hello!");
}
}
My main presenter:
public interface IMainPresenter
{
void ShowMessageHandler(object sender, EventArgs e);
void ShowData();
}
public class MainPresenter: IMainPresenter
{
private readonly IMainView _view;
private readonly IInterfaceFromDomainProject _foo;
public MainPresenter(IMainView view, IInterfaceFromDomainProject foo)
{
_view = view;
_foo = foo;
_view.MyCustomEvent += ShowMessageHandler;
}
public void ShowMessageHandler(object sender, EventArgs e)
{
_view.ShowMessage();
}
public void ShowData()
{
// Do something with _foo. Get data and display it in its view.
}
}
From the previous link:
The presenter doesn't have any methods that the view can call, but the view has events that the presenter can subscribe to.
The presenter knows its view. This is accomplished using constructor injection.
The view has no idea what presenter is controlling it.
Question
Based on this implementation of MVP and DI, how and when do I create my presenters? They are dependent on interfaces from the domain project, which is why they are used in the lamar-container. Am I supposed to call var mainPresenter = new MainPresenter(container.GetRequiredService<IMainView>(), /* get service for all required interfaces*/); for all my presenters in Program.cs?
Am I misunderstanding something regarding DI or the MVP pattern?
Edit
The var mainPresenter = new MainPresenter(container.GetRequiredService<IMainView>() /* get service for all required interfaces*/); doesn't work and i get a NullReferenceException: 'Object reference not set to an instance of an object.' on the MyCustomEvent.Invoke(sender, EventArgs.Empty); (I know I should use ?.).
The only way to do it is to call:
var mainView = new MainView();
var mainPresenter = new MainPresenter(mainView);
I have seen other implementations of MVP, where the presenter is created in the constructor for the concrete view, but how do i pass the necessary interfaces to the presenter?
E.g.:
public partial class MainView : Form, IMainView
{
public MainView()
{
InitializeComponent();
var presenter = new MainPresenter(this, /* How to pass interfaces here? */)
}
}
Your question is very broad and not easy to answer in such a post. You are asking about overall UI architecture, probably because you want to modularize your user interface. This is generally difficult to achieve with Winforms as, by its core design, everything is attached to the main window, and views can only hardly be decoupled from components such as presenters or view models.
If you want to achieve modularity of your UI, I would recommend you to switch technology to Windows Presentation Framework (WPF), which has a much better, modular, architecture and supports MVP, MVVM, event tunneling and much more.
If you want to study how to build modular applications, a good starting point might be the prism library, which highlights most of the concepts. You will also find answers on how to use dependency injection, how to create presenters, etc. Please have a look here: https://prismlibrary.com/docs/wpf/view-composition.html

AvaloniaUI - What is the proper way to inject ViewModels into Views using composition-root based DI system?

I am new to Avalonia/ WPF, Xaml and desktop development in general so please forgive and clarify any related misunderstandings I demonstrate. I will continue to study available documentation but I am having a difficult time finding material which addresses the point I am getting stuck on.
I am trying to implement a composition-root, constructor-injection based dependency-injection system in my Avalonia application, using the recommended MVVM pattern and associated Avalonia project template. I have some familiarity with the Microsoft.Extensions.DependencyInjection package so have been trying to work with this system.
Between tutorials for WPF and Avalonia based on this DI framework as well as other frameworks, I have tried to piece together a working solution. I think I have things figured out conceptually as far as registering Services and ViewModels and setting up constructors for these classes appropriately such that the framework will inject dependencies into these classes on instantiation. However, where I am getting stuck is with how to implement constructor injection for View classes.
I attempted to register both MainWindow and MainWindowViewModel as services:
// App.axaml.cs
public partial class App : Application
{
private IServiceProvider _services;
public override void Initialize()
{
AvaloniaXamlLoader.Load(this);
}
public override void OnFrameworkInitializationCompleted()
{
ConfigureServiceProvider();
if (ApplicationLifetime is IClassicDesktopStyleApplicationLifetime desktop)
{
desktop.MainWindow = _services.GetService<MainWindow>();
}
base.OnFrameworkInitializationCompleted();
}
private void ConfigureServiceProvider()
{
var services = ConfigureServices();
_services = services.BuildServiceProvider();
}
private static IServiceCollection ConfigureServices()
{
var services = new ServiceCollection();
services.AddTransient<MainWindow>();
services.AddTransient<MainWindowViewModel>();
return services;
}
}
The goal is then to be able to inject the MainWindowViewModel class into the MainWindow class via constructor and then assign that argument to the DataContext property of the MainWindow view-class:
// MainWindow.axaml.cs
public partial class MainWindow : Window
{
public MainWindow(MainWindowViewModel viewModel)
{
DataContext = viewModel;
InitializeComponent();
#if DEBUG
this.AttachDevTools();
#endif
}
private void InitializeComponent()
{
AvaloniaXamlLoader.Load(this);
}
}
However, this causes the following error to be raised:
MainWindow.axaml(1, 2): [XAMLIL] Unable to find public constructor for type MyApp.Client:MyApp.Client.Views.MainWindow() Line 1, position 2.
It seems the View cannot be instantiated without the existence of a parameter-less constructor, however, this would seem to prevent constructor injection.
It is very possible I have some fundamental misunderstanding about the intended relationship between ViewModels and Views. I have come across a number of examples where ViewModels are not registered with the service-container, and instead are instantiated directly in the View constructor and assigned to the DataContext property. I would prefer to avoid this approach.
Meanwhile, every tutorial I have come across which demonstrates injecting ViewModels into corresponding View classes, does so using the Service Locator pattern, where the DI service container is passed explicitly (or invoked as a global object) and the ViewModel is resolved explicitly from the container.
Can anybody direct me to any example source code or tutorial which demonstrates how to properly inject ViewModels into Views via constructor? Is this possible to achieve? Is there something I can modify in the MainWindow.axaml file to enable the desired behavior? Thank you for your time and again, I would greatly appreciate clarification of any misunderstandings I may have.
Just for reference, here is the MainWindow markup:
// MainWindow.axaml
<Window xmlns="https://github.com/avaloniaui"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:vm="using:MyApp.Client.ViewModels"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
mc:Ignorable="d" d:DesignWidth="800" d:DesignHeight="450"
x:Class="MyApp.Client.Views.MainWindow"
x:DataType="vm:MainWindowViewModel"
x:CompileBindings="True"
Icon="/Assets/avalonia-logo.ico"
Title="MyApp">
<TextBlock Text="{Binding Greeting}" HorizontalAlignment="Center" VerticalAlignment="Center"/>
</Window>
View models are associated with views via DataContext instead of constructor injection. Note that a singe view can be reusable (especially if you are dealing with virtualized lists).
In general your DI should not know about most of the view part at all, it should be only concerned with ViewModel and lower layers.
Instead of being created via DI views are usually located via view locator by other views that bind particular properties to ContentControl, e. g.
<ContentControl Content="{Binding MySubViewModel} />
(you can find a simple view locator in avalonia.mvvm template, you can tune it for your needs).
When one needs to show a new top-level view from your view model code, they usually implement some kind of a window manager that manages top-level views and is accessible from the view model via DI, e. g.
public class ViewManager : IViewManager
{
private Window CreateWindowForModel(object model)
{
foreach (var template in Application.Current.DataTemplates)
{
if (template.Match(model))
{
var control = template.Build(model);
if (control is Window w)
return w;
return new Window { Content = control };
}
}
throw new KeyNotFoundException("Unable to find view for model: " + model);
}
public void ShowWindow(object model) => CreateWindowForModel(model).Show();
}
Then you add IViewManager implementation to your DI.
Note that this approach is reusable for all XAML frameworks and makes it possible to completely reuse the view model between various platforms (e. g. if you want to implement mobile UI with Xamarin and desktop with Avalonia) with only a few UI-toolkit specific services.

WPF Caliburn.Micro - Best way to navigate in Single Window Application

My history:
I'm developing a WPF application, which will run in full screen on the touch screen. Navigation in my application can be done only by clicking a button on each page ("back" or "logout").
This is not a Universal App, but it looks like.
Assumptions of the project:
Application will run on full screen mode in Windows 7 on the touch screen.
I'm using Caliburn.Micro MVVM framework.
Problem and question:
I've got 1 window and 3 UserControl (and ViewModels) Concept art
Window ShellView
UserControl LoginView
UserControl OrdersView
UserControl OrderDetailView
When application starting, i'm set LoginView as default and load it by using CM Conductor ActivateItem method, but i don't know how to set another View from UserControl like LoginView
I have read: this question but this doesn't cover my case
and this answer but it's to hard to understand for me.
My ideas:
make static method in ShellViewModel like:
ShellViewModel
public static void setOrdersView() {
ActivateItem(new OrdersViewModel());
// Error : An object reference is required for the non-static field, method, or property 'Caliburn.Micro.ConductorBase<object>.ActivateItem(object)
}
ShellViewModel.setOrdersView();
make listener in ShellViewModel and send event from child ViewModel ( but now i don't know how to achieve it)
Question: What is the best way to handle navigation in this case?
Application architecture:
ShellView
<Window>
<ContentControl x:Name="ActiveItem" />
</Window>
ShellViewModel
public class ShellViewModel : Conductor<object>, IShell
{
public ShellViewModel()
{
LoadDefault();
}
public void LoadDefault()
{
ActivateItem(new LoginViewModel());
}
}
LoginView
<UserControl>
<Button x:Name="Login" />
</UserControl>
LoginViewModel
public class LoginViewModel : PropertyChangedBase
{
public void Login() {
if (LoginManager.Login("User", "Password")) {
// How to redirect user to OrdersView?
}
}
}
I have similar applications with one shell window and many activated views inside and some dialog windows.
You should use EventAggregator pattern for these needs and Caliburn already has implementation.
How to Achieve:
Minimum Shell signature
public class ShellViewModel : Conductor<object>,
IHandle<ChangePageMessage>,
IHandle<OpenWindowMessage>
You need two fields inside (second one is for dialogs):
public IEventAggregator EventAggregator { get; private set; }
public IWindowManager WindowManager { get; private set; }
I've set single instances of that objects through IoC. You can also define them as Singletons.
EventAggregator needs subscription on object where IHandles are implemented.
EventAggregator.Subscribe(this); //You should Unsubscribe when message handling is no longer needed
Handlers implementation:
public void Handle(ChangePageMessage message) {
var instance = IoC.GetInstance(message.ViewModelType, null);//Or just create viewModel by type
ActivateItem(instance);
}
public void Handle(OpenWindowMessage message) {
var instance = IoC.GetInstance(message.ViewModelType, null);//Or just create viewModel by type
WindowManager.ShowWindow(instance);
}
Messages for event aggregator can be only marker classes but sometimes it is useful to pass more parameters like ours OpenWindowMessage and ChangePageMessage classes - they are absolutely similar by content, so for example:
public class OpenWindowMessage {
public readonly Type ViewModelType;
public OpenWindowMessage(Type viewModelType) {
ViewModelType = viewModelType;
}
}
All your viewModels can also subscribe to EventAggregator instance and Handle some messages for communication, or even for initial parameters. I have something like MyViewModelInitMessage classes almost for every viewModel, and just use two publish methods together.
EventAggregator.Publish(new ChangePageMessage(typeof(MyViewModel)));
EventAggregator.Publish(new MyViewModelInitMessage("...all needed parameters"));
So when I publish those two - mine ViewModel will be activated, then it subscribes to EventAggregator(don't forget to do that or second message handling never occurs), and will handle its InitMessage right after.
Now with EventAggregator you can send messages between all ViewModels that are currently subscribed to it.
That seems pretty common solution.

Standard Approach of Launching Dialogs/Child Windows from a WPF Application using MVVM

All, I would like to know the recognised best approach/industry standard of launching [child] dialogs/windows from an WPF using the MVVM pattern. I have come across the following articles:
A. CodeProject - Showing Dialogs When Using the MVVM Pattern
This approach seems good but excessive to me. The is some degree of code replication and I am not convinced this is the right way to go.
B. WPF MVVM and Showing Dialogs
This briefly runs through three options with various links which are all fairly/very poor at explaining the methodology or are of-topic.
Can someone please provide an explanation of the industry standard method/approach of launching dialogs from a WPF application using MVVM and preferably some links to further reading material? If you can provide an example yourself I would of course be most appreciative!
Thanks for your time.
First of all, i don't know of any "industry-standard" way for showing dialogs using MVVM because there is no such thing.
Secondly, Welcome to MVVM, you have just touched on of the areas that MVVM don't have a standard for.
To tell you the truth, MVVM has many pain points and this is the reason why there are tons of MVVM frameworks out there, just to mention a few MVVM Light, PRISM, Caliburn.Micro, Cinch, Catel, WAF, Baboon, shell i stop or you want more.
Now to answer your question and after dealing with most of those frameworks, i noticed one commonality, they all use a DI/IoC container and then provide you with an interface, something like IDialogManager and an implementation of their own, and then they ask you to accept this interface in your view model and use it to show dialogs. So to sum this up, i would use dependency injection, have an interface for showing dialogs, and then provide and implementation of that, and register it with the di container and then consume it from my view model or views.
Edit: So you have picked PRISM (which in my opinion) is hardest between them all in showing dialogs. Now that's aside, there is the hard way which is by using Interaction Requests (check the middle of the article), or you can use this Answer as a quicker way.
Recently, I've implemented my own Navigation Service for WPF, which uses Caliburn.Micro's WindowManager (but you could replace it by something else).
Example (how to use):
_navigationService.GetWindow<ClientDetailsViewModel>()
.WithParam(vm => vm.IsEditing, true)
.WithParam(vm => vm.Client, SelectedClient)
.DoIfSuccess(() => RefreshSelectedClient())
.ShowWindowModal();
Implementation:
namespace ClientApplication.Utilities
{
public class NavigationService : INavigationService
{
SimpleContainer _container;
IWindowManager _windowManager;
public NavigationService(SimpleContainer container, IWindowManager windowManager)
{
_container = container;
_windowManager = windowManager;
}
public INavigationService<TViewModel> GetWindow<TViewModel>()
{
return new NavigationService<TViewModel>(_windowManager, (TViewModel)_container.GetInstance(typeof(TViewModel), null));
}
}
public class NavigationService<TVM> : INavigationService<TVM>
{
IWindowManager _windowManager;
TVM _viewModel;
System.Action _action;
public NavigationService(IWindowManager windowManager, TVM viewModel)
{
_windowManager = windowManager;
_viewModel = viewModel;
}
public INavigationService<TVM> WithParam<TProperty>(Expression<Func<TVM, TProperty>> property, TProperty value)
{
var prop = (PropertyInfo)((MemberExpression)property.Body).Member;
prop.SetValue(_viewModel, value, null);
return this;
}
public INavigationService<TVM> DoBeforeShow(Action<TVM> action)
{
action(_viewModel);
return this;
}
public INavigationService<TVM> DoIfSuccess(System.Action action)
{
_action = action;
return this;
}
public void ShowWindow(IDictionary<string, object> settings = null)
{
_windowManager.ShowWindow(_viewModel, null, settings);
}
public bool ShowWindowModal(IDictionary<string, object> settings = null)
{
bool result = _windowManager.ShowDialog(_viewModel, null, settings) ?? false;
if (result && _action != null)
_action();
return result;
}
}
}
Interfaces:
namespace Common
{
public interface INavigationService<TVM>
{
INavigationService<TVM> WithParam<TProperty>(Expression<Func<TVM, TProperty>> property, TProperty value);
INavigationService<TVM> DoIfSuccess(System.Action action);
INavigationService<TVM> DoBeforeShow(Action<TVM> action);
void ShowWindow(IDictionary<string, object> settings = null);
bool ShowWindowModal(IDictionary<string, object> settings = null);
}
public interface INavigationService
{
INavigationService<TViewModel> GetWindow<TViewModel>();
}
}
The most recent release of Prism (download here) contains a so-called "Reference Implementation" of an MVVM application called "Stock Trader". My rationale was that if the Prism team is calling it a "Reference Implementation", that's the most "standard" from their point of view (if anything in MVVM is standard), and the logical choice to press on with.
The source contains an infrastructure library for raising modal dialogs, and it's pretty good. So I adopted that library and have deployed it successfully (I uploaded such an app to Codeplex).
I needed to tweak the code to 1 add the parent's icon to the title bar because the library didn't provide for it; and [2] add some meaningful text to the title bar because the library left it blank and [3] add a delegate to invoke when the dialog closes. It's abstracted to the extent that a VM can raise a dialog by passing two strings (i.e., the Unity Registration Names) to a mediator. Those changes are available on Codeplex.
So among all the other 'standards' the "Reference Implementation" should minimally participate as a viable choice. The more oblique answer to your question is that if your View Models are sufficiently isolated and work entirely through POCO interfaces, then in THEORY, it shouldn't matter because switching to another 'standard' should be a trivial exercise.
i simply use a dialogservice, see here
within your viewmodel you just have to do:
var result = this.uiDialogService.ShowDialog("Dialogwindow title goes here", dialogwindowVM);
... do anything with the dialog result...
Creating a dialog service has worked out well for me, and is also suggested in both your links.
Later I saw the same solution at the dev days in an MVVM presentation by Gill Cleeren. Check the link for working code samples (though written for Metro)
Only thing that nags me a bit about the dialog service is that it is in some way UI technology (rich client) dependent.
A simple request-response web frontend View can be built on top of the same ViewModel and Model code that the WPF XAML binds to. Until the ViewModel starts popping up dialogs through the dialog service. I would not know how to implement the dialog service for a web view. Implementing dialogs there would require pushing a bit more logic to the view.
Purpose of using interface to implement dialogs is to make code testable. In this case, "A" is widely used, but it is still hard to say "Standard". If you do not have test on your ViewModel or you can test your ViewModel avoid touching dialogs, such as using Extract-Override, you can definitely do not follow the instruction.

Showing a message box from the ViewModel is a violation of MVVM - how to avoid?

While watching a video about MVVM on Pluralsight there was a situation where the MVVM pattern got violated but no correct way of doing it was shown:
The view had a button that uses ICommand to trigger a handler in the ViewModel.
The handler correctly relayed the execution to a repository implementation.
The concrete implementation of the repository called a web service method.
However: if the webservice call failed, the ViewModel would bring up a message box that informs the user about the error.
As the ViewModel is an abstraction of the View, it should not directly create UI, but what is the 100% clean way to get that message box presented to the user?
Create a service:
interface IDialogService
{
void ShowMessageBox(string message);
}
Implement it:
class DialogService : IDialogService
{
public void ShowMessageBox(string message)
{
MessageBox.Show(); // ...
}
}
Use dependency injection:
class ViewModel
{
[Import] // This is MEF-specific sample
private readonly IDialogService dialogService;
}
or service location:
class ViewModel
{
private AnyCommandExecute()
{
// This is MEF-specific sample
var dialogService = container.GetExportedValue<IDialogService>();
}
}
to obtain a concrete IDialogService in your view model, then call the obtained implementation from ViewModel.
The same approach is applicable for any other similar cases: show open/save dialog, show your custom view model in dialog.
There are several ways to do this that adhere to the MVVM pattern, such as Interaction Service and Interaction Request.
Interaction Service
... a service that can be used by the view model to initiate interaction
with the user, thereby preserving its independence on the view's
implementation
Interaction Request
... uses events raised by the view model to express the intent to
interact with the user, along with components in the view that are
bound to these events and that manage the visual aspects of the
interaction.
Source
Both quotes above are from this source (which also contains more detailed information about the patterns): http://msdn.microsoft.com/en-us/library/gg405494(v=pandp.40).aspx#sec10

Categories