Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How to bundle View, ViewModel and DataTemplate in a WPF application for easy reuse?

Situation:

I'd like to create a flexible application which is ViewModel driven.

The basic flow is like this:

  1. Design the main ViewModel
  2. Create a UserControl as View and a DataTemplate for the main ViewModel to select this View
  3. If there are sub components, the are modelled by sub ViewModels
  4. Create a UserControl as View and a DataTemplate for the sub ViewModel to select this View

If a sub view model needs to be presented, it is done via a DataTemplate.

This approach can also be seen here (option 8).

So the main window xaml looks something like this:

<Window>
    <!-- somehow I need to add the mapping from ViewModel to View -->
    <Grid>
        <!-- the main ViewModel -->
        <ContentPresenter Content="{Binding Path=Content}"/>
    </Grid>
</Window>

The Content property might contain a view model that contains a list of elements named Children and it's associated DataTemplate might look like this: The children are also flexibly rendered by a suitable DataTemplate.

<UserControl>
    <Grid>
        <StackPanel>
            <!-- display the child ViewModels  in a list -->
            <ItemsControl ItemsSource="{Binding Path=Children}" />
         </StackPanel>
    </Grid>
</UserControl>

Question:

  1. How should I organize the ViewModels, Views and their DataTemplates so I don't need to hardwire them in the MainWindow?

  2. How do I then connect this to the main window?

  3. It would be nice if it is stub-able, i.e. I can see the result during design time with a design time dataContext.

Basically I want to bundle the View, ViewModel and DataTemplate and be able to use them in an application that doesn't need to know about the details (e.g. some sub ViewModel implements a certain interface and is injected into the main ViewModel).

like image 619
Onur Avatar asked Nov 02 '22 03:11

Onur


1 Answers

Have you looked into Prism.

The framework allows you to define regions within your UI that views can be registered against. I believe this answers your 2nd question (2).

xmlns:cal="http://www.codeplex.com/prism"

<Window>
   <!-- somehow I need to add the mapping from ViewModel to View -->
   <Grid>
      <!-- the main ViewModel -->
      <ContentPresenter cal:RegionManager.RegionName="MainRegion"/>
   </Grid>
</Window>

For your first question (1) we structure our entities in the following way:

View - we have an abstract base class that looks similar too:

public abstract class ViewBase<T> : UserControl, IView<T> where T: IViewModel
{
    public T ViewModel
    {
        get
        {
            return this.viewModel;
        }
        protected set
        {
            this.viewModel = value;

            this.DataContext = this.viewModel;
        }
    }

    public ViewBase(IUnityContainer container)
    {
        this.ViewModel = container.Resolve<T>();
    }
}

This then allows us to create Views in xaml using the following:

<ui:ViewBase x:Class="MyView"
         xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
         xmlns:ui="NAMESPACE FOR VIEWBASE"
         xmlns:vm="NAMESPACE FOR VIEWMODEL"
         xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
         x:TypeArguments="vm:MYVIEWMODEL">

In the code behind of a View we do the following:

public partial class MyView : ViewBase<IMyViewModel>

This then makes use of the constructor in the base class to resolve the ViewModel and set it to it's DataContext.

This then allows you to design your view (3) as you intended and also removes the need for having a DataTemplate.

Using the UnityContainer we then register the views as follows:

this.container.RegisterType<IMyView, MyView>();
this.container.RegisterType<IMyViewModel, MyViewModel>();

this.regionManager.RegisterViewWithRegion("MainRegion", typeof(IMyView));

Note that "MainRegion" here matches the RegionName specified in the MainWindow xaml. You can expand this further to use a TabControl if you wanted to display multiple views in the same area, or even break your MainWindow down into different regions.

I hope this helps.

like image 179
Bijington Avatar answered Nov 14 '22 07:11

Bijington