【问题标题】:Difficulties with ThemeInfo in WPF?WPF 中 ThemeInfo 的难点?
【发布时间】:2013-03-20 11:40:21
【问题描述】:

我的AssemblyInfo.cs 文件中的ThemeInfo 属性卡住了。我正在尝试编写自定义控件。自定义控件位于名为“MyCustomControls.dll”的 dll 中。此外,控件本身派生自另一个自定义控件,该控件位于另一个名为“MyAnotherCustomControls.dll”的 dll 中。

我在 MSDN 上读到,必须使用 2 个参数声明 ThemeInfo,这些参数负责控制主题特定位置和通用特定位置,尽管我不知道什么是“主题”和什么是“通用”。我怎样才能更好地理解这两个?

如果有人可以从头开始向我解释什么是“通用”以及什么是“主题”。此外,何时使用“通用”或“主题”。 WPF系统如何使用这两个?我真的需要用简单的英语进行解释,所以如果您要向我发布 MSDN 链接,请节省您的时间。我阅读了有关 ThemeInfo 的 msdn 文档,但我不明白。

也有人可以告诉我如何使用ThemeInfo 告诉我的“MyCustomControls.dll”使用“MyAnotherCustomControls.dll”中定义的字典资源吗?这是否可以仅通过 ThemeInfo 完成,或者我是否必须处理“MyCustomControls.dll”中的 MergedDirectories?我希望 WPF 系统负责定位资源,以便我可以使用“MyAnotherCustomControls.dll”中的样式键,而无需在“MyCustomControls.dll”中添加合并目录。

【问题讨论】:

    标签: wpf xaml wpf-controls


    【解决方案1】:

    我在 MSDN 上读到,必须使用 2 个参数声明 ThemeInfo,这些参数负责控制主题特定位置和通用特定位置,尽管我不知道什么是“主题”和什么是“通用”。我怎样才能更好地理解这两个?

    基本上,WPF 框架将查找名称与操作系统主题名称匹配的 xaml 资源。因此,如果您在 XP 上运行蓝色主题为“luna.normal.xaml”。如果找不到具有该确切名称的名称,它将查找“generic.xaml”。实际上,我认为如果他们没有找到与操作系统特定的匹配的“classic.xaml”,他们首先会寻找“classic.xaml”,然后他们会寻找generic.xaml。您可以将 generic.xaml 视为默认资源。

    ThemeInfo 属性只是告诉 WPF在哪里定义了这些资源。有 3 个选项:

    • 无 - 您没有提供“主题”或“通用”资源。这确实是一种优化,因此 WPF 框架不会费心寻找它们。您通常不会将其用于 GenericDictionaryLocation,但您可以将其用于 ThemeDictionaryLocation,以防您不打算定义操作系统主题特定的资源(即您没有不希望您的控件在不同的操作系统主题下看起来不同)。通常,控件供应商会为每个可能的操作系统主题定义一个资源字典,以便控件看起来与该操作系统中运行的其他控件和窗口一致。
    • ExternalAssembly - 这意味着您正在为资源定义一个单独的程序集。因此,如果您查看 WPF 框架,您会看到他们将其用于 ThemeDictionaryLocation 的 PresentationFramework 程序集。然后他们为他们想要支持的每个操作系统主题定义了一个单独的程序集(例如 PresentationFramework.Aero.dll、PresentationFramework.Luna.dll 等)。它将搜索的程序集名称是定义程序集的名称加上主题的名称。
    • SourceAssembly - 这意味着资源是在程序集本身内定义的。因此,在该程序集中,您将拥有一个包含资源字典的“主题”文件夹。

    This article on MSDN 关于控件创作的信息对于提供这方面的信息确实还不错。

    也有人可以告诉我如何使用 ThemeInfo 告诉我的“MyCustomControls.dll”使用“MyAnotherCustomControls.dll”中定义的字典资源吗?这是否可以仅通过 ThemeInfo 完成,或者我是否必须处理“MyCustomControls.dll”中的 MergedDirectories?我希望 WPF 系统负责定位资源,以便我可以使用“MyAnotherCustomControls.dll”中的样式键,而无需在“MyCustomControls.dll”中添加合并目录。

    您不能使用 ThemeInfo 告诉 WPF 它应该在某个任意程序集中查找您的资源。话虽如此,如果您不设置或覆盖 DefaultStyleKey,就像在定义自定义控件时通常所做的那样,那么它应该继续使用已设置 DefaultStyleKey 的基类的资源/被默认资源覆盖。

    但应注意 local 样式解析(即,当您的控件的 Style 属性未设置并且 WPF 从元素所在的位置查找并向上走视觉/逻辑树查找可能隐式影响该元素的 Style)将总是查找其 Key 与类的确切类型匹配的 Style。因此,如果有一个 Style,其 TargetType(因此放置在 ResourceDictionary 中时的默认键)是在窗口的资源中定义的 TextBox,它将影响该窗口中的所有 TextBox 实例(除非它们在视觉中具有更接近的样式树 - 即在其自身之间的某个元素的资源中定义某个祖先 - 或其 Style 属性已设置)。但是,如果您有一个派生自 TextBox 的类(例如 MyTextBox 类:TextBox),那么它将不会选择/使用该样式。相反,它将寻找 TargetType/Key 为 typeof(MyTextBox) 的 Style。解决这个问题的一种方法是将 Style 属性设置为 DynamicResource 为基本类型。例如

    public MyTextBox()
    {
      this.SetResourceReference(StyleProperty, typeof(TextBox));
    }
    

    基本上,这会在控件的 Style 属性上设置一个本地值,该属性正在为一个 Style 进行动态资源查找,该 Style 的 Key(以及未设置 x:Key 的 Styles 的 TargetType)是指定类型(在本例中为 TextBox )。

    正如您所指出的,替代方法是在您的程序集中本地为基类程序集定义的每个主题定义 xaml 文件,然后将 ResourceDictionary 添加到其 MergedDictionaries 中,使用 pack uri notation 来引用基类中的资源部件。如果您要设置 DefaultStyleKey,那么您可能需要在每个 ResourceDictionaries 中定义 TargetType 是您的类类型的 Style,然后将 BasedOn 设置为 StaticResource,其中资源键是基类的类型。听起来您不需要这样做。

    【讨论】:

    • 'ExternalAssembly - 这意味着您正在为资源定义一个单独的程序集。'vs.'您不能使用 ThemeInfo 告诉 WPF 它应该在某个任意程序集中查找您的资源。我采用“MyCustomControls.dll”并在其中放置一个 Theme/generic.xaml。我告诉我的“MyCustomControl.dll”的 ThemeInfo 属性,主题是在外部程序集中定义的。然后我拿我的“MyAnotherCustomControl.dll”并按照命名约定命名它,以便“MyCustomControl.dll”可以找到它。最后我在“MyCustomControls.dll”中使用“MyAnotherControls.dll”中的资源键会起作用吗?
    • 我不知道。我从来没有尝试过,但它应该很容易让你尝试。我可以说,如果 MyAnotherCustomControl 是强名称签名的,那么除非您的主程序集使用相同的公钥/私钥文件签名,否则它肯定不会工作。如果您想查看 WPF 是如何进行解析的,那么您可以使用 Reflector/ILSpy 并查看 SystemResources.ResourceDictionaries 类 - 特别是 LoadExternalAssembly 方法。
    • 您是否在派生类上设置了 DefaultStyleKey(或覆盖了元数据)?你试过删除它吗?问题的实际表现是什么?正如我在回复中提到的,无论您做什么,本地隐式样式将始终是您的默认控件类型,而我知道的唯一方法就是我提到的 hack。
    猜你喜欢
    • 2010-11-12
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2012-02-21
    • 2020-06-24
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多