Using a View Placer

0

A while back, I wrote about the navigation solution provided by MVVM Fabric.  I also wrote about using the ModalViewPlacer provided by MVVM Fabric.  In this post, I will talk about how to provide your own implementation of the IViewPlacer interface.

Overview

Since each application is unique, with its own unique UI, MVVM Fabric leaves it up to you, the developer, to provide the mechanism to place a view into that unique UI.  IVewPlacer is that mechanism.

An implementation of the IViewPlacer interface is required by the navigation solution provided by MVVM Fabric.  It will be used by the ViewController to place a view into your application when a request is made to navigate to a view.

A First Look

The IViewPlacer interface provided by MVVM Fabric follows the single responsibility principle.  Its only job is to place a view into your application, which you can see by looking at the interface itself.

public interface IViewPlacer
{
    void PlaceView(ViewResult viewResult);
}

When you implement IViewPlacer, you must provide an implementation for the PlaceView method.  The PlaceView method takes a ViewResult as a parameter.  So, let’s look at the ViewResult class as well.

public class ViewResult
{
    public FrameworkElement View { get; private set; }
    public ViewTargets ViewTarget { get; private set; }

    public ViewResult(FrameworkElement view, ViewTargets viewTarget)
    {
        View = view;
        ViewTarget = viewTarget;
    }
}

As you can see, the ViewResult class has two properties.  The first property is the actual instance of the view, as created by the view factory.  This view will have a fully loaded view model and is all ready to make an entrance into your UI.  The second property is the ViewTargets value for that view.  There may be times where you need to place certain views in different places than the rest of the views.  With the ViewTargets property on ViewResult, you know what view you are placing and can act accordingly.

IViewPlacer Implementation

The sample application provided with the MVVM Fabric source has an implementation of the IViewPlacer interface.  We will be looking at a simplified version of that implementation.

public sealed class ViewPlacer : IViewPlacer
{
    private TabControl MainTabControl { get; set; }

    public ViewPlacer(Window appWindow, TabControl mainTabControl)
    {
        MainTabControl = mainTabControl;
    }

    public void PlaceView(ViewResult viewResult)
    {
        var exists = false;
        var title = GetTitleFromViewModel(viewResult.View.DataContext);
        foreach (TabItem tabItem in MainTabControl.Items)
        {
            if (tabItem.Header.ToString() == title)
            {
                exists = true;
                break;
            }
        }

        if (!exists)
        {
            var newTabItem = new TabItem() { Header = title, Content = viewResult.View };
            MainTabControl.Items.Add(newTabItem);

            newTabItem.Focus();
        }
    }

    // Other functionality omitted for brevity...
}

The sample application has a tabbed user interface, so the view placer needs to take a view and place it into the TabControl used by the main view.  As you can see, it cycles through any existing views so that duplicates are not shown.  Then, if the view is not a duplicate, it is placed into the TabControl and given focus.

This is a simple UI, so a fairly simple view placer is needed.  More complicated UIs will likely need more complicated view placers.  As an example, a previous project I worked on used a docking solution similar to Visual Studio.  In that case, the view placer would need to be aware of what views go into what docking sections.

Conclusion

The IViewPlacer interface is integral to the navigation solution provided by MVVM Fabric.  Since each application’s user interface is different, it is left up to the consumer of the library to implement this interface.  The complexity of the view placer implementation is dependent on the complexity of the user interface, but placing the view is its only responsibility.

Share

Using ModalViewPlacer

0

In yesterday’s post, I talked about how to do testable navigation with MVVM Fabric.  In that post I made mention of ModalViewPlacer as a stock implementation for IViewPlacer provided by MVVM Fabric.  I think ModalViewPlacer is worth explaining so that you can make effective use of it.

Overview

ModalViewPlacer is an implementation for IViewPlacer which displays views as a modal dialog.  It works with two interfaces provided by MVVM Fabric: IModalView and IModalViewModel.

IModalView must be implemented by the view and provides some hooks for ModalViewPlacer to do its thing.  MVVM Fabric provides an implementation of IModalView, called ModalView, to simplify things.  ModalView inherits from Window.  I will show how to use this shortly.

IModalViewModel must be implemented by the view model and provides a hook for view models to request that they be closed.  MVVM Fabric also provides an implementation of IModalViewModel, called ModalViewModelBase.  ModalViewModelBase inherits from ViewModelBase and provides implementations for the hooks.

When a modal view is shown, its view model needs a way to be able to request the view be closed.  IModalViewModel provides an event, RequestClose, which can be raised.  The view subscribes to that event and closes itself when the event is raised.  ModalViewModel provides several methods which can be used to initiate that close request.  The methods also provide the ability for the view model to indicate whether it was accepted (or cancelled) and give back some sort of result object.  ModalView then takes what was provided by the view model and exposes them as properties to be used by ModalViewPlacer.  ModalViewPlacer will use those properties to create a new message, called ModalViewClosedMessage, and publish it to the message bus to signal to the application that the view was closed.

Using ModalViewPlacer

The sample application provided with MVVM Fabric uses ModalViewPlacer in conjunction with its own custom implementation of IViewPlacer to have a combination of tabbed views and modal views.  This example will be more simple and will only leverage ModalViewPlacer.

First off, you need to have your view inherit from ModalView.

public partial class AdvancedSearch : ModalView
{
    public AdvancedSearch()
    {
        InitializeComponent();
    }
}

Next, you need to update your XAML to reflect this.

<nav:ModalView x:Class="MvvmFabric.Movies.Client.Views.AdvancedSearch"
    xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
    xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"

    xmlns:nav="clr-namespace:MvvmFabric.Navigation;assembly=MvvmFabric">

    ...

</nav:ModalView>

Notice that Window is replaced by nav:ModalView, with nav being the namespace I defined for MVVM Fabric.

Next you need to have your view model inherit from ModalViewModelBase.

public sealed class AdvancedSearchViewModel : ModalViewModelBase
{
}

Your view and view model are all set now.  You can have methods on your view model such as Accept and Cancel to react to the OK and Cancel buttons.

public void Accept()
{
    NotifyCloseRequest(true, Keywords);
}

public void Cancel()
{
    NotifyCloseRequest(false);
}

This illustrates two uses of the NotifyCloseRequest method provided by ModalViewModelBase.  The Accept method signals that the view was accepted and returns Keywords as a result.  Cancel signals that the view was not accepted (read cancelled).

To wire up and request a modal view is no different than I described in yesterday’s post. unless you care about the modal view being closed and possibly the results it provided.  If that is the case, then you must subscribe to the ModalViewClosedMessage from the message bus prior to making the request for the view.

public void ShowAdvancedSearch()
{
    // Listen for the modal view being closed.
    MessageBus.Subscribe<ModalViewClosedMessage>(HandleModalViewClosed);

    // Request the search view.
    var message = new ShowViewMessage(MoviesViewTargets.AdvancedSearch);
    MessageBus.Publish<ShowViewMessage>(message);
}

public void HandleModalViewClosed(ModalViewClosedMessage message)
{
    // Do stuff
}

With the ModalViewClosedMessage, you can determine which view just closed and act according to whether the view was accepted and optionally use the result.

Conclusion

Modal dialogs are a type of view that you don’t want to overuse in modern applications, but there usually is a need for at least a few of them.  Using the ModalViewPlacer, you can tap into the navigation functionality provided by MVVM Fabric and still show modal dialogs in a testable way.

Share