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

时间:2015-10-31 17:45:44

标签: xaml custom-controls uwp

这是我的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时,控件的背景始终保持灰色。

我错过了什么?

2 个答案:

答案 0 :(得分:0)

逻辑错误。

我们只能在较低范围的ResourceDictionary中覆盖较高范围的ResourceDictionary。实际上,它涉及资源查找行为,而不是关于首先加载哪个xaml文件

在WinRT xaml应用程序中,资源字典范围如下所示(不是MS官方图片): enter image description here

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

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

[<强>更新

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

例如,如果请求的主题是黑暗(在您的情况下)。您只需使用以下内容覆盖app.xaml中的默认值即可。

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

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

答案 1 :(得分:0)

不幸的是,没有一种很好的方法来定义自定义控件的主题资源,以便它们可以像内置资源(如SystemControlBackgroundBaseLowBrush)一样被覆盖。但是如果你愿意在运行时移动一些东西,那就有一个解决方法。诀窍是最终导致Generic.xaml中没有包含默认资源的情况,而是在与应用程序资源合并的资源字典中定义,可以覆盖它们。实现这一目标的方法不止一种。

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

我更喜欢的一种不同的方法是使用资源字典的代码隐藏来定义控件的默认样式。但是你不能拥有Generic.xaml的代码隐藏,因为框架永远不会实例化类的实例。相反,将样式移动到其自己的资源字典中(使用x:Class和代码隐藏),并将新字典的实例添加到Generic.xaml的合并字典中。在调用InitializeComponent之后,您将能够在此资源字典的构造函数中移动。例如,如果在xaml文件中您在MergedDictionaries集合中定义资源字典并将主题资源放入其中,那么在运行时您可以从MergedDictionaries集合中删除该字典并将其合并到应用程序的资源中。

任何一种方法的限制是,如果您想按主题覆盖资源,即光明和黑暗。将重写资源直接放入App.xaml的资源中将起作用,但不允许不同主题的不同值。为此,有必要在资源字典的ThemeResources中定义覆盖资源,资源字典本身放在应用程序资源的MergedDictionaries中。 (我不知道,但似乎这可能是一个错误。)

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>