简体   繁体   English

如何使用应用程序ResourceDictionary进行RuntimeType映射

[英]How to use the Application ResourceDictionary to do RuntimeType mapping

I'm trying to add a mapping from one (ViewModel) RuntimeType to another (View) RuntimeType in the Application's ResourceDictionary. 我正在尝试在应用程序的ResourceDictionary中将一个(ViewModel)RuntimeType映射到另一个(View)RuntimeType。 This is so that my Controller class can look-up a ViewModel object class and bind it to a new instance of the appropriate View class. 这样,我的Controller类就可以查找ViewModel对象类,并将其绑定到适当的View类的新实例。 The application is implemented as a collection of plug-ins, which mean that the mapping is not known at compile time. 该应用程序被实现为插件的集合,这意味着在编译时不知道映射。

In my sandbox application (which I use for prototyping) the mapping is added to the main Window's resource dictionary as follows: 在我的沙箱应用程序(用于原型设计)中,将映射添加到主Window的资源字典中,如下所示:

<Window.Resources>
    <!-- This template associates the ConfirmDialog type
         with the ConfirmDialogViewModel type. -->
    <x:Type TypeName="v:ConfirmDialog" x:Key="{x:Type vm:ConfirmDialogViewModel}" />
</Window.Resources>

This compiles and runs perfectly, with the DependencyProperty that uses the mapping to display the ConfirmDialog Window finding the correct class and instantiating it when the attached ViewModel changes. 这可以编译并完美运行,并且DependencyProperty使用映射来显示ConfirmDialog窗口,以查找正确的类并在附加的ViewModel更改时实例化该类。

However, when I attempt to put the same mapping into my Application's Resource dictionary an exception is thrown: 但是,当我尝试将相同的映射放入应用程序的Resource字典中时,会引发异常:

A first chance exception of type 'System.Xaml.XamlObjectWriterException' occurred in System.Xaml.dll System.Xaml.dll中发生类型为'System.Xaml.XamlObjectWriterException'的第一次机会异常

System.Windows.Markup.XamlParseException occurred 发生System.Windows.Markup.XamlParseException
HResult=-2146233087 Message='Missing key value on 'RuntimeType' object.' HResult = -2146233087消息='“ RuntimeType”对象上缺少键值。 Line number '20' and line position '14'. 行号“ 20”和行位置“ 14”。
Source=PresentationFramework LineNumber=20 LinePosition=14 来源= PresentationFramework LineNumber = 20 LinePosition = 14
StackTrace: at System.Windows.Markup.WpfXamlLoader.Load(XamlReader xamlReader, IXamlObjectWriterFactory writerFactory, Boolean skipJournaledProperties, Object rootObject, XamlObjectWriterSettings settings, Uri baseUri) StackTrace:位于System.Windows.Markup.WpfXamlLoader.Load(XamlReader xamlReader,IXamlObjectWriterFactory writerFactory,布尔skipJournaledProperties,对象rootObject,XamlObjectWriterSettings设置,Uri baseUri)
InnerException: System.Xaml.XamlObjectWriterException HResult=-2146233088 Message='Missing key value on 'RuntimeType' object.' InnerException:System.Xaml.XamlObjectWriterException HResult = -2146233088 Message ='缺少“ RuntimeType”对象上的键值。 Line number '20' and line position '14'. 行号“ 20”和行位置“ 14”。 Source=System.Xaml 来源= System.Xaml

The resource is included as follows in the App.xaml: 该资源包含在App.xaml中,如下所示:

<Application.Resources>
    <ResourceDictionary>
        <ResourceDictionary.MergedDictionaries>
            <!-- This resource file contains the DataTemplates. -->
            <ResourceDictionary Source="Resources/DataTemplates.Resources.xaml" />
            <!-- This resource file contains the Styles. -->
            <ResourceDictionary Source="Resources/Styles.Resources.xaml" />

            <!-- This section is used for mapping Views to ViewModels. -->
            <ResourceDictionary>
                <!-- This template associates the ConfirmDialog type 
                     with the ConfirmDialogViewModel type. -->
                <x:Type TypeName="v:ConfirmDialog" x:Key="{x:Type vm:ConfirmDialogViewModel}" />
            </ResourceDictionary>
        </ResourceDictionary.MergedDictionaries>
    </ResourceDictionary>
</Application.Resources>

Any suggestions regarding why the Window and Application ResourceDictionary objects behave differently, how I can find more information from the exception, or things I might try to resolve it would be appreciated. 关于Window和Application ResourceDictionary对象为何行为不同,如何从异常中查找更多信息或我可能尝试解决的任何建议。

My solution is to add a new ResourceDictionary XAML file to the project and include that as a MergedDictionary. 我的解决方案是向项目添加一个新的ResourceDictionary XAML文件,并将其作为MergedDictionary包括在内。

Mappings.Resources.xaml (namespaces changed): Mappings.Resources.xaml(名称空间已更改):

<ResourceDictionary
    xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
    xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
    xmlns:v="clr-namespace:MyWpfViewNamespace"
    xmlns:vm="clr-namespace:MyViewModelNamespace;assembly=MyViewModelAssembly"
    >

    <!-- This template associates the ConfirmDialog type 
        with the ConfirmDialogViewModel type. -->
    <x:Type TypeName="v:ConfirmDialog" x:Key="{x:Type vm:ConfirmDialogViewModel}" />

</ResourceDictionary>

App.xaml (extract): App.xaml(摘录):

<Application.Resources>
    <ResourceDictionary>
        <ResourceDictionary.MergedDictionaries>
            <!-- This resource file contains the DataTemplates. -->
            <ResourceDictionary Source="Resources/DataTemplates.Resources.xaml" />
            <!-- This resource file contains the Styles. -->
            <ResourceDictionary Source="Resources/Styles.Resources.xaml" />
            <!-- This resource file contains the RuntimeType mappings. -->
            <ResourceDictionary Source="Resources/Mappings.Resources.xaml" />
        </ResourceDictionary.MergedDictionaries>
    </ResourceDictionary>
</Application.Resources>

And (in case anyone is interested) the instantiation is done in the PropertyChanged event handler of a DependencyProperty that binds an object that exposes the interface to a top-level ViewModel base class. 并且(如果有人感兴趣),实例化是在DependencyProperty的PropertyChanged事件处理程序中完成的,该事件处理程序绑定了一个将接口暴露给顶级ViewModel基类的对象。

In the Mappings.Resources.xaml mapped types: 在Mappings.Resources.xaml映射类型中:

  • ConfirmDialogViewModel implements the ITopLevelViewModel interface. ConfirmDialogViewModel实现ITopLevelViewModel接口。
  • ConfirmDialog extends the BaseWpfWindow View base class. ConfirmDialog扩展BaseWpfWindow视图基类。

ShowModalViewProperty.cs (extract): ShowModalViewProperty.cs(摘录):

public static void ShowModalViewPropertyChanged(
    DependencyObject d,
    DependencyPropertyChangedEventArgs e)
{
    var window = d as Window;
    ITopLevelViewModel newValue = e.NewValue as ITopLevelViewModel;
    if ((null != window) && (null != newValue))
    {
        // Search the local and Application resources for a mapping
        // between the ViewModel type of the new property and a
        // View type to use to display it.
        Type typeOfViewModel = newValue.GetType();
        Type typeOfView = (Type)window.TryFindResource(typeOfViewModel);
        if (null == typeOfView)
        {
            typeOfView = (Type)Application.Current.TryFindResource(typeOfViewModel);
        }

        // If a concrete type of the correct class is available...
        if ((null != typeOfView)&&
            (!typeOfView.IsAbstract)&&
            (typeOfView.IsSubclassOf(typeof(BaseWpfWindow))))
        {
            // Create a new window and show it as a (modal) dialog.
            BaseWpfWindow dialogWindow = (BaseWpfWindow)
                Activator.CreateInstance(typeOfView);
            if (null != dialogWindow)
            {
                dialogWindow.Owner = window;
                dialogWindow.DataContext = newValue;

                // ModalResult is a Property of ITopLevelViewModel, used to return
                // the Window.DialogResult back to the ViewModel object.
                newValue.ModalResult = dialogWindow.ShowDialog();
            }
        }
    }
}

I still don't know why the seperate MergedDictionary method works. 我仍然不知道为什么单独的MergedDictionary方法有效。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM