现有颜色的新颜色,在 xaml 中制作 renamed/copy/alias

New Color from existing Color, making a renamed/copy/alias in xaml

我试图遵循“不要重复自己”原则 XAML,但我已经到了挣扎的地步。

我正在为蒙皮定义颜色。 假设我已经定义了我想要作为皮肤基础的颜色:

<ResourceDictionary>
    ...
    <Color x:Key="B+">#FF999999</Color>
    <Color x:Key="B">#FF808080</Color>
    <Color x:Key="B-">#FF666666</Color>
    ...
</ResourceDictionary>

现在假设我想使用颜色 B 作为我的边框颜色,我会为我的文本框、按钮等设置 BorderBrush 的样式:

<Style TargetType="TextBox">
    <Setter Property="BorderBrush">
        <Setter.Value>
            <SolidColorBrush Color="{StaticResource B}" />
        </Setter.Value>
    </Setter> 
</Style>
<Style TargetType="Button">
    <Setter Property="BorderBrush">
        <Setter.Value>
            <SolidColorBrush Color="{StaticResource B}" />
        </Setter.Value>
    </Setter> 
</Style>

现在假设我想使用较暗的色调 B-,因此不必(记住)更改所有控件的资源,我宁愿使用一个名为 "placeholder" 颜色的替代方案B,所以我只需要更改一次:

<Color x:Key="controlBorderColor">B</Color><!-- Not working -->

我不知道该怎么做。在XAML中是否有通用的"pattern"或定义"constants"的方法?


解决方法

不可能按照我想要的方式,查看答案。

简而言之解决方法(感谢@dotsven 处理亮度的巧妙技巧):将所有颜色定义为画笔,并使用标记扩展来检索 Color 属性。

AFAIK Color 不支持它的内容分配,因此没有简单的方法从 XAML 定义或绑定它而不单独对 A、R、G、 B等属性。画笔在这里充当包装器,因此我们可以绑定到颜色。

将所有颜色定义为Brushes

<SolidColorBrush x:Key="B">
    <SolidColorBrush.Color>
        #FF808080
    </SolidColorBrush.Color>
</SolidColorBrush>

创建一个 MarkupExtension 以从 SolidColorBrush:

检索 Color
[MarkupExtensionReturnType(typeof(Color))]
public class SkinColorExtension : MarkupExtension {
  public SkinColorExtension() {
  }

  public SkinColorExtension(SolidColorBrush baseColor, int brightness) {
    this.BaseColor = baseColor;
    this.Brightness = brightness;
  }

  [ConstructorArgument("baseColor")]
  public SolidColorBrush BaseColor { get; set; }
  [ConstructorArgument("brightness")]
  public int Brightness { get; set; }

  public override object ProvideValue(IServiceProvider serviceProvider) {
    return SkinColor_Helper.ApplyBrightness(BaseColor.Color, Brightness);
  }
}

static class SkinColor_Helper {
  public static Color ApplyBrightness(Color c, int b) {
    Func<int, byte> Scale = (val) => {
      int scaledVal = val + b;
      if (scaledVal < 0) return 0;
      else if (scaledVal > 255) return 255;
      else return (byte)scaledVal;
    };
    return Color.FromArgb(c.A, Scale(c.R), Scale(c.G), Scale(c.B));
  }
}

然后通过亮度偏移创建我的皮肤颜色:

<SolidColorBrush x:Key="controlBorder" Color="{ext:SkinColor BaseColor={StaticResource B}, Brightness=-25}"/>
<SolidColorBrush x:Key="controlBackground" Color="{ext:SkinColor BaseColor={StaticResource B}, Brightness=-100}" />

当控件需要画笔时,例如Background, 只需绑定到 {StaticResource controlBackground}.

如果您必须绑定到需要 Color 的内容,请绑定到 {ext:SkinColor BaseColor={StaticResource controlBackground}} 以从画笔中检索它。

遗憾的是,无法创建作为另一个别名的静态资源。至少在 XAML.

中没有

如果您可以从代码访问资源字典,那么您可以只使用它的 Add 方法。毕竟是字典。

对于某些类型,例如 Styles,您可以使用继承伪造别名。例如:

<Style x:Key="Foo">...</Style>
<Style x:Key="Bar" BasedOn="{StaticResource Foo}"/>
<!-- now Bar is an "alias" for Foo -->

遗憾的是,没有对此的直接支持。但是通过使用自定义标记扩展,您可以获得一个灵活的解决方法。创建这样的标记扩展:

[MarkupExtensionReturnType(typeof(Color))]
public class RelativeColorExtension : MarkupExtension
{
    public RelativeColorExtension()
    {
    }

    public RelativeColorExtension(Color baseColor)
    {
        this.BaseColor = baseColor;
    }

    [ConstructorArgument("baseColor")]
    public Color BaseColor { get; set; }

    public override object ProvideValue(IServiceProvider serviceProvider)
    {
        return Color.FromArgb(this.BaseColor.A, (Byte)(this.BaseColor.R / 2), (Byte)(this.BaseColor.G /2), (Byte)(this.BaseColor.B / 2));
    }
}

那你就可以这样使用了:

    <Grid.Resources>
        <Color x:Key="Color1" R="10" G="10" B="128" A="255" />
        <SolidColorBrush x:Key="Color1Brush" Color="{StaticResource Color1}" />
        <SolidColorBrush x:Key="Color2Brush" Color="{ext:RelativeColor BaseColor={StaticResource Color1}}" />            
    </Grid.Resources>

请务必注意,您必须在单独的程序集中实现标记扩展。您将使用允许您以多种方式调暗、调亮或操纵基色的属性来扩展 markuop 扩展。