如何解决如何显示以MVVM方式从外部程序集动态加载的UserControl
我正在开发WPF MVVM应用程序。
MainWindow VM加载包含UserControl及其ViewModel的目标外部程序集。
我想在MainWindow视图中显示此UserControl。
我认为我应该使用DataTemplates,但是我不明白如何使它们与动态加载的Types一起使用。我没有要显示的代码,因为我不知道如何进行,任何建议都值得赞赏。
编辑:下面是用于从程序集中加载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中。然后将有责任通过适当的消息传递与合适的收件人进行通信。
解决方法
我的建议有点长,只能发表评论。
由于您“不知道”如何执行此操作,因此这里有一些建议可为您指明正确的方向。
Managed Extensibility Framework是为动态发现而设计的,用于以您描述的方式扩展应用程序。他们为你做到了。
https://docs.microsoft.com/en-us/dotnet/framework/mef/
除了将视图和视图模型放入该程序集中之外,我还建议在其中也放置一个数据模板化resourcedictionary。您可以使用它来将视图类型与viewmodel类型相关联。 您可以使用mef或仅使用命名约定来定义此资源字典是什么。
要使mef可发现资源字典,您需要在类后面添加代码。然后,您可以将正确的属性应用于该属性,例如:
[Export(typeof(ResourceDictionary))]
public partial class ExternalDataTemplateResourceDictionary : ResourceDictionary
{
public ExternalDataTemplateResourceDictionary ()
{
InitializeComponent();
}
}
要将该类连接到您的资源字典,请使用与Windows或用户控件中可能见过的类似机制。您在其开始标记中使用x:Class:
<ResourceDictionary
....
x:Class="YourProject.ExternalDataTemplateResourceDictionary "
当您发现一个dll时,您将加载它的内容并合并它的模板资源字典。 然后,父视图不需要明确知道您的specialFoo用户控件与该dll中的superFooViewModel关联。合并的数据模板使用“标准”视图模型的第一个数据类型关联来完成此操作。
,感谢这里的建议,以及在SO WPF聊天中,我如下解决了我的问题。
我添加了一个约束:我的外部程序集只能包含一个UserControl,并且此用户控件必须将DataTemplate定义为具有固定名称的资源。 我的主VM从外部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的建议更“专业”,但是据我控制整个应用程序,并且我还是唯一的用户,我认为我对这种简单的解决方案感到满意。
版权声明:本文内容由互联网用户自发贡献,该文观点与技术仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请发送邮件至 dio@foxmail.com 举报,一经查实,本站将立刻删除。