#231 – You Can Use a Brush for a Control’s Foreground

Though brushes in WPF are most often used to fill in the background for a window or a control,  you can also render the foreground of many controls using a brush.

For example, we can use a brush instead of a solid color for the Foreground property of a Label.  The text of the label will be rendered in a specified font and the body of the letters will be rendered using the specified brush.

		<Label Content="You can render text with a brush.." HorizontalAlignment="Center" Margin="20"
			   FontSize="24" FontWeight="Bold">
			<Label.Foreground>
				<LinearGradientBrush StartPoint="0,0.5" EndPoint="1.0,0.5">
					<GradientStop Color="DarkSlateBlue" Offset="0.0"/>
					<GradientStop Color="SkyBlue" Offset="1.0"/>
				</LinearGradientBrush>
			</Label.Foreground>
		</Label>

Advertisement

#230 – Changing a Radial Gradient as Mouse Moves Over a Control

Here’s an example of one way to change a radial gradient at runtime, based on mouse movements.

The radial gradient is defined in the XAML:

		<Button x:Name="btnActiveGradient" Content="Click Me" Width="120" Height="30" Margin="30"
			MouseMove="btnActiveGradient_MouseMove"
			MouseLeave="btnActiveGradient_MouseLeave"
			Style="{DynamicResource ButtonStyle1}" >
			<Button.Background>
				<RadialGradientBrush x:Name="gradRadial" RadiusX="0.25">
					<GradientStop Color="AliceBlue" Offset="0.0"/>
					<GradientStop Color="LightSteelBlue" Offset="1.0"/>
				</RadialGradientBrush>
			</Button.Background>
		</Button>

We also need to disable the default rendering of the button while the mouse is over it.  We can do this by changing the RenderMouseOver property of the ButtonChrome object–found in the button’s control template–to False.

Finally, we add some code to the button’s MouseMove and MouseLeave events, to change the gradient’s origin and center as we move the mouse.

        private void btnActiveGradient_MouseMove(object sender, System.Windows.Input.MouseEventArgs e)
        {
            Point pt = Mouse.GetPosition(btnActiveGradient);
            gradRadial.GradientOrigin = new Point(pt.X / btnActiveGradient.Width, pt.Y / btnActiveGradient.Height);
            gradRadial.Center = gradRadial.GradientOrigin;
        }

        private void btnActiveGradient_MouseLeave(object sender, System.Windows.Input.MouseEventArgs e)
        {
            gradRadial.GradientOrigin = new Point(0.5, 0.5);   // Default
            gradRadial.Center = gradRadial.GradientOrigin;
        }

#229 – Using a Gradient Brush for a Window’s Border

The Window class has BorderBrush and BorderThickness properties that allow creating a thick border around the window’s client area.  (Just inside the standard window chrome).

You can use any brush you like as a border brush.  In the example below, we use a linear gradient brush that starts in the upper left corner of the window and cycles through the colors of the rainbow until it reaches the lower right corner of the window.  Note that we set the BorderThickness to 10.

<Window
	xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
	xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
	xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
	xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
	mc:Ignorable="d"
	xmlns:local="clr-namespace:WpfApplication1"
    xmlns:sys="clr-namespace:System;assembly=mscorlib"
    x:Class="WpfApplication1.MainWindow"
	x:Name="Window"
	Title="Rainbow Border"
	BorderThickness="10">

	<Window.BorderBrush>
		<LinearGradientBrush StartPoint="0,0" EndPoint="1,1">
		    <GradientStop Color="Red" Offset="0.0"/>
		    <GradientStop Color="Orange" Offset="0.17"/>
		    <GradientStop Color="Yellow" Offset="0.33"/>
		    <GradientStop Color="Green" Offset="0.5"/>
		    <GradientStop Color="Blue" Offset="0.67"/>
		    <GradientStop Color="Indigo" Offset="0.83"/>
		    <GradientStop Color="Violet" Offset="1.0"/>
		</LinearGradientBrush>
	</Window.BorderBrush>

	<StackPanel VerticalAlignment="Center">
		<Button Content="Click Me" Width="100" Margin="10"/>
		<Label Content="I'm a label, you know" HorizontalAlignment="Center" Margin="10"/>
	</StackPanel>
</Window>

#228 – Starting/Ending a Gradient Fill Outside a Control

If you specify start and end points for a linear gradient that are inside the control (i.e. between 0 and 1), the start/stop colors are extended to the edge of the control based on the value of the SpreadMethod property.

You can also specify StartPoint and EndPoint positions that are outside the control, i.e. by using X/Y values that are less than 0.0 or greater than 1.0.

When you specify start/stop positions outside of a control, the control will be filled with that portion of the larger gradient that corresponds to the control’s position within the gradient.

Here’s an example with a red-blue fill that starts and finishes outside of the control.  Notice that the control is filled with a subset of the full red-blue gradient, so ends up being shades of magenta.

		<Rectangle Width="200" Height="100" Margin="30">
			<Rectangle.Fill>
				<LinearGradientBrush StartPoint="-1,-1" EndPoint="1.5,1.5">
				    <GradientStop Color="Red" Offset="0.0"/>
				    <GradientStop Color="Blue" Offset="1.0"/>
				</LinearGradientBrush>
			</Rectangle.Fill>
		</Rectangle>

#227 – You Can Specify Gradient Fills in Absolute Coordinates

When you specify a gradient fill’s starting position in 2D space, you typically use coordinates that are normalized to the size of the control, ranging from 0.0 to 1.0.

With the MappingMode property, you can instead specify the gradient’s start and stop points in absolute (device-independent) units, rather than as a ratio of the control’s size.

In the example below, we set MappingMode to Absolute, and then specify the gradients start/stop in absolute units.  This has the effect of having the gradient reach its target colors the same number of pixels into each control.

	<Window.Resources>
		<LinearGradientBrush x:Key="HappyFill" MappingMode="Absolute" StartPoint="0,10" EndPoint="50,10">
		    <GradientStop Color="AliceBlue" Offset="0.0"/>
		    <GradientStop Color="Chocolate" Offset="1.0"/>
		</LinearGradientBrush>
	</Window.Resources>

	<StackPanel>
		<Rectangle Height="100" Width="100" Fill="{StaticResource HappyFill}" Margin="30"/>
		<Button Content="!" Width="25" Background="{StaticResource HappyFill}" Height="25"/>
		<Label Content="We few, we happy few, we band of brothers.  --Hal V"
			HorizontalAlignment="Center" Margin="15"
			Background="{StaticResource HappyFill}"/>
	</StackPanel>


Note that MappingMode does not apply to the gradient stop offsets.

#226 – Gradient Fills Adjust to the Size of the Control

Because the start/stop points and gradient stops within a gradient fill are normalized to a 0.0-1.0 range, the same gradient can be used to fill controls of different sizes.  The fill changes color proportionally, relative to the size of the control, rather than at specific pixel positions.

In the example below, we define a linear gradient brush and then apply it to several controls of different sizes.

	<Window.Resources>
		<LinearGradientBrush x:Key="HappyFill" StartPoint="0,0.5" EndPoint="1,0.5">
		    <GradientStop Color="AliceBlue" Offset="0.0"/>
		    <GradientStop Color="Chocolate" Offset="1.0"/>
		</LinearGradientBrush>
	</Window.Resources>

	<StackPanel>
		<Rectangle Height="100" Width="200" Fill="{StaticResource HappyFill}" Margin="30"/>
		<Button Content="!" Width="25" Background="{StaticResource HappyFill}" Height="25"/>
		<Label Content="We few, we happy few, we band of brothers.  --Hal V"
			HorizontalAlignment="Center" Margin="15"
			Background="{StaticResource HappyFill}"/>
	</StackPanel>

#225 – Using a Brush that Will Update When a System Color Changes

The System.Windows.SystemColors class contains a series of static properties that expose the current predefined system colors.  (Click here to see a list of these system colors).

If you use one of these predefined system colors as a brush using the x:Static markup extension, the color used will be the value of that system color at the time that the application started.  If the system color changes while the application is running, the brush will not change.

You can create and use a dynamic brush, which will change whenever the system color changes, as follows:

	<StackPanel>
		<Ellipse Height="100" Width="200" Fill="{DynamicResource {x:Static SystemColors.HighlightBrushKey}}" Margin="30"/>
	</StackPanel>

Notice that we use the XxxBrushKey property, rather than the XxxBrush property.  Both properties give us a brush, but the XxxBrushKey property gives us a dynamic brush that will change when the system color changes.

#224 – Using Predefined System Colors in XAML

The System.Windows.SystemColors class contains a series of static properties that expose the current predefined system colors.  (Click here to see a list of these system colors).

You can use the x:Static markup extension in XAML to reference one of the predefined static properties in the SystemColors class.  Here we use a property that represents a predefined SolidColorBrush.

	<StackPanel>
		<Ellipse Height="100" Width="200" Fill="{x:Static SystemColors.HighlightBrush}" Margin="30"/>
	</StackPanel>

#223 – Predefined System Colors

The System.Windows.SystemColors class contains a series of static properties that expose the current predefined system colors.  These are the predefined system colors that you can change from the Control Panel (Control Panel\Appearance and Personalization\Personalization\Window Color and Appearance in Windows 7).

The properties come in triplets.  For each system color Xxx, there are XxxBrush, XxxBrushKey and XxxColor properties.

Here’s a chart showing the default colors, under the Aero theme in Windows 7.  (Click on the image to see it full-sized).

#222 – Example: Changing a Color Using RGB Sliders

Here’s an example of changing a color at run-time, using three sliders.

The XAML defines a control and three sliders:

	<Window.Resources>
		<SolidColorBrush x:Key="magicBrush" Color="Red"/>
	</Window.Resources>

	<StackPanel>
		<Ellipse Height="100" Width="200" Fill="{StaticResource magicBrush}" Margin="30"/>

		<StackPanel Orientation="Horizontal" HorizontalAlignment="Center">
			<Label Content="Red" VerticalAlignment="Center"/>
        	<Slider Name="sliR" Width="150" Margin="10" Minimum="0" Maximum="255" TickFrequency="1" IsSnapToTickEnabled="True"
				Value="255"
				ValueChanged="sli_ValueChanged"/>
			<Label Content="{Binding ElementName=sliR, Path=Value}" VerticalAlignment="Center"/>
		</StackPanel>
		<StackPanel Orientation="Horizontal" HorizontalAlignment="Center">
			<Label Content="Green" VerticalAlignment="Center"/>
        	<Slider Name="sliG" Width="150" Margin="10" Minimum="0" Maximum="255" TickFrequency="1" IsSnapToTickEnabled="True"
				ValueChanged="sli_ValueChanged"/>
			<Label Content="{Binding ElementName=sliG, Path=Value}" VerticalAlignment="Center"/>
		</StackPanel>
		<StackPanel Orientation="Horizontal" HorizontalAlignment="Center">
			<Label Content="Blue" VerticalAlignment="Center"/>
        	<Slider Name="sliB" Width="150" Margin="10" Minimum="0" Maximum="255" TickFrequency="1" IsSnapToTickEnabled="True"
				ValueChanged="sli_ValueChanged"/>
			<Label Content="{Binding ElementName=sliB, Path=Value}" VerticalAlignment="Center"/>
		</StackPanel>
	</StackPanel>

In code, we have a single method that executes when the sliders change, which changes the brush color:

        private void sli_ValueChanged(object sender, System.Windows.RoutedPropertyChangedEventArgs<double> e)
        {
			SolidColorBrush magicBrush = (SolidColorBrush)this.Resources["magicBrush"];

            if ((sliR != null) && (sliG != null) && (sliB != null))
            {
                magicBrush.Color = Color.FromRgb((byte)sliR.Value, (byte)sliG.Value, (byte)sliB.Value);
            }
        }