在UWP应用程序中覆盖Generic.xaml中的资源

3
这是我的Generic.xaml文件,我在其中定义了控件(CustomControlBackground)的默认背景色:

<ResourceDictionary
    xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation" 
    xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
    xmlns:local="using:TomShane.Framework.Controls">

  <ResourceDictionary.ThemeDictionaries>
    <ResourceDictionary x:Key="Default">
      <SolidColorBrush x:Key="CustomControlBackground" Color="Gray"/>
    </ResourceDictionary>
  </ResourceDictionary.ThemeDictionaries>

  <Style TargetType="local:CustomControl">
    <Setter Property="Background" Value="{ThemeResource CustomControlBackground}"/>
    <Setter Property="Template">
      <Setter.Value>
        <ControlTemplate TargetType="local:CustomControl">
          <Border
            Background="{TemplateBinding Background}"
            BorderBrush="{TemplateBinding BorderBrush}"
            BorderThickness="{TemplateBinding BorderThickness}">
          </Border>
        </ControlTemplate>
      </Setter.Value>
    </Setter>
  </Style>
</ResourceDictionary>

在App.xaml中,我想要覆盖来自Generic.xaml的默认背景:
<Application
    x:Class="TomShane.Framework.Demo.App"
    xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
    xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
    xmlns:local="using:TomShane.Framework.Demo"
    xmlns:ctrl="using:TomShane.Framework.Controls"
    RequestedTheme="Dark">
  <Application.Resources>
    <ResourceDictionary>
      <ResourceDictionary.ThemeDictionaries>
        <ResourceDictionary x:Key="Default">
          <SolidColorBrush x:Key="CustomControlBackground" Color="Red"/>
        </ResourceDictionary>
      </ResourceDictionary.ThemeDictionaries>
    </ResourceDictionary>
  </Application.Resources>
</Application>

通过这种技术,我能够覆盖UWP系统资源(如SystemControlBackgroundBaseLowBrush),但是当我尝试覆盖CustomControlBackground时,控件的背景保持灰色。

我漏掉了什么?


你首先加载哪一个?为什么要在App.xaml中覆盖颜色,不直接在那里定义它呢? - Igor Ralic
1
Generic.xaml 是主题/控件程序集的一部分,与主应用程序隔离开来。它应该是通用的,并且主应用程序应该能够覆盖一些基本颜色,如强调等。我没有显式加载 Generic.xaml,它是自动加载的,所以我只需要在 App.xaml 中重新定义资源即可。 - Tom Shane
2个回答

0

逻辑有误。

我们只能在较低的范围内覆盖较高范围的ResourceDictionary。实际上,这与资源查找行为有关,而不是哪个xaml文件先加载。

在WinRT xaml应用程序中,Resource Dictionary范围如下(非MS官方图片): enter image description here

基于以上,我们可以在app.xaml中覆盖系统默认资源,在page.xaml中覆盖系统/应用程序资源等。

因此,在您的代码中,实际上,在generic.xaml中定义的资源将覆盖您在app.xaml中定义的资源。如果我们简单地从generic.xaml中删除资源CustomControlBackground,您将获得红色背景。

[更新]

如果您确实想要一个默认主题,您可以保留设计,但我们不能使用x:Default来设置resourcedictionary。

例如,在您的情况下,如果请求的主题是Dark。您可以简单地使用以下内容来覆盖app.xaml中的默认主题。

        <ResourceDictionary.ThemeDictionaries>
            <ResourceDictionary x:Key="Dark">
                <SolidColorBrush x:Key="CustomControlBackground" Color="Yellow"/>
            </ResourceDictionary>
        </ResourceDictionary.ThemeDictionaries>

或者你可以将主题放入一个单独的文件中,只在客户端应用程序的app.xaml中引用它。但是你必须重写文件以使用不同的值。


没错,但我需要在主题程序集中的某个地方定义CustomControlBackground的默认背景,然后在App.xaml中覆盖它。由于Generic.xaml会自动加载,我无法控制何时以及何种顺序加载。 我(几乎)确定在旧的WPF应用程序中,我的OP中的步骤实际上是有效的。 - Tom Shane
我不认为WPF应用程序能够满足你的需求。实际上,这与资源查找行为有关。主题文件夹中的generic.xaml将始终是首先查找此场景的位置。一旦找到资源,查找就会停止。 - Alan Yao - MSFT
我承认,我的WPF可能是错误的。不管怎样,在UWP应用程序中有没有一种方法可以实现这个目标?是否可以使用自定义控件创建一个单独的程序集,并使用特定资源,并能够在客户端应用程序(App.xaml)中覆盖它们?如果不能在XAML中实现,那么是否可以在代码后台中实现? - Tom Shane
谢谢您的更新,不幸的是它仍然无法正常工作。我已经将所有“默认”字典更改为“暗黑”,但颜色仍未被覆盖并保持灰色。如果它对您有效,如果我能看到您的示例代码,那将是非常好的。 - Tom Shane
我很高兴在UWP中这不是真的。 - Jerry Nixon

0

很遗憾,目前还没有一种好的方法来定义主题资源以供自定义控件使用,并且可以像内置资源(例如SystemControlBackgroundBaseLowBrush)那样被覆盖。但是,如果您愿意在运行时移动一些东西,就有一个解决方法。诀窍是要最终使您的默认资源不包含在Generic.xaml中,而是在与应用程序资源合并的资源字典中定义,从而可以被覆盖。有多种方法可以实现这一点。

其中一种方法是创建一个具有关联代码文件的x:Class的单独资源字典。在其中定义您的默认主题资源。然后找出一种将类的实例合并到应用程序资源中的方法。其中一种方法是在控件的静态构造函数中。请注意不要合并超过一个实例。

一种稍微不同的方法,我更喜欢的是使用资源字典的代码后台来定义控件的默认样式。但是你不能为Generic.xaml添加代码后台,因为框架永远不会实例化该类的实例。相反,将样式移动到其自己的资源字典中(带有x:Class和代码后台),并将新字典的实例添加到Generic.xaml的合并字典中。在这个资源字典的构造函数中,您可以在调用InitializeComponent之后移动东西。例如,如果在xaml文件中在MergedDictionaries集合中定义一个资源字典并将主题资源放置在其中,则在运行时,您将能够从MergedDictionaries集合中删除该字典,并将其合并到应用程序的资源中。
任何一种方法的局限性都是如果您想通过主题(即Light和Dark)覆盖资源。将覆盖资源直接放入App.xaml的资源中将起作用,但不允许不同主题的不同值。要实现这一点,必须在放置在应用程序资源的MergedDictionaries中的资源字典的ThemeResources内定义覆盖资源。(我不知道,但这似乎可能是一个错误。)

CustomControl.xaml:

<ResourceDictionary x:Class="CustomControlLibrary.Themes.CustomControlTheme" 
    xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation" 
    xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
    xmlns:local="using:CustomControlLibrary">

    <ResourceDictionary.MergedDictionaries>
        <ResourceDictionary>
            <ResourceDictionary.ThemeDictionaries>
                <ResourceDictionary x:Key="Light">
                    <SolidColorBrush x:Key="CustomControlBackground" Color="#000000" />
                </ResourceDictionary>
                <ResourceDictionary x:Key="Dark">
                    <SolidColorBrush x:Key="CustomControlBackground" Color="#FFFFFF" />
                </ResourceDictionary>
            </ResourceDictionary.ThemeDictionaries>
        </ResourceDictionary>
    </ResourceDictionary.MergedDictionaries>

    <Style TargetType="local:CustomControl">
        <Setter Property="Background" Value="{ThemeResource CustomControlBackground}" />
    </Style>    

</ResourceDictionary>

CustomControl.xaml.cs:

namespace CustomControlLibrary.Themes
{
    partial class CustomControlTheme
    {
        public CustomControlTheme()
        {
            InitializeComponent();
            if(MergedDictionaries.Count > 0)
            {
                var themeResources = MergedDictionaries[0];
                MergedDictionaries.RemoveAt(0);
                Application.Current.Resources.MergedDictionaries.Insert(0, themeResources);
            }
        }
    }
}

Generic.xaml:

<ResourceDictionary
    xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
    xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
    xmlns:themes="using:CustomControlLibrary.Themes">

    <ResourceDictionary.MergedDictionaries>
        <themes:CustomControlTheme />
    </ResourceDictionary.MergedDictionaries>

</ResourceDictionary>

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接