如何在WPF应用程序中捆绑View,ViewModel和DataTemplate以便于重用?

时间:2014-03-07 09:50:06

标签: c# wpf data-binding mvvm contentpresenter

情况:

我想创建一个灵活的应用程序,它是ViewModel驱动的。

基本流程是这样的:

  1. 设计主ViewModel
  2. 创建一个UserControl作为View,并为主ViewModel创建一个DataTemplate以选择此View
  3. 如果有子组件,则由子ViewModels建模
  4. 为View ViewModel创建一个UserControl作为View和一个DataTemplate来选择此View
  5. 如果需要显示子视图模型,则通过DataTemplate完成。

    这种方法也可以看作here(选项8)。

    所以主窗口xaml看起来像这样:

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

    Content属性可能包含一个视图模型,其中包含名为Children的元素列表,并且它的关联DataTemplate可能如下所示: 孩子们也可以通过合适的DataTemplate灵活地呈现。

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

    问题:

    1. 我应该如何组织ViewModel,Views及其DataTemplates,以便我不需要在MainWindow中对它们进行硬连线处理?

    2. 如何将其连接到主窗口?

    3. 如果它是可存根的,那就太好了,即我可以在设计时使用设计时dataContext看到结果。

    4. 基本上我想捆绑View,ViewModel和DataTemplate,并且能够在不需要了解细节的应用程序中使用它们(例如,一些子ViewModel实现某个接口并注入到主ViewModel中)

2 个答案:

答案 0 :(得分:2)

你有没有看过Prism

该框架允许您在UI中定义可以注册视图的区域。我相信这回答了你的第二个问题(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>

对于您的第一个问题(1),我们按以下方式构建实体:

查看 - 我们有一个看起来相似的抽象基类:

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>();
    }
}

然后,我们可以使用以下命令在xaml中创建视图:

<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">

在View背后的代码中,我们执行以下操作:

public partial class MyView : ViewBase<IMyViewModel>

然后利用基类中的构造函数来解析ViewModel并将其设置为它的DataContext。

然后,您可以按预期设计视图(3),并且无需使用DataTemplate。

使用UnityContainer,我们按如下方式注册视图:

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

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

请注意&#34; MainRegion&#34;这里匹配MainWindow xaml中指定的RegionName。如果要在同一区域中显示多个视图,或者甚至将MainWindow分解为不同的区域,可以进一步扩展它以使用TabControl。

我希望这会有所帮助。

答案 1 :(得分:0)

1)您可以在每个视图中添加UserControl.Resources中的DataTemplates,即

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

现在您为每个ViewModel定义了相应的View。 您只为该视图中的ViewModel添加了数据模板, 即儿童的ViewModels

2)Hm,你的MainWindow也必须有一个ViewModel,即在MainWindow DataContext中放入一个MainWindows的ViewModel实例。 ViewModel必须包含属性Content(在其中放置ViewModel作为内容)。 您可以在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)我不确定这一点,你可能无法在设计时看到结果。

我不确定我是否完全理解你想要什么,如果这没有帮助, 请重播这个答案并作进一步解释。