【问题标题】:How to Define Common Resources Across WPF Applications (including templates)?如何跨 WPF 应用程序定义公共资源(包括模板)?
【发布时间】:2014-01-16 10:23:33
【问题描述】:

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

this question 的选定答案表明我必须将该资源字典合并到我的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 资源在整个应用程序(包括控件模板)中可访问的正确方法是什么?

【问题讨论】:

  • 提供的答案是否不够好?
  • @RohitVats: 耐心 ;-) 我已经看到你的回答并且它有效,我可能会接受它;我只是倾向于将问题留待一天左右,以防其他人提出更好的解决方案。

标签: wpf resources controltemplate staticresource app.xaml


【解决方案1】:

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

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2022-08-17
    • 1970-01-01
    • 2012-02-08
    • 2020-10-10
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多