When a background color is chosen by the user, you can use a converter that calculates luminosity contrast ratio to choose the best foreground color for the background given. In the code sample attached, I use a converter that does this getting the luminosity contrast for the background with a white foreground and a black foreground. Whichever has the better luminosity contrast ratio, is the foreground color I use.
I got formula for the luminosity contrast ratio from the W3C Recommendation for Web Content Accessiblity Guidelines (WCAG) 2.0. Outside of code, the formula looks like:
(L1 + 0.05) / (L2 + 0.05), where
- L1 is the relative luminance of the lighter color
- L2 is the relative luminance of the darker color
Next question I had was “how do I calculate relative luminance”? The formula for this is also within the guidelines. First of all, a definition: relative luminance is “the relative brightness of any point in a colorspace, normalized to 0 for the darkest black and 1 for the lightest white.”
It can be calculated using the formula below:
Relative Luminance = 0.2126 * R + 0.7152 * G + 0.0722 * B, where:
R: If RsRGB <= 0.03928 then R = RsRGB / 12.92 else R = ((RsRGB + 0.055)/1.055)2.4
G: If GsRGB <= 0.03928 then G = GsRGB / 12.92 else G = ((GsRGB + 0.055)/1.055)2.4
B: If BsRGB <= 0.03928 then B = BsRGB / 12.92 else B = ((BsRGB + 0.055)/1.055)2.4
Ok, but another variable is introduced here, how are RsRGB, GsRGB, BsRGB each calculated? This is a bit easier and is defined in the same area as the relative luminance formula.
- RsRGB = R8bit/255
- GsRGB = G8bit/255
- BsRGB = B8bit/255
Finally, now how does all of this look in C#?
- private double LuminosityContrast(Color foreground, Color background)
- {
- var foregroundLuminosity = RelativeLuminance(foreground.R, foreground.G, foreground.B);
- var backgroundLuminosity = RelativeLuminance(background.R, background.G, background.B);
- if (foregroundLuminosity > backgroundLuminosity)
- {
- return (foregroundLuminosity + 0.05) / (backgroundLuminosity + 0.05);
- }
- else
- {
- return (backgroundLuminosity + 0.05) / (foregroundLuminosity + 0.05);
- }
- }
- private double RelativeLuminance(byte r, byte g, byte b)
- {
- double rs = ((double)r / 255);
- double gs = ((double)g / 255);
- double bs = ((double)b / 255);
- double R = 0;
- double G = 0;
- double B = 0;
- R = (rs <= 0.03928) ? (double)(rs / 12.92) : Math.Pow(((rs + 0.055) / 1.055), 2.4);
- G = (gs <= 0.03928) ? (double)(gs / 12.92) : Math.Pow(((gs + 0.055) / 1.055), 2.4);
- B = (bs <= 0.03928) ? (double)(bs / 12.92) : Math.Pow(((bs + 0.055) / 1.055), 2.4);
- return ((double)(0.2126 * R)) + ((double)(0.7152 * G)) + ((double)(0.0722 * B));
- }
And now, I can use this in my Convert method of my IValueConverter:
- public object Convert(object value, Type targetType, object parameter, System.Globalization.CultureInfo culture)
- {
- if (value != null && value is Color)
- {
- Color backgroundColor = (Color)value;
- // Calculate the luminosity constrast ratio for the given color against black and white.
- var blackContrast = LuminosityContrast(Colors.Black, backgroundColor);
- var whiteContrast = LuminosityContrast(Colors.White, backgroundColor);
- // return a solid color brush using either black or white, depending on which had the higher luminosity constrast ratio.
- return blackContrast >= whiteContrast ? new SolidColorBrush(Colors.Black) : new SolidColorBrush(Colors.White);
- }
- else
- {
- return new SolidColorBrush(Colors.Black);
- }
- }
Finally, I can (declare and) use my converter in XAML to pick the better color for use against the background the user of my application has chosen.
- <Converters:ForegroundColorFromBackgroundColorConverter x:Key="foregroundDetector" />
- …
- …
- …
- <TextBlock HorizontalAlignment="Center" VerticalAlignment="Center"
- Foreground="{Binding SelectedBrush.Color, Converter={StaticResource ResourceKey=foregroundDetector}, Mode=OneWay}">
- <Run FontWeight="Bold">Selected Color</Run>
- </TextBlock>















Pingback: Windows Client Developer Roundup 062 for 3/7/2011 - Pete Brown's 10rem.net
Pingback: Windows Client Developer Roundup 062 for 3/7/2011 · All About Computer
Brilliant! That was very useful for grid headers where the user can change the column colors.
This was perfect for what I needed to accomplish. Thank you!
very, very helpfull, thank you!