I just finished writing a small Windows Store app. It is a color mixing program that stands out because it includes an Alpha channel control.
I downloaded someone else’s color mixer and was sadly disappointed. Their user interface had multiple preview boxes, and changing a setting in one place did not cause other color settings to be updated. It was not intuitive and not pleasant to use. I decided that my first Windows Store app to get published should be a better color mixer.
Alpha Color Mixer
In this post, I will be including some code from the Alpha Color Mixer.
The first step was to create a new Visual Studio project. I chose to use a Windows Store – Blank App (XAML). This was picked because I am writing another Windows Store app and I already have the XAML for a page in that app that has a title and some minimal content, and I just needed to copy that code. Using a more complicated app design, like the Grid App (XAML) project, would have add a lot of unwanted code to my project.
Now that I had a page with a title and an area for some content, I added a preview box, a text box, and four sliders for alpha, red, green, and blue. This was easy to get working. The text box accepted 8 digits of hexadecimal, and represents the 32 bit color value with one byte per value. I added some code to limit the characters accepted by the box, which is shown below in it’s final form.
static public bool IsDigitOrFlow( Windows.System.VirtualKey Key, bool bAllowHex,
bool bAllowWhiteSpace, out bool bIsDigit)
{
bool isGoodData = true;
bIsDigit = false;
if( Key >= Windows.System.VirtualKey.Number0
&& Key <= Windows.System.VirtualKey.Number9 )
{
isGoodData = true;
bIsDigit = true;
}
else if (Key == Windows.System.VirtualKey.Tab)
isGoodData = true;
else if (Key == Windows.System.VirtualKey.Enter)
isGoodData = true;
else if( Key >= Windows.System.VirtualKey.NumberPad0
&& Key <= Windows.System.VirtualKey.NumberPad9)
{
isGoodData = true;
bIsDigit = true;
}
else if( bAllowHex && Key >= Windows.System.VirtualKey.A
&& Key <= Windows.System.VirtualKey.F )
{
isGoodData = true;
bIsDigit = true;
}
else if( bAllowWhiteSpace
&& ( Key == Windows.System.VirtualKey.Space || Key.ToString() == "188" ) )
{
isGoodData = true;
}
else if (Key == Windows.System.VirtualKey.Decimal || (int)Key == 190)
{
// The character is a decimal point, 190 is the keyboard period code
// which is not in the VirtualKey enumeration
isGoodData = false;
}
else if( Key == Windows.System.VirtualKey.Back )
isGoodData = true;
else
isGoodData = false;
return isGoodData;
}
private void HexBox_KeyDown(object sender, KeyRoutedEventArgs e)
{
bool bIsDigit = false;
if( IsDigitOrFlow( e.Key, true, false, out bIsDigit ) )
{
if (bIsDigit)
{
if( HexBox.SelectedText == null
|| HexBox.SelectedText.Length == 0)
{
if (HexBox.Text.Length >= 8)
e.Handled = true;
}
}
return;
}
e.Handled = true;
}
The code above is based on some code that I found on the internet. It is a bit tricky to limit keyboard input in a Windows Store C# app because there is no character based event handler. There is just key up and key down handlers. I think that there are a few keys that I reject, that should be accepted. HexBox_KeyDown(), seen in the above code block, is the handler function and it calls IsDigitOrFlow() to filter the keys being used. The initial code for this was modified later to handle the decimal text box, that was also added later.
Oh, and 188 is the comma key. I didn’t write a comment (yet) about it.
Standard Slider Style
The standard slider style seen above doesn’t provide the look that I needed. First, I changed the background colors. This let me have a red slider, a green slider, etc. then later, I made larger changes to get a bigger thumb and images for background. My first version of the app had sliders like the one in the picture above, but with colored backgrounds.
I eventually change the slider style, seem in the code below, to have a taller thumb and a transparent background. I commented out some of the transitions to keep the backgrounds transparent when the pointer is over the slider and when the slider is being pressed/clicked.
<Style TargetType="Slider">
<Setter Property="Background" Value="{StaticResource SliderTrackBackgroundThemeBrush}" />
<Setter Property="BorderBrush" Value="{StaticResource SliderBorderThemeBrush}" />
<Setter Property="BorderThickness" Value="{StaticResource SliderBorderThemeThickness}" />
<Setter Property="Foreground" Value="{StaticResource SliderTrackDecreaseBackgroundThemeBrush}" />
<Setter Property="ManipulationMode" Value="None" />
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="Slider">
<Grid Margin="{TemplateBinding Padding}">
<Grid.Resources>
<Style TargetType="Thumb" x:Key="SliderThumbStyle">
<Setter Property="BorderThickness" Value="0" />
<Setter Property="BorderBrush" Value="Black" />
<Setter Property="Background" Value="{StaticResource SliderThumbBackgroundThemeBrush}" />
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="Thumb">
<Border Background="{TemplateBinding Background}"
BorderBrush="Black"
BorderThickness="2" />
</ControlTemplate>
</Setter.Value>
</Setter>
</Style>
</Grid.Resources>
<VisualStateManager.VisualStateGroups>
<VisualStateGroup x:Name="CommonStates">
<VisualState x:Name="Normal" />
<VisualState x:Name="Pressed">
<Storyboard>
<!--<ObjectAnimationUsingKeyFrames Storyboard.TargetName="HorizontalDecreaseRect"
Storyboard.TargetProperty="Fill">
<DiscreteObjectKeyFrame KeyTime="0" Value="{StaticResource SliderTrackDecreasePressedBackgroundThemeBrush}" />
</ObjectAnimationUsingKeyFrames>
<ObjectAnimationUsingKeyFrames Storyboard.TargetName="HorizontalTrackRect"
Storyboard.TargetProperty="Fill">
<DiscreteObjectKeyFrame KeyTime="0" Value="{StaticResource SliderTrackPressedBackgroundThemeBrush}" />
</ObjectAnimationUsingKeyFrames>-->
<ObjectAnimationUsingKeyFrames Storyboard.TargetName="VerticalDecreaseRect"
Storyboard.TargetProperty="Fill">
<DiscreteObjectKeyFrame KeyTime="0" Value="{StaticResource SliderTrackDecreasePressedBackgroundThemeBrush}" />
</ObjectAnimationUsingKeyFrames>
<ObjectAnimationUsingKeyFrames Storyboard.TargetName="VerticalTrackRect"
Storyboard.TargetProperty="Fill">
<DiscreteObjectKeyFrame KeyTime="0" Value="{StaticResource SliderTrackPressedBackgroundThemeBrush}" />
</ObjectAnimationUsingKeyFrames>
<ObjectAnimationUsingKeyFrames Storyboard.TargetName="HorizontalThumb"
Storyboard.TargetProperty="Background">
<DiscreteObjectKeyFrame KeyTime="0" Value="{StaticResource SliderThumbPressedBackgroundThemeBrush}" />
</ObjectAnimationUsingKeyFrames>
<ObjectAnimationUsingKeyFrames Storyboard.TargetName="HorizontalThumb"
Storyboard.TargetProperty="BorderBrush">
<DiscreteObjectKeyFrame KeyTime="0" Value="{StaticResource SliderThumbPressedBorderThemeBrush}" />
</ObjectAnimationUsingKeyFrames>
<ObjectAnimationUsingKeyFrames Storyboard.TargetName="VerticalThumb"
Storyboard.TargetProperty="Background">
<DiscreteObjectKeyFrame KeyTime="0" Value="{StaticResource SliderThumbPressedBackgroundThemeBrush}" />
</ObjectAnimationUsingKeyFrames>
<ObjectAnimationUsingKeyFrames Storyboard.TargetName="VerticalThumb"
Storyboard.TargetProperty="BorderBrush">
<DiscreteObjectKeyFrame KeyTime="0" Value="{StaticResource SliderThumbPressedBorderThemeBrush}" />
</ObjectAnimationUsingKeyFrames>
</Storyboard>
</VisualState>
<VisualState x:Name="Disabled">
<Storyboard>
<ObjectAnimationUsingKeyFrames Storyboard.TargetName="HorizontalBorder"
Storyboard.TargetProperty="Stroke">
<DiscreteObjectKeyFrame KeyTime="0" Value="{StaticResource SliderDisabledBorderThemeBrush}" />
</ObjectAnimationUsingKeyFrames>
<ObjectAnimationUsingKeyFrames Storyboard.TargetName="VerticalBorder"
Storyboard.TargetProperty="Stroke">
<DiscreteObjectKeyFrame KeyTime="0" Value="{StaticResource SliderDisabledBorderThemeBrush}" />
</ObjectAnimationUsingKeyFrames>
<ObjectAnimationUsingKeyFrames Storyboard.TargetName="HorizontalDecreaseRect"
Storyboard.TargetProperty="Fill">
<DiscreteObjectKeyFrame KeyTime="0" Value="{StaticResource SliderTrackDecreaseDisabledBackgroundThemeBrush}" />
</ObjectAnimationUsingKeyFrames>
<ObjectAnimationUsingKeyFrames Storyboard.TargetName="HorizontalTrackRect"
Storyboard.TargetProperty="Fill">
<DiscreteObjectKeyFrame KeyTime="0" Value="{StaticResource SliderTrackDisabledBackgroundThemeBrush}" />
</ObjectAnimationUsingKeyFrames>
<ObjectAnimationUsingKeyFrames Storyboard.TargetName="VerticalDecreaseRect"
Storyboard.TargetProperty="Fill">
<DiscreteObjectKeyFrame KeyTime="0" Value="{StaticResource SliderTrackDecreaseDisabledBackgroundThemeBrush}" />
</ObjectAnimationUsingKeyFrames>
<ObjectAnimationUsingKeyFrames Storyboard.TargetName="VerticalTrackRect"
Storyboard.TargetProperty="Fill">
<DiscreteObjectKeyFrame KeyTime="0" Value="{StaticResource SliderTrackDisabledBackgroundThemeBrush}" />
</ObjectAnimationUsingKeyFrames>
<ObjectAnimationUsingKeyFrames Storyboard.TargetName="HorizontalThumb"
Storyboard.TargetProperty="Background">
<DiscreteObjectKeyFrame KeyTime="0" Value="{StaticResource SliderThumbDisabledBackgroundThemeBrush}" />
</ObjectAnimationUsingKeyFrames>
<ObjectAnimationUsingKeyFrames Storyboard.TargetName="HorizontalThumb"
Storyboard.TargetProperty="BorderBrush">
<DiscreteObjectKeyFrame KeyTime="0" Value="{StaticResource SliderThumbDisabledBackgroundThemeBrush}" />
</ObjectAnimationUsingKeyFrames>
<ObjectAnimationUsingKeyFrames Storyboard.TargetName="VerticalThumb"
Storyboard.TargetProperty="Background">
<DiscreteObjectKeyFrame KeyTime="0" Value="{StaticResource SliderThumbDisabledBackgroundThemeBrush}" />
</ObjectAnimationUsingKeyFrames>
<ObjectAnimationUsingKeyFrames Storyboard.TargetName="VerticalThumb"
Storyboard.TargetProperty="BorderBrush">
<DiscreteObjectKeyFrame KeyTime="0" Value="{StaticResource SliderThumbDisabledBackgroundThemeBrush}" />
</ObjectAnimationUsingKeyFrames>
<ObjectAnimationUsingKeyFrames Storyboard.TargetName="TopTickBar"
Storyboard.TargetProperty="Fill">
<DiscreteObjectKeyFrame KeyTime="0" Value="{StaticResource SliderTickMarkOutsideDisabledForegroundThemeBrush}" />
</ObjectAnimationUsingKeyFrames>
<ObjectAnimationUsingKeyFrames Storyboard.TargetName="HorizontalInlineTickBar"
Storyboard.TargetProperty="Fill">
<DiscreteObjectKeyFrame KeyTime="0" Value="{StaticResource SliderTickMarkInlineDisabledForegroundThemeBrush}" />
</ObjectAnimationUsingKeyFrames>
<ObjectAnimationUsingKeyFrames Storyboard.TargetName="BottomTickBar"
Storyboard.TargetProperty="Fill">
<DiscreteObjectKeyFrame KeyTime="0" Value="{StaticResource SliderTickMarkOutsideDisabledForegroundThemeBrush}" />
</ObjectAnimationUsingKeyFrames>
<ObjectAnimationUsingKeyFrames Storyboard.TargetName="LeftTickBar"
Storyboard.TargetProperty="Fill">
<DiscreteObjectKeyFrame KeyTime="0" Value="{StaticResource SliderTickMarkOutsideDisabledForegroundThemeBrush}" />
</ObjectAnimationUsingKeyFrames>
<ObjectAnimationUsingKeyFrames Storyboard.TargetName="VerticalInlineTickBar"
Storyboard.TargetProperty="Fill">
<DiscreteObjectKeyFrame KeyTime="0" Value="{StaticResource SliderTickMarkInlineDisabledForegroundThemeBrush}" />
</ObjectAnimationUsingKeyFrames>
<ObjectAnimationUsingKeyFrames Storyboard.TargetName="RightTickBar"
Storyboard.TargetProperty="Fill">
<DiscreteObjectKeyFrame KeyTime="0" Value="{StaticResource SliderTickMarkOutsideDisabledForegroundThemeBrush}" />
</ObjectAnimationUsingKeyFrames>
</Storyboard>
</VisualState>
<VisualState x:Name="PointerOver">
<Storyboard>
<!--<ObjectAnimationUsingKeyFrames Storyboard.TargetName="HorizontalDecreaseRect"
Storyboard.TargetProperty="Fill">
<DiscreteObjectKeyFrame KeyTime="0" Value="{StaticResource SliderTrackDecreasePointerOverBackgroundThemeBrush}" />
</ObjectAnimationUsingKeyFrames>
<ObjectAnimationUsingKeyFrames Storyboard.TargetName="HorizontalTrackRect"
Storyboard.TargetProperty="Fill">
<DiscreteObjectKeyFrame KeyTime="0" Value="{StaticResource SliderTrackPointerOverBackgroundThemeBrush}" />
</ObjectAnimationUsingKeyFrames>-->
<ObjectAnimationUsingKeyFrames Storyboard.TargetName="VerticalDecreaseRect"
Storyboard.TargetProperty="Fill">
<DiscreteObjectKeyFrame KeyTime="0" Value="{StaticResource SliderTrackDecreasePointerOverBackgroundThemeBrush}" />
</ObjectAnimationUsingKeyFrames>
<ObjectAnimationUsingKeyFrames Storyboard.TargetName="VerticalTrackRect"
Storyboard.TargetProperty="Fill">
<DiscreteObjectKeyFrame KeyTime="0" Value="{StaticResource SliderTrackPointerOverBackgroundThemeBrush}" />
</ObjectAnimationUsingKeyFrames>
<ObjectAnimationUsingKeyFrames Storyboard.TargetName="HorizontalThumb"
Storyboard.TargetProperty="Background">
<DiscreteObjectKeyFrame KeyTime="0" Value="{StaticResource SliderThumbPointerOverBackgroundThemeBrush}" />
</ObjectAnimationUsingKeyFrames>
<ObjectAnimationUsingKeyFrames Storyboard.TargetName="HorizontalThumb"
Storyboard.TargetProperty="BorderBrush">
<DiscreteObjectKeyFrame KeyTime="0" Value="{StaticResource SliderThumbPointerOverBorderThemeBrush}" />
</ObjectAnimationUsingKeyFrames>
<ObjectAnimationUsingKeyFrames Storyboard.TargetName="VerticalThumb"
Storyboard.TargetProperty="Background">
<DiscreteObjectKeyFrame KeyTime="0" Value="{StaticResource SliderThumbPointerOverBackgroundThemeBrush}" />
</ObjectAnimationUsingKeyFrames>
<ObjectAnimationUsingKeyFrames Storyboard.TargetName="VerticalThumb"
Storyboard.TargetProperty="BorderBrush">
<DiscreteObjectKeyFrame KeyTime="0" Value="{StaticResource SliderThumbPointerOverBorderThemeBrush}" />
</ObjectAnimationUsingKeyFrames>
</Storyboard>
</VisualState>
</VisualStateGroup>
<VisualStateGroup x:Name="FocusStates">
<VisualState x:Name="Focused">
<Storyboard>
<DoubleAnimation Storyboard.TargetName="FocusVisualWhiteHorizontal"
Storyboard.TargetProperty="Opacity"
To="1"
Duration="0" />
<DoubleAnimation Storyboard.TargetName="FocusVisualBlackHorizontal"
Storyboard.TargetProperty="Opacity"
To="1"
Duration="0" />
<DoubleAnimation Storyboard.TargetName="FocusVisualWhiteVertical"
Storyboard.TargetProperty="Opacity"
To="1"
Duration="0" />
<DoubleAnimation Storyboard.TargetName="FocusVisualBlackVertical"
Storyboard.TargetProperty="Opacity"
To="1"
Duration="0" />
</Storyboard>
</VisualState>
<VisualState x:Name="Unfocused" />
</VisualStateGroup>
</VisualStateManager.VisualStateGroups>
<Grid x:Name="HorizontalTemplate" Background="Transparent">
<Grid.ColumnDefinitions>
<ColumnDefinition Width="Auto" />
<ColumnDefinition Width="Auto" />
<ColumnDefinition Width="*" />
</Grid.ColumnDefinitions>
<Grid.RowDefinitions>
<RowDefinition Height="0" />
<RowDefinition Height="Auto" />
<RowDefinition Height="0" />
</Grid.RowDefinitions>
<Rectangle x:Name="HorizontalTrackRect"
Fill="{TemplateBinding Background}"
Grid.Row="1"
Grid.ColumnSpan="3" />
<Rectangle x:Name="HorizontalDecreaseRect"
Fill="{TemplateBinding Foreground}"
Grid.Row="1" />
<TickBar x:Name="TopTickBar"
Visibility="Collapsed"
Fill="{StaticResource SliderTickmarkOutsideBackgroundThemeBrush}"
Height="{StaticResource SliderOutsideTickBarThemeHeight}"
VerticalAlignment="Bottom"
Margin="0,0,0,2"
Grid.ColumnSpan="3" />
<TickBar x:Name="HorizontalInlineTickBar"
Visibility="Collapsed"
Fill="{StaticResource SliderTickMarkInlineBackgroundThemeBrush}"
Height="{StaticResource SliderTrackThemeHeight}"
Grid.Row="1"
Grid.ColumnSpan="3" />
<TickBar x:Name="BottomTickBar"
Visibility="Collapsed"
Fill="{StaticResource SliderTickmarkOutsideBackgroundThemeBrush}"
Height="{StaticResource SliderOutsideTickBarThemeHeight}"
VerticalAlignment="Top"
Margin="0,2,0,0"
Grid.Row="2"
Grid.ColumnSpan="3" />
<Rectangle x:Name="HorizontalBorder"
Stroke="{TemplateBinding BorderBrush}"
StrokeThickness="{TemplateBinding BorderThickness}"
Grid.Row="1"
Grid.ColumnSpan="3" />
<Thumb x:Name="HorizontalThumb"
Background="{StaticResource SliderThumbBackgroundThemeBrush}"
Style="{StaticResource SliderThumbStyle}"
DataContext="{TemplateBinding Value}"
Height="{StaticResource SliderTrackThemeHeight}"
Width="12"
Grid.Row="1"
Grid.Column="1" />
<Rectangle x:Name="FocusVisualWhiteHorizontal"
IsHitTestVisible="False"
Stroke="{StaticResource FocusVisualWhiteStrokeThemeBrush}"
StrokeEndLineCap="Square"
StrokeDashArray="1,1"
Opacity="0"
StrokeDashOffset="1.5"
Grid.RowSpan="3"
Grid.ColumnSpan="3" />
<Rectangle x:Name="FocusVisualBlackHorizontal"
IsHitTestVisible="False"
Stroke="{StaticResource FocusVisualBlackStrokeThemeBrush}"
StrokeEndLineCap="Square"
StrokeDashArray="1,1"
Opacity="0"
StrokeDashOffset="0.5"
Grid.RowSpan="3"
Grid.ColumnSpan="3" />
</Grid>
<Grid x:Name="VerticalTemplate" Visibility="Collapsed" Background="Transparent">
<Grid.RowDefinitions>
<RowDefinition Height="*" />
<RowDefinition Height="Auto" />
<RowDefinition Height="Auto" />
</Grid.RowDefinitions>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="0" />
<ColumnDefinition Width="Auto" />
<ColumnDefinition Width="0" />
</Grid.ColumnDefinitions>
<Rectangle x:Name="VerticalTrackRect"
Fill="{TemplateBinding Background}"
Grid.Column="1"
Grid.RowSpan="3" />
<Rectangle x:Name="VerticalDecreaseRect"
Fill="{TemplateBinding Foreground}"
Grid.Column="1"
Grid.Row="2" />
<TickBar x:Name="LeftTickBar"
Visibility="Collapsed"
Fill="{StaticResource SliderTickmarkOutsideBackgroundThemeBrush}"
Width="{StaticResource SliderOutsideTickBarThemeHeight}"
HorizontalAlignment="Right"
Margin="0,0,2,0"
Grid.RowSpan="3" />
<TickBar x:Name="VerticalInlineTickBar"
Visibility="Collapsed"
Fill="{StaticResource SliderTickMarkInlineBackgroundThemeBrush}"
Width="{StaticResource SliderTrackThemeHeight}"
Grid.Column="1"
Grid.RowSpan="3" />
<TickBar x:Name="RightTickBar"
Visibility="Collapsed"
Fill="{StaticResource SliderTickmarkOutsideBackgroundThemeBrush}"
Width="{StaticResource SliderOutsideTickBarThemeHeight}"
HorizontalAlignment="Left"
Margin="2,0,0,0"
Grid.Column="2"
Grid.RowSpan="3" />
<Rectangle x:Name="VerticalBorder"
Stroke="{TemplateBinding BorderBrush}"
StrokeThickness="{TemplateBinding BorderThickness}"
Grid.Column="1"
Grid.RowSpan="3" />
<Thumb x:Name="VerticalThumb"
Background="{StaticResource SliderThumbBackgroundThemeBrush}"
Style="{StaticResource SliderThumbStyle}"
DataContext="{TemplateBinding Value}"
Width="{StaticResource SliderTrackThemeHeight}"
Height="{StaticResource SliderTrackThemeHeight}"
Grid.Row="1"
Grid.Column="1" />
<Rectangle x:Name="FocusVisualWhiteVertical"
IsHitTestVisible="False"
Stroke="{StaticResource FocusVisualWhiteStrokeThemeBrush}"
StrokeEndLineCap="Square"
StrokeDashArray="1,1"
Opacity="0"
StrokeDashOffset="1.5"
Grid.RowSpan="3"
Grid.ColumnSpan="3" />
<Rectangle x:Name="FocusVisualBlackVertical"
IsHitTestVisible="False"
Stroke="{StaticResource FocusVisualBlackStrokeThemeBrush}"
StrokeEndLineCap="Square"
StrokeDashArray="1,1"
Opacity="0"
StrokeDashOffset="0.5"
Grid.RowSpan="3"
Grid.ColumnSpan="3" />
</Grid>
</Grid>
</ControlTemplate>
</Setter.Value>
</Setter>
</Style>
I am aware that my line numbering runs out at 312 and that there is a glitch at the end. The line numbers are a background image, and I never expected to have this many lines of code in a post.
To accomplish the various backgrounds for the sliders, I use images with tweaked margins to get them to appear under the sliders. he is a tiny bit of code to show how I did it:
<Grid>
<Image Margin="6,8,18,3" Source="/Assets/redgradation.jpg" HorizontalAlignment="Stretch" VerticalAlignment="Top" Stretch="Fill" Height="16"/>
<Slider Value="255" x:Name="RedSlider" ValueChanged="Slider_ValueChanged" Foreground="Transparent" Maximum="255" Background="Transparent"
SmallChange="1" Margin="0,0,12,3" VerticalAlignment="Top" HorizontalAlignment="Stretch"/>
</Grid>
Once I got the sliders looking nice with their various shaded backgrounds, I moved on to the more interesting parts of the app.
I added HSB (or HSL) sliders to give the app a more varied interface. I had to find some code on the internet to do the RGB to HSL conversion in each direction. I have code for this in some other C++ Windows program, but that source code is filed away and the internet was easier to search than my own code archives. Here’s the code for the conversions on both directions:
using System;
using Windows.UI;
namespace ColorMixer
{
public class ColorRGB
{
public byte R;
public byte G;
public byte B;
public ColorRGB(Color value)
{
this.R = value.R;
this.G = value.G;
this.B = value.B;
}
public static implicit operator Color(ColorRGB rgb)
{
Color c = Color.FromArgb( 0xff, rgb.R, rgb.G, rgb.B);
return c;
}
public static explicit operator ColorRGB(Color c)
{
return new ColorRGB(c);
}
// Given H,S,L in range of 0-1
// Returns a Color (RGB struct) in range of 0-255
public static ColorRGB HSL2RGB(double h, double sl, double l)
{
double v;
double r,g,b;
r = l; // default to gray
g = l;
b = l;
v = (l <= 0.5) ? (l * (1.0 + sl)) : (l + sl - l * sl);
if (v > 0)
{
double m;
double sv;
int sextant;
double fract, vsf, mid1, mid2;
m = l + l - v;
sv = (v - m ) / v;
h *= 6.0;
sextant = (int)h;
fract = h - sextant;
vsf = v * sv * fract;
mid1 = m + vsf;
mid2 = v - vsf;
switch (sextant)
{
case 0:
r = v;
g = mid1;
b = m;
break;
case 1:
r = mid2;
g = v;
b = m;
break;
case 2:
r = m;
g = v;
b = mid1;
break;
case 3:
r = m;
g = mid2;
b = v;
break;
case 4:
r = mid1;
g = m;
b = v;
break;
case 5:
r = v;
g = m;
b = mid2;
break;
}
}
ColorRGB rgb = new ColorRGB( Colors.Black );
rgb.R = Convert.ToByte(r * 255.0f);
rgb.G = Convert.ToByte(g * 255.0f);
rgb.B = Convert.ToByte(b * 255.0f);
return rgb;
}
// Given a Color (RGB Struct) in range of 0-255
// Return H,S,L in range of 0-1
public static void RGB2HSL (ColorRGB rgb, out double h, out double s, out double l)
{
double r = rgb.R/255.0;
double g = rgb.G/255.0;
double b = rgb.B/255.0;
double v;
double m;
double vm;
double r2, g2, b2;
h = 0; // default to black
s = 0;
l = 0;
v = Math.Max(r,g);
v = Math.Max(v,b);
m = Math.Min(r,g);
m = Math.Min(m,b);
l = (m + v) / 2.0;
if (l <= 0.0)
{
return;
}
vm = v - m;
s = vm;
if (s > 0.0)
{
s /= (l <= 0.5) ? (v + m ) : (2.0 - v - m) ;
}
else
{
return;
}
r2 = (v - r) / vm;
g2 = (v - g) / vm;
b2 = (v - b) / vm;
if (r == v)
{
h = (g == m ? 5.0 + b2 : 1.0 - g2);
}
else if (g == v)
{
h = (b == m ? 1.0 + r2 : 3.0 - b2);
}
else
{
h = (r == m ? 3.0 + g2 : 5.0 - r2);
}
h /= 6.0;
}
}
}
The named colors are the colors provided by the Windows.UI.Colors class. These are only useful to programmers, but that’s me! I included these to make things interesting. Here’s a class that I used to make a list and sort it by hue, saturation, and lightness:
public class NamedColor
{
public NamedColor( string InName, Color InColor )
{
name = InName;
color = InColor;
ColorRGB RGBValue = new ColorRGB( color );
ColorRGB.RGB2HSL( RGBValue, out hue, out saturation, out lightness );
}
public string name;
public Color color;
public double hue;
public double saturation;
public double lightness;
}
public class ColorList : List<NamedColor>
{
private static int CompareHSL( NamedColor x, NamedColor y )
{
if( x == null || y == null )
return 0;
if( x.hue < y.hue )
return -1;
else if( x.hue > y.hue )
return 1;
else if( x.lightness < y.lightness )
return 1;
else if( x.lightness > y.lightness )
return -1;
else if( x.saturation < y.saturation )
return -1;
else if( x.saturation > y.saturation )
return 1;
return 0;
}
public ColorList()
{
this.Add( new NamedColor( "AliceBlue", Windows.UI.Colors.AliceBlue ) );
this.Add( new NamedColor( "AntiqueWhite", Windows.UI.Colors.AntiqueWhite) );
this.Add( new NamedColor( "Aqua", Windows.UI.Colors.Aqua) );
this.Add( new NamedColor( "Aquamarine", Windows.UI.Colors.Aquamarine) );
this.Add( new NamedColor( "Azure", Windows.UI.Colors.Azure) );
this.Add( new NamedColor( "Beige", Windows.UI.Colors.Beige) );
this.Add( new NamedColor( "Bisque", Windows.UI.Colors.Bisque) );
this.Add( new NamedColor( "Black", Windows.UI.Colors.Black) );
this.Add( new NamedColor( "BlanchedAlmond", Windows.UI.Colors.BlanchedAlmond) );
this.Add( new NamedColor( "Blue", Windows.UI.Colors.Blue) );
this.Add( new NamedColor( "BlueViolet", Windows.UI.Colors.BlueViolet) );
this.Add( new NamedColor( "Brown", Windows.UI.Colors.Brown) );
this.Add( new NamedColor( "BurlyWood", Windows.UI.Colors.BurlyWood) );
this.Add( new NamedColor( "CadetBlue", Windows.UI.Colors.CadetBlue) );
this.Add( new NamedColor( "Chartreuse", Windows.UI.Colors.Chartreuse) );
this.Add( new NamedColor( "Chocolate", Windows.UI.Colors.Chocolate) );
this.Add( new NamedColor( "Coral", Windows.UI.Colors.Coral) );
this.Add( new NamedColor( "CornflowerBlue", Windows.UI.Colors.CornflowerBlue) );
this.Add( new NamedColor( "Cornsilk", Windows.UI.Colors.Cornsilk) );
this.Add( new NamedColor( "Crimson", Windows.UI.Colors.Crimson) );
this.Add( new NamedColor( "Cyan", Windows.UI.Colors.Cyan) );
this.Add( new NamedColor( "DarkBlue", Windows.UI.Colors.DarkBlue) );
this.Add( new NamedColor( "DarkCyan", Windows.UI.Colors.DarkCyan) );
this.Add( new NamedColor( "DarkGoldenrod", Windows.UI.Colors.DarkGoldenrod) );
this.Add( new NamedColor( "DarkGray", Windows.UI.Colors.DarkGray) );
this.Add( new NamedColor( "DarkGreen", Windows.UI.Colors.DarkGreen) );
this.Add( new NamedColor( "DarkKhaki", Windows.UI.Colors.DarkKhaki) );
this.Add( new NamedColor( "DarkMagenta", Windows.UI.Colors.DarkMagenta) );
this.Add( new NamedColor( "DarkOliveGreen", Windows.UI.Colors.DarkOliveGreen) );
this.Add( new NamedColor( "DarkOrange", Windows.UI.Colors.DarkOrange) );
this.Add( new NamedColor( "DarkOrchid", Windows.UI.Colors.DarkOrchid) );
this.Add( new NamedColor( "DarkRed", Windows.UI.Colors.DarkRed) );
this.Add( new NamedColor( "DarkSalmon", Windows.UI.Colors.DarkSalmon) );
this.Add( new NamedColor( "DarkSeaGreen", Windows.UI.Colors.DarkSeaGreen) );
this.Add( new NamedColor( "DarkSlateBlue", Windows.UI.Colors.DarkSlateBlue) );
this.Add( new NamedColor( "DarkSlateGray", Windows.UI.Colors.DarkSlateGray) );
this.Add( new NamedColor( "DarkTurquoise", Windows.UI.Colors.DarkTurquoise) );
this.Add( new NamedColor( "DarkViolet", Windows.UI.Colors.DarkViolet) );
this.Add( new NamedColor( "DeepPink", Windows.UI.Colors.DeepPink) );
this.Add( new NamedColor( "DeepSkyBlue", Windows.UI.Colors.DeepSkyBlue) );
this.Add( new NamedColor( "DimGray", Windows.UI.Colors.DimGray) );
this.Add( new NamedColor( "DodgerBlue", Windows.UI.Colors.DodgerBlue) );
this.Add( new NamedColor( "Firebrick", Windows.UI.Colors.Firebrick) );
this.Add( new NamedColor( "FloralWhite", Windows.UI.Colors.FloralWhite) );
this.Add( new NamedColor( "ForestGreen", Windows.UI.Colors.ForestGreen) );
this.Add( new NamedColor( "Fuchsia", Windows.UI.Colors.Fuchsia) );
this.Add( new NamedColor( "Gainsboro", Windows.UI.Colors.Gainsboro) );
this.Add( new NamedColor( "GhostWhite", Windows.UI.Colors.GhostWhite) );
this.Add( new NamedColor( "Gold", Windows.UI.Colors.Gold) );
this.Add( new NamedColor( "Goldenrod", Windows.UI.Colors.Goldenrod) );
this.Add( new NamedColor( "Gray", Windows.UI.Colors.Gray) );
this.Add( new NamedColor( "Green", Windows.UI.Colors.Green) );
this.Add( new NamedColor( "GreenYellow", Windows.UI.Colors.GreenYellow) );
this.Add( new NamedColor( "Honeydew", Windows.UI.Colors.Honeydew) );
this.Add( new NamedColor( "HotPink", Windows.UI.Colors.HotPink) );
this.Add( new NamedColor( "IndianRed", Windows.UI.Colors.IndianRed) );
this.Add( new NamedColor( "Indigo", Windows.UI.Colors.Indigo) );
this.Add( new NamedColor( "Ivory", Windows.UI.Colors.Ivory) );
this.Add( new NamedColor( "Khaki", Windows.UI.Colors.Khaki) );
this.Add( new NamedColor( "Lavender", Windows.UI.Colors.Lavender) );
this.Add( new NamedColor( "LavenderBlush", Windows.UI.Colors.LavenderBlush) );
this.Add( new NamedColor( "LawnGreen", Windows.UI.Colors.LawnGreen) );
this.Add( new NamedColor( "LemonChiffon", Windows.UI.Colors.LemonChiffon) );
this.Add( new NamedColor( "LightBlue", Windows.UI.Colors.LightBlue) );
this.Add( new NamedColor( "LightCoral", Windows.UI.Colors.LightCoral) );
this.Add( new NamedColor( "LightCyan", Windows.UI.Colors.LightCyan) );
this.Add( new NamedColor( "LightGoldenrodYellow", Windows.UI.Colors.LightGoldenrodYellow) );
this.Add( new NamedColor( "LightGray", Windows.UI.Colors.LightGray) );
this.Add( new NamedColor( "LightGreen", Windows.UI.Colors.LightGreen) );
this.Add( new NamedColor( "LightPink", Windows.UI.Colors.LightPink) );
this.Add( new NamedColor( "LightSalmon", Windows.UI.Colors.LightSalmon) );
this.Add( new NamedColor( "LightSeaGreen", Windows.UI.Colors.LightSeaGreen) );
this.Add( new NamedColor( "LightSkyBlue", Windows.UI.Colors.LightSkyBlue) );
this.Add( new NamedColor( "LightSlateGray", Windows.UI.Colors.LightSlateGray) );
this.Add( new NamedColor( "LightSteelBlue", Windows.UI.Colors.LightSteelBlue) );
this.Add( new NamedColor( "LightYellow", Windows.UI.Colors.LightYellow) );
this.Add( new NamedColor( "Lime", Windows.UI.Colors.Lime) );
this.Add( new NamedColor( "LimeGreen", Windows.UI.Colors.LimeGreen) );
this.Add( new NamedColor( "Linen", Windows.UI.Colors.Linen) );
this.Add( new NamedColor( "Magenta", Windows.UI.Colors.Magenta) );
this.Add( new NamedColor( "Maroon", Windows.UI.Colors.Maroon) );
this.Add( new NamedColor( "MediumAquamarine", Windows.UI.Colors.MediumAquamarine) );
this.Add( new NamedColor( "MediumBlue", Windows.UI.Colors.MediumBlue) );
this.Add( new NamedColor( "MediumOrchid", Windows.UI.Colors.MediumOrchid) );
this.Add( new NamedColor( "MediumPurple", Windows.UI.Colors.MediumPurple) );
this.Add( new NamedColor( "MediumSeaGreen", Windows.UI.Colors.MediumSeaGreen) );
this.Add( new NamedColor( "MediumSlateBlue", Windows.UI.Colors.MediumSlateBlue) );
this.Add( new NamedColor( "MediumSpringGreen", Windows.UI.Colors.MediumSpringGreen) );
this.Add( new NamedColor( "MediumTurquoise", Windows.UI.Colors.MediumTurquoise) );
this.Add( new NamedColor( "MediumVioletRed", Windows.UI.Colors.MediumVioletRed) );
this.Add( new NamedColor( "MidnightBlue", Windows.UI.Colors.MidnightBlue) );
this.Add( new NamedColor( "MintCream", Windows.UI.Colors.MintCream) );
this.Add( new NamedColor( "MistyRose", Windows.UI.Colors.MistyRose) );
this.Add( new NamedColor( "Moccasin", Windows.UI.Colors.Moccasin) );
this.Add( new NamedColor( "NavajoWhite", Windows.UI.Colors.NavajoWhite) );
this.Add( new NamedColor( "Navy", Windows.UI.Colors.Navy) );
this.Add( new NamedColor( "OldLace", Windows.UI.Colors.OldLace) );
this.Add( new NamedColor( "Olive", Windows.UI.Colors.Olive) );
this.Add( new NamedColor( "OliveDrab", Windows.UI.Colors.OliveDrab) );
this.Add( new NamedColor( "Orange", Windows.UI.Colors.Orange) );
this.Add( new NamedColor( "OrangeRed", Windows.UI.Colors.OrangeRed) );
this.Add( new NamedColor( "Orchid", Windows.UI.Colors.Orchid) );
this.Add( new NamedColor( "PaleGoldenrod", Windows.UI.Colors.PaleGoldenrod) );
this.Add( new NamedColor( "PaleGreen", Windows.UI.Colors.PaleGreen) );
this.Add( new NamedColor( "PaleTurquoise", Windows.UI.Colors.PaleTurquoise) );
this.Add( new NamedColor( "PaleVioletRed", Windows.UI.Colors.PaleVioletRed) );
this.Add( new NamedColor( "PapayaWhip", Windows.UI.Colors.PapayaWhip) );
this.Add( new NamedColor( "PeachPuff", Windows.UI.Colors.PeachPuff) );
this.Add( new NamedColor( "Peru", Windows.UI.Colors.Peru) );
this.Add( new NamedColor( "Pink", Windows.UI.Colors.Pink) );
this.Add( new NamedColor( "Plum", Windows.UI.Colors.Plum) );
this.Add( new NamedColor( "PowderBlue", Windows.UI.Colors.PowderBlue) );
this.Add( new NamedColor( "Purple", Windows.UI.Colors.Purple) );
this.Add( new NamedColor( "Red", Windows.UI.Colors.Red) );
this.Add( new NamedColor( "RosyBrown", Windows.UI.Colors.RosyBrown) );
this.Add( new NamedColor( "RoyalBlue", Windows.UI.Colors.RoyalBlue) );
this.Add( new NamedColor( "SaddleBrown", Windows.UI.Colors.SaddleBrown) );
this.Add( new NamedColor( "Salmon", Windows.UI.Colors.Salmon) );
this.Add( new NamedColor( "SandyBrown", Windows.UI.Colors.SandyBrown) );
this.Add( new NamedColor( "SeaGreen", Windows.UI.Colors.SeaGreen) );
this.Add( new NamedColor( "SeaShell", Windows.UI.Colors.SeaShell) );
this.Add( new NamedColor( "Sienna", Windows.UI.Colors.Sienna) );
this.Add( new NamedColor( "Silver", Windows.UI.Colors.Silver) );
this.Add( new NamedColor( "SkyBlue", Windows.UI.Colors.SkyBlue) );
this.Add( new NamedColor( "SlateBlue", Windows.UI.Colors.SlateBlue) );
this.Add( new NamedColor( "SlateGray", Windows.UI.Colors.SlateGray) );
this.Add( new NamedColor( "Snow", Windows.UI.Colors.Snow) );
this.Add( new NamedColor( "SpringGreen", Windows.UI.Colors.SpringGreen) );
this.Add( new NamedColor( "SteelBlue", Windows.UI.Colors.SteelBlue) );
this.Add( new NamedColor( "Tan", Windows.UI.Colors.Tan) );
this.Add( new NamedColor( "Teal", Windows.UI.Colors.Teal) );
this.Add( new NamedColor( "Thistle", Windows.UI.Colors.Thistle) );
this.Add( new NamedColor( "Tomato", Windows.UI.Colors.Tomato) );
//this.Add( new NamedColor( "Transparent", Windows.UI.Colors.Transparent) );
this.Add( new NamedColor( "Turquoise", Windows.UI.Colors.Turquoise) );
this.Add( new NamedColor( "Violet", Windows.UI.Colors.Violet) );
this.Add( new NamedColor( "Wheat", Windows.UI.Colors.Wheat) );
this.Add( new NamedColor( "White", Windows.UI.Colors.White) );
this.Add( new NamedColor( "WhiteSmoke", Windows.UI.Colors.WhiteSmoke) );
this.Add( new NamedColor( "Yellow", Windows.UI.Colors.Yellow) );
this.Add( new NamedColor( "YellowGreen", Windows.UI.Colors.YellowGreen) );
this.Sort( CompareHSL );
}
}
The list came courtesy of a post on StackOverflow. I added the strings with the color names to the list that was provided. I think that there is a C# way to get the actual name of the member variable at runtime, but I’m not familiar with that aspect of C#, so I added a string myself.
You can also see the sorting function and the call to sort the list. My daughter had immediately rejected the idea of showing the list alphabetically. She was right that no one is searching by color name, but rather by color hue.
I took out the transparent color in the current version because it has no color hue and any color can be made completely transparent using the Alpha slider.
After working on this for a day, I got to the final version that you see in the first image of this post. Besides the slider style changes, the conversions between RGB and HSL, and the interesting keyboard handling, there is not much else to this app.
I debated posting all of the source code for this app here and as a zip file, but I chose not to do so. There are millions of unethical people in the world who would take my code, change the name of the app, and try to make a few dollars selling it. I don’t really want to see a clone of my app show up on the in the Windows Store. If you want to see all of the source code, register for my blog and make a comment that you want it. I’ll send it to you in an email.
Note that if you register for my blog and make no comment, I will delete the user account because it looks like a spam account.
I will add some more bits and pieces of this app in later posts. keep an eye out for them.
Dave