如何在WPF应用程序(包括模板)中定义公用资源?

O. *_*per 5 wpf resources controltemplate app.xaml staticresource

我正在寻找一种定义WPF资源(目前用作静态资源)的方法,该资源可从我的应用程序中的任何位置访问。

我的资源在此资源字典中定义:

<ResourceDictionary xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
    xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml">
    <Style x:Key="flatButtonStyle" BasedOn="{StaticResource {x:Static ToolBar.ButtonStyleKey}}" TargetType="{x:Type Button}">
        <Setter Property="BorderThickness" Value="4"/>
    </Style>
</ResourceDictionary>
Run Code Online (Sandbox Code Playgroud)

选择此问题的答案表明,我必须将该资源字典合并到我的App.xaml文件中(名为的项目AppWideResources):

<Application x:Class="AppWideResources.App"
    xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
    xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
    StartupUri="Window1.xaml">
    <Application.Resources>
        <ResourceDictionary>
            <ResourceDictionary.MergedDictionaries>
                <ResourceDictionary Source="/AppWideResources;component/CommonResources.xaml"/>
            </ResourceDictionary.MergedDictionaries>
        </ResourceDictionary>
    </Application.Resources>
</Application>
Run Code Online (Sandbox Code Playgroud)

起初,这似乎有效;该窗口中的按钮以适当的扁平样式设计,带有超厚边框:

<Window x:Class="AppWideResources.Window1"
    xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
    xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
    Title="AppWideResources" Height="300" Width="300"
    >
    <StackPanel>
        <Button Style="{StaticResource flatButtonStyle}" Content="Test" HorizontalAlignment="Stretch"/>
    </StackPanel>
</Window>
Run Code Online (Sandbox Code Playgroud)

但是,一旦在控件模板中使用共享资源,此操作就会停止:

我(出于此问题的目的,对此进行了极其简化)控制:

using System;
using System.Windows;
using System.Windows.Controls;

namespace AppWideResources
{
    public class MyControl : Control
    {
        static MyControl()
        {
            DefaultStyleKeyProperty.OverrideMetadata(typeof(MyControl), new FrameworkPropertyMetadata(typeof(MyControl)));
        }
    }
}
Run Code Online (Sandbox Code Playgroud)

...以及相应的Themes\Generic.xaml文件:

<ResourceDictionary xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
    xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
    xmlns:local="clr-namespace:AppWideResources">

    <Style TargetType="local:MyControl">
        <Setter Property="Template">
            <Setter.Value>
                <ControlTemplate TargetType="local:MyControl">
                    <StackPanel Orientation="Horizontal">
                        <Button Style="{StaticResource flatButtonStyle}" Content="1"/>
                        <Button Style="{StaticResource flatButtonStyle}" Content="2"/>
                    </StackPanel>
                </ControlTemplate>
            </Setter.Value>
        </Setter>
    </Style>

</ResourceDictionary>
Run Code Online (Sandbox Code Playgroud)

然后将此控件插入到我的窗口中:

<Window x:Class="AppWideResources.Window1"
    xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
    xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
    xmlns:local="clr-namespace:AppWideResources"
    Title="AppWideResources" Height="300" Width="300"
    >
    <StackPanel>
        <Button Style="{StaticResource flatButtonStyle}" Content="Test" HorizontalAlignment="Stretch"/>
        <local:MyControl/>
    </StackPanel>
</Window>
Run Code Online (Sandbox Code Playgroud)

如果运行此命令,XamlParseException则会引发a,表示flatButtonStyle找不到静态资源。

我发现的唯一解决方法是将公共资源字典显式合并到控制模板的本地资源字典中:

<ControlTemplate TargetType="local:MyControl">
    <ControlTemplate.Resources>
        <ResourceDictionary>
            <ResourceDictionary.MergedDictionaries>
                <ResourceDictionary Source="/AppWideResources;component/CommonResources.xaml"/>
            </ResourceDictionary.MergedDictionaries>
        </ResourceDictionary>
    </ControlTemplate.Resources>
    <StackPanel Orientation="Horizontal">
        <Button Style="{StaticResource flatButtonStyle}" Content="1"/>
        <Button Style="{StaticResource flatButtonStyle}" Content="2"/>
    </StackPanel>
</ControlTemplate>
Run Code Online (Sandbox Code Playgroud)

但是,这不仅冗长且容易出错,而且具有几行冗余代码(我的实际项目包含很多模板化控件,而不仅仅是一个),我还担心通过多次加载静态资源来浪费系统资源(包括一次CommonResources.xaml。(该关注点是否合理?还是WPF每次加载特定资源字典时都使用相同的实例?)

因此,问题是:使WPF资源(包括控件模板)遍及整个应用程序的正确方法什么?

Roh*_*ats 3

在应用程序实际运行之前加载 XAML 期间,StaticResource将被解析并分配给该属性。所以,static resource has to be available at the time of loading

它适用于普通按钮,因为资源查找一直持续到应用程序资源并从那里解析它。但MyControl template still not loaded into app visual tree so it can't traverse upto application resources

有两种方法可以实现这一目标:

  1. 将您已经实现的 ResourceDictionary 合并到 Generic.xaml 中。

  2. 另一种方法是使用DynamicResource,它在加载期间将 Expression 对象分配给属性,但直到运行时要求 Expression 对象提供值时才实际查找资源。这defers looking up the resource until it is needed at runtime.

在这里阅读 - StaticResource vs DynamicResource.

无需在 Generic.xaml 中合并字典即可工作

<Button Style="{DynamicResource flatButtonStyle}" Content="1"/>
<Button Style="{DynamicResource flatButtonStyle}" Content="2"/>
Run Code Online (Sandbox Code Playgroud)