How to Implement Custom ViewMediator

Sometimes there is arises some necessity in using of non standard views. MugenMvvm provided with some abstraction that allows to end-user connect non-standard views and viewmodels. It's abstraction called "ViewMediator".

As we know, "mediator" is a behavioral pattern that encapsulates how a set of objects interact. In our case, a typical "mediator" encapsulates interaction of view and viewmodel.

So, in order to use non standard views in your project you have to follow the next algorithm:

  1. Create your own view mediator.
  2. Register it in ServiceProvider.

Step One: Creation

In order to create your own view mediator you have to implement IWindowViewMediator. The members of this interface are quite simple:

  • IsOpen - property to check view is opened;
  • View - reference for the view;
  • ViewModel - reference for the viewmodel;
  • ShowAsync - method that allows to show view;
  • CloseAsync - method that allows to close view;
  • UpdateView - method that allows to update view;
public interface IWindowViewMediator
{
  bool IsOpen { get; }

  [CanBeNull]
  object View { get; }

  [NotNull]
  IViewModel ViewModel { get; }

  Task ShowAsync([CanBeNull] IOperationCallback callback, 
                 [CanBeNull] IDataContext context);

  Task<bool> CloseAsync([CanBeNull] object parameter);

  void UpdateView([CanBeNull] object view, bool isOpen, 
                  [CanBeNull] IDataContext context);
}

By the way there is exists base implementation of this interface in MugenMvvm (WindowViewMediatorBase).

This base implementation contains quite many lines of code, but the main abstract methods are:

protected abstract void ShowView([NotNull] TView view, bool isDialog, 
                                 IDataContext context);

protected abstract void ActivateView([NotNull] TView view, 
                                     IDataContext context);

protected abstract void InitializeView([NotNull] TView view, 
                                       IDataContext context);

protected abstract void CleanupView([NotNull] TView view);

protected abstract void CloseView([NotNull] TView view);

Let's take an example. As example we will take an implementation of WindowViewMediatorBase for WPF:

public class WindowViewMediator : WindowViewMediatorBase<IWindowView>
{
	private readonly NavigationWindow _window;

	public WindowViewMediator([NotNull] IViewModel viewModel, [NotNull] IThreadManager threadManager,
		[NotNull] IViewManager viewManager, [NotNull] IWrapperManager wrapperManager, [NotNull] IOperationCallbackManager callbackManager)
		: base(viewModel, threadManager, viewManager, wrapperManager, callbackManager)
	{
	}

	internal WindowViewMediator([NotNull] NavigationWindow window, [NotNull] IViewModel viewModel, [NotNull] IThreadManager threadManager,                              
		 [NotNull] IViewManager viewManager, [NotNull] IWrapperManager wrapperManager, [NotNull] IOperationCallbackManager callbackManager)
		: base(viewModel, threadManager, viewManager, wrapperManager, callbackManager)
	{
		Should.NotBeNull(window, nameof(window));
		_window = window;
	}

	protected override void ShowView(IWindowView view, bool isDialog, IDataContext context)
	{
		if (isDialog)
			view.ShowDialog();
		else
			view.Show();
	}

	protected override void ActivateView(IWindowView view, IDataContext context)
	{
		view.Activate();
	}

	protected override void CloseView(IWindowView view)
	{
		view.Close();
	}

	protected override void InitializeView(IWindowView windowView, IDataContext context)
	{
		windowView.Closing += OnClosing;
	}

	protected override void CleanupView(IWindowView windowView)
	{
		windowView.Closing -= OnClosing;
	}

	public override IViewModel ViewModel
	{
		get
		{
			if (_window == null)
				return base.ViewModel;
			if (ThreadManager.IsUiThread)
				return ToolkitExtensions.GetDataContext(_window.Content) as IViewModel ?? base.ViewModel;
			return base.ViewModel;
		}
	}

	private void OnClosing(object sender, CancelEventArgs cancelEventArgs)
	{
		OnViewClosing(sender, cancelEventArgs);
	}
}

As you can see, WindowViewMediator is quite straightforward.

Step Two: Registration

To register your mediator you can choose one of the next approaches:

  1. Register through ServiceProvider.WindowViewMediatorFactory;
  2. Override CreateWindowViewMediator method in DynamicViewModelWindowPresenter.

Let's see how first approach works. First of all, let's create a module in your project:

public class MyModule : IModule
{
    public int Priority { get; } = 0;

    public bool Load(IModuleContext context)
    {
        return true;
    }

    public void Unload(IModuleContext context)
    {
    }
}

And now just assign ServiceProvider.WindowViewMediatorFactory to our delegate:

public class MyModule : IModule
{
    public int Priority { get; } = 0;

    public bool Load(IModuleContext context)
    {
        ServiceProvider.WindowViewMediatorFactory = (model, type, arg3) =>
        {
            if (type.IsAssignableFrom(typeof(NonStandardView)))
            {
                return new WindowViewMediator(model, /* and other parameters */);
            }
            return null;
        };
        return true;
    }

    public void Unload(IModuleContext context)
    {
    }
}

In our delegate we check that passed type is the type assignable from non standard view type and return appropriate type of mediator. Else return null.