5

我正在寻找一种方法来定义可从我的应用程序中的任何位置访问的 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>

此问题的选定答案表明我必须将该资源字典合并到我的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>

起初,这似乎有效。此窗口中的按钮以带有超厚边框的扁平方式适当地设置样式:

<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>

但是,一旦我在控制模板中使用我的共享资源,这将停止工作:

我的(出于这个问题的目的,非常简化)控制:

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)));
        }
    }
}

...和相应的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>

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

<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>

如果我运行这个,XamlParseException会抛出一个,说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>

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

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

4

1 回答 1

3

StaticResource将在应用程序实际运行之前加载 XAML 期间解析并分配给属性。所以,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. 合并您已经实现的 Generic.xaml 中的 ResourceDictionary。

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

在这里阅读 - StaticResource v/s DynamicResource

这将在不合并 Generic.xaml 中的字典的情况下工作

<Button Style="{DynamicResource flatButtonStyle}" Content="1"/>
<Button Style="{DynamicResource flatButtonStyle}" Content="2"/>
于 2013-12-29T10:44:40.673 回答