Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

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

I am attempting to follow the Don't Repeat Yourself principle i XAML, but I've reached a point where I'm struggling.

I am defining colors for skinning. Say I have defined the colors I want to base my skin on:

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

Now say I want to use the color B as my border color, I'd style the BorderBrush of my textboxs, buttons etc.:

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

Now suppose I want to use a darker tone, B-, and so instead of having (to remember) to change the resource for all controls, I would rather have an alternative named "placeholder" color from say B, so I'd only have to change it once:

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

I am at a loss on how to do this. Is there a general "pattern" or way of defining "constants" in XAML?


Workaround solution

Not possible the way I want to, see answers.

Workaround in short (Thanks to @dotsven for the neat trick to handle brightness): Define all colors as brushes, and use markup extensions to retrieve the Color property.

AFAIK Color doesn't support it assignment of content, so there is no easy way from XAML to define or bind to it without doing it individually to the A,R,G,B etc. attributes. The brush sort of serves as a wrapper here so we can bind to Color.

Define all colors as Brushes

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

Create a MarkupExtension to retrieve Color from the SolidColorBrush:

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

Then create my skinning colors by brightness offsets as such:

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

When controls need a brush, e.g. Background, just bind to {StaticResource controlBackground}.

If you have to bind to something that needs a Color, bind to {ext:SkinColor BaseColor={StaticResource controlBackground}} to retrieve it from the brush instead.

like image 648
Skod Avatar asked Dec 21 '25 20:12

Skod


1 Answers

Unfortunately there's no direct support for this. But by using a custom markup extension you get a fexible workaround. Create a markup extension like this:

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

Then you can use it as follows:

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

It's important to note that you've got to implement the markup extension in a separate assembly. You'd extend the markuop extension with properties that allow you to dim, brighten or manipulate the base color in a number of ways.

like image 167
dotsven Avatar answered Dec 23 '25 23:12

dotsven



Donate For Us

If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!