【问题标题】:How to plug in an arbitrary view dynamically into xaml in MVVM application?如何在 MVVM 应用程序中将任意视图动态插入 xaml?
【发布时间】:2016-05-17 22:15:18
【问题描述】:

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

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

  1. ContentControl.Content 绑定到视图模型并使用DataTemplates 的字典来查找合适的视图。我对这种方法的问题是,即使我要在字典中列出所有可能的组合,在某些情况下,我在编译时根本不知道视图的确切类型(或视图模型,如果有的话)。我认为,使用这种方法托管非 WPF 内容也会遇到麻烦。
  2. 创建某种接口:

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

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

您认为最佳选择是什么以及为什么?也许有更好的方法?

【问题讨论】:

标签: c# wpf mvvm


【解决方案1】:

这是一个非常有趣的场景,对于这些情况,我通常会引入 ViewResolverServiceViewModelResolverService(或两者)。因此,可以基于view(类、类型或名称)为您提供ViewModel 的东西可以匹配它们以将它们托管在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】:

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

    这是通过在 ResourceDictionary 的本地 ContentControl 范围内为您的 ViewModel 类型设置特定类型的 DataTemplates(即没有键引用的 DataTemplates)来实现的。

    请注意,在单个 ViewModel 可以关联多个 Views 的情况下,您需要非常仔细地确定 ResourceDictionary 的范围。

    更新

    使用隐式View判断的原因是:

    • 一般来说,View 解析查找比编写视图解析服务要快。
    • 编写自己的View 解析器不会重复工作,然后需要将其插入WPF 运行时。

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

    作为在使用此模式之前创建了插件框架的人,使用概念上的“纯 MVVM”实现可以大大简化事情 - 外部资源为 VM 和您提供 ViewModel 类和 ResourceDictionary 资源让WPF为你做View的重担。

    【讨论】:

    • 引用我自己的话:“我对这种方法的问题是,即使我要在字典中列出所有可能的组合,在某些情况下,我根本不知道视图的确切类型(或视图模型,如果有任何)在编译时。”
    • 我知道,但它有什么帮助?例如,一些外部资源给了我Winforms 对主机的控制权。如何将其放入DataTemplate?我为什么要这样做,这种方法的优势是什么?就是这个问题。
    • 感谢您的澄清。可悲的是,我无法控制那些外部观点。我得到我得到的。因此,如果我要遵循这种方法,我可能必须为我必须使用的每个非 MVVM(或非 WPF)控件创建“假”MVVM 包装器。我需要考虑一下。
    • @NikitaBrizhak 您可能无法同时定义所有可能的视图(组合)。但我认为您可以设计从基本类型或根据合同派生的视图。你必须概括你的观点来为模板创建某种定义。
    【解决方案3】:

    对 ContentControls 使用 DataTemplate:

        <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:is是您的内容控件内容的对象类型

    视图:如果将特定类型的对象设置为 ContentControl 的内容,您想要查看的特定视图

    【讨论】:

    • 这是他已经考虑过的第一个选项。 “您认为最好的选择是什么以及为什么?”。阅读粗体部分;)
    • 他已经给出了一个很好的理由不做第二种方法。 ViewModel 层中的直接视图引用。
    • 嘿@Tseng 你有答案吗stackoverflow.com/questions/35311116/… :)
    【解决方案4】:

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

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

    将这些“插件”传递给视图模型是一个错误,您不应该这样做。您可以并且应该做的是将您的视图划分为较小的独立段的方法,并以非 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,大致上它做同样的事情,但更通用和灵活的方式。虽然它对我的应用程序来说有点太复杂了,所以我决定让它保持简单。但是,如果您正在尝试解决类似的问题,您应该尝试一下。

    【讨论】:

      猜你喜欢
      • 2011-06-03
      • 1970-01-01
      • 1970-01-01
      • 2021-06-26
      • 2016-12-26
      • 1970-01-01
      • 1970-01-01
      • 2012-04-18
      • 1970-01-01
      相关资源
      最近更新 更多