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

╄→尐↘猪︶ㄣ 提交于 2019-12-04 07:49:27

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.

1) You can in each view add DataTemplates in UserControl.Resources, i.e.

<UserControl.Resources>
    <DataTemplate DataType="{x:Type viewmodels:Customer1ViewModel}">
       <views:Customer1View/>
    </DataTemplate>
    <DataTemplate DataType="{x:Type viewmodels:Customer2ViewModel}">
       <views:Customer2View/>
    </DataTemplate>
</UserControl.Resources>

Now you defined for each ViewModel appropriate View. You put only data templates for ViewModels that you expect in that View, i.e. children's ViewModels

2) Hm, your MainWindow also has to have a ViewModel, i.e put in MainWindow DataContext an instance of MainWindows's ViewModel. That ViewModel has to contain the property Content (in which you put ViewModel for content). You can do that manually in App.xaml.cs

public partial class App : Application
{

    public App()
    {
        this.Startup += App_Startup;
    }

    public void App_Startup(object sender, StartupEventArgs e)
    {

        this.MainWindow = new MainWindow();

        //create view model and set data context
        MainWindowViewModel vm = new MainWindowViewModel();
        this.MainWindow.DataContext = vm;

        //show window
        this.MainWindow.ShowDialog(vm);
    }
}

3) I'm not sure about this, you probably will not be able to see results in design time.

I'm not sure if I'm fully understanding what exactly you want, if this doesn't help, please replay to this answer with further explanation.

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!