如何在MVVM应用程序中动态插入任意视图到xaml?

时间:2016-02-08 09:45:08

标签: c# wpf mvvm

我的应用程序中有多个位置ContentControl放置在xaml中,我事先并不知道Content将会是什么。实现此方案的最佳做法是什么?

现在我正在考虑两种方法:

  1. 绑定ContentControl.Content以查看模型并使用DataTemplate的字典查找合适的视图。我对这种方法的问题在于,即使我要在一个单独的冠状动画中列出所有可能的组合,在某些情况下我只是在编译时根本不知道确切的视图类型(或视图模型,如果有的话)。我想,使用这种方法来托管非WPF内容我也会遇到麻烦。
  2. 创建某种界面:

    interface IContentPlugin : IDisposable
    {
        object View { get; }
    }
    

    直接将ContentControl.Content绑定到IContentPlugin.View。然后我可以有多个这个接口的实现,并在需要时交换它们。但是这个解决方案并没有让我觉得与MVVM应用程序相处得很好,因为它迫使我在我的视图模型中引用IContentPlugin

  3. 您认为最佳选择 以及为何 ?也许有更好的方法?

4 个答案:

答案 0 :(得分:2)

您应该通过DataTemplates使用隐式视图确定。

这是通过为DataTemplates ViewModel范围内ResourceDictionary类型的ContentControl类型提供特定于类型的ResourceDictionary(即没有关键参考的DataTemplates)来实现的。

请注意,如果单个ViewModel可以与多个Views相关联,则需要非常谨慎地考虑View范围。

<强>更新

使用隐式View确定的原因是:

  • 通常,View分辨率查找比编写View解析服务要快。
  • 您不必通过编写自己的WPF解析程序来重复工作,然后需要将其插入WPF运行时。

您应该告诉外部源您支持的内容,在这种情况下,始终保持ResourceDictionary ResourceDictionaries,这样无论内容/资源如何,您都可以将其合并到运行时WinForms - 这意味着,您的外部资源需要为您提供ViewModel控件包装。

作为在使用此模式之前创建了插件框架的人,使用概念性的纯粹MVVM&#34;实现大大简化了事情 - 外部资源为VM提供ResourceDictionary类和WPF资源,让View为您做出cansend决心

答案 1 :(得分:2)

这是一个非常有趣的场景,对于这些情况,我通常会引入ViewResolverServiceViewModelResolverService(或两者)。因此,根据ViewModel(类,类型或名称)为view提供的内容可以匹配它们以在ContentControl中托管它们。或者可以根据ViewModel(类型或字符串名称)为您提供视图的服务。使用这个强大的概念,您可以使用ContentControls和/或DataTemplates,并且您可以完全控制。

我回答了一些解释这些概念的问题:

Register all viewmodel and services in ViewModelLocator

在这里: Get the View & ViewModel from a plugin

更多信息:https://stackoverflow.com/search?q=ViewModelResolver

因此,如果您从鸟瞰图中查看它,您需要将MVVM应用于您的ContentControls与您的视图。 (并且视图也有MVVM自身应用)。

HTH

答案 2 :(得分:0)

使用DataTemplate for ContentControls:

    <DataTemplate DataType="{x:Type vm:DataSourceViewModel}">
           <view:DataSourceView></view:DataSourceView>
      </DataTemplate>
      <DataTemplate DataType="{x:Type vm:SelectTemplateViewModel}">
           <view:SelectTemplateView></view:SelectTemplateView>
      </DataTemplate>
.........
........
    <ContentControl Margin="5"  HorizontalAlignment="Stretch" Content="{Binding CurrentPage}" Name="ImportControls"></ContentControl>

VM:作为contentcontrol内容的对象类型

查看:是您希望查看特定类型的对象是否设置为ContentControl内容的特定视图

答案 3 :(得分:0)

最终,我采用了第二种方法。我能够解决我的主要问题,即:

  

但是这个解决方案并没有让我觉得与MVVM应用程序相处得很好,因为它迫使我在我的视图模型中引用了IContentPlugins。

传递那些&#34;插件&#34;进入viewmodels是一个错误,你不应该这样做。您可以而且应该做的是找到一种方法将视图划分为更小的独立段,并以非MVVM方式设置其内容。所以基本上我最终得到了一个视图,它充当容器,看起来像这样:

<UserControl x:Name=this>
    <Grid>
        <Grid.RowDefinitions>
            <RowDefiniton>
            <RowDefiniton>
            <RowDefiniton>
        </Grid.RowDefinition>
        <ContentControl Grid.Row="0" Content="{Binding PluginA.View, ElementName=this}"/>
        <ContentControl Grid.Row="1" Content="{Binding PluginB.View, ElementName=this}"/>
        <ContentControl Grid.Row="2" Content="{Binding PluginC.View, ElementName=this}"/>
    </Grid>
</UserControl>

其中PluginAPluginBPluginC是代码隐藏中的依赖项属性,由DI容器使用属性注入设置。我对最终结果很满意,它给了我所需的灵活性。

你也可以使用PRISM,大概说的是同样的事情,但是以更一般和灵活的方式。虽然我的应用程序有点过于复杂,所以我决定保持简单。但是如果你想解决类似的问题,你应该试一试。