首页 > 解决方案 > 如何以 MVVM 方式显示从外部程序集动态加载的 UserControl

问题描述

我正在开发一个 WPF MVVM 应用程序。

MainWindow VM 加载包含 UserControl 及其 ViewModel 的目标外部程序集。

我想在我的 MainWindow 视图中显示这个 UserControl。

我想我应该使用 DataTemplates,但我不明白如何让它们与动态加载的类型一起工作。我没有要显示的代码,因为我不知道如何进行,任何建议都值得赞赏。

编辑:下面是用于从程序集中加载 UC 和 VM 的代码

Assembly assembly = Assembly.LoadFile(testProgramPath);
var publicTypes = assembly.GetTypes().Where(t => t.IsPublic).ToArray();
TestProgramUserControl = publicTypes.Single(t => t.BaseType.FullName == "System.Windows.Controls.UserControl");
TestProgramUserControlViewModel = publicTypes.Single(t => t.GetCustomAttribute<TestProgramUserControlViewModelAttribute>() != null);

我不能对 UC 或其 VM 做出任何假设,我想在我的 MainWindow 中显示它包含或执行的任何内容。然后,它有责任通过适当的消息传递与合适的接收者进行沟通。

标签: c#wpfmvvmuser-controls

解决方案


感谢这里的建议,以及在 SO WPF 聊天中,我解决了我的问题如下。

我添加了一个约束:我的外部程序集只能包含一个 UserControl,并且这个用户控件必须将一个 DataTemplate 定义为具有固定名称的资源。我的主虚拟机从外部 UC 获得具有上述固定名称的唯一资源。我的主视图将此 DataTemplate 用作 ContentPresenter 的 ContentTemplate。

外部用户控制的一些简化代码:

<UserControl xmlns:local="clr-namespace:MyNamespace">
    <UserControl.Resources>
        <ResourceDictionary>
            <DataTemplate x:Key="FixedKeyTemplate"
                          DataType="{x:Type local:MyOuterViewModel}">
                <StackPanel>
                    ...
                </StackPanel>
            </DataTemplate>
        </ResourceDictionary>
    </UserControl.Resources>
</UserControl>

主视图模型:

Assembly assembly = Assembly.LoadFile(testProgramPath);
var publicTypes = assembly.GetTypes().Where(t => t.IsPublic).ToArray();

Type userControlType = publicTypes.Single(t => t.BaseType.FullName == "System.Windows.Controls.UserControl");
UserControl userControlView = Activator.CreateInstance(userControlType) as UserControl;
DataTemplate userControlDataTemplate = userControlView.Resources["TestProgramGUIDataTemplate"] as DataTemplate;

Type userControlViewModelType = publicTypes.Single(t => t.GetCustomAttribute<UserControlViewModelCustomAttribute>() != null);
object userControlViewModel = Activator.CreateInstance(userControlViewModelType);

主视图:

<ContentPresenter Content="{Binding UserControlViewModel}"
                  ContentTemplate="{Binding Path=DataContext.UserControlTemplate,
                                            RelativeSource={RelativeSource Mode=FindAncestor,
                                                                           AncestorType={x:Type Window}}}"/>

@Andy 的建议更“专业”,但就我控制整个应用程序而言,我也是唯一的用户,我想我可以对这个更简单的解决方案感到满意。


推荐阅读