Use XAML and code behind to draw and outline shapes with gradients in WPF and C#

[gradients]

My unofficial slogan for WPF is, “Twice and flexible and only five times as hard,” and that applies to working with gradients. Gradients are undoubted cool and very flexible, but they are verbose and a bit tricky to use.

How you create and use a gradient is structurally similar whether you do it in XAML code or C# code behind, although the syntactic details are naturally very different.

XMAL Code

The following code shows how the program’s XAML code creates its green ellipse.

<Canvas Name="canDrawing">
    <Ellipse Canvas.Top="20" Canvas.Left="20" Width="240" Height="100"
        StrokeThickness="15">
        <Ellipse.Fill>
            <LinearGradientBrush StartPoint="0,0" EndPoint="0,1">
                <GradientStop Color="DarkGreen" Offset="0.0"/>
                <GradientStop Color="LightGreen" Offset="1.0"/>
            </LinearGradientBrush>
        </Ellipse.Fill>
        <Ellipse.Stroke>
            <LinearGradientBrush StartPoint="0,0" EndPoint="0,1">
                <GradientStop Color="LightGreen" Offset="0.0"/>
                <GradientStop Color="DarkGreen" Offset="1.0"/>
            </LinearGradientBrush>
        </Ellipse.Stroke>
    </Ellipse>
</Canvas>

This code first creates a Canvas object. Inside that object, it creates an Ellipse. It sets the Canvas.Top and Canvas.Left attached properties. Those properties are provided by the Canvas class not the Ellipse class, so they require the somewhat unusual syntax shown here.

The Ellipse tag also defines the ellipse’s Width, Height, and StrokeThickness properties. Those are properties of the Ellipse class, so their syntax is more normal.

Next, the Ellipse element includes an Ellipse.Fill sub-element. If you wanted to use a solid color, you could include it directly in the Ellipse tag as in Fill="Red". Because we want to use a more complicated fill, in this case a gradient, we need to use this nested Ellipse.Fill element.

Inside the Ellipse.Fill element, we place the object that we use to to fill the shape, in this case, a LinearGradientBrush. The colors blend from the StartPoint to the EndPoint. Those properties are defined on a 0.0 – 1.0 scale, so (0.0, 0.0) is the object’s upper left corner and (0.0, 1.0) is the lower left corner. That makes the gradient in this example shade vertically from the ellipse’s top to its bottom.

The gradient object contains a gradient stop collection. In XAML code, you simply place GradientStop objects inside the brush. If you were making a RadialGradientBrush, you would do the same thing. Each GradientStop represents a color along the gradient. The Color property indicates the color at that point in the gradient. (Duh!) The Offset property determines where in the gradient that color belongs on a 0.0 to 1.0 scale. The Offset 0.0 means the color belongs at the beginning of the gradient. The value 1.0 means the color belonsg at the end of the gradient. You can use as many GradientStop objects as you like to make the gradient fade through multiple colors.

After it finishes defining the ellipse’s fill gradient, the program uses similar code to define the ellipse’s stroke (outline) gradient.

C# Code Behind

When the program starts, the following Window_Loaded event handler executes. When you read the code, you should be able to find the pieces that corresponding to the previous XAML code.

// Draw an ellipse with a LinearGradient fill and outline.
private void Window_Loaded(object sender, RoutedEventArgs e)
{
    // Make the ellipse and add it to the Canvas.
    Ellipse ellipse = new Ellipse();
    canDrawing.Children.Add(ellipse);

    // Set the size and position.
    ellipse.Width = 240;
    ellipse.Height = 100;
    Canvas.SetTop(ellipse, 140);
    Canvas.SetLeft(ellipse, 20);

    // Define the fill.
    GradientStop[] fill_stops =
    {
        new GradientStop(Colors.Brown, 0.0),
        new GradientStop(Colors.Yellow, 1.0),
    };
    GradientStopCollection fill_stop_collection =
        new GradientStopCollection(fill_stops);
    LinearGradientBrush fill_brush =
        new LinearGradientBrush(fill_stop_collection,
            new Point(0.0, 0.0), new Point(0.0, 1.0));
    ellipse.Fill = fill_brush;

    // Define the outline.
    ellipse.StrokeThickness = 15;
    GradientStop[] outline_stops =
    {
        new GradientStop(Colors.Yellow, 0.0),
        new GradientStop(Colors.Brown, 1.0),
    };
    GradientStopCollection outline_stop_collection =
        new GradientStopCollection(outline_stops);
    LinearGradientBrush outline_brush =
        new LinearGradientBrush(outline_stop_collection,
            new Point(0.0, 0.0), new Point(0.0, 1.0));
    ellipse.Stroke = outline_brush;
}

The code first creates an Ellipse object and adds it to the canDrawing Canvas control’s Children collection. (Tip: One common mistake is to forget to add a new control to an existing control’s Children collection. If you forget, then the new control does not appear.)

Next, the code sets the ellipse’s Width and Height properties as you would expect. The Canvas.Top and Canvas.Left properties are attached properties defined by the Canvas class, so the program must use that class’s SetTop and SetLeft methods to set them. (Contrast this with the way the XAML code does this.)

The code then defines the fill gradient. It first creates an array of GradientStop objects to define the fill’s colors. The GradientStop constructors takes parameters that give each stop’s color and offset value.

The code uses the array of gradient stops to define a GradientStopCollection. It then uses that collection to define the LinearGradientBrush. Finally, the code sets the ellipse’s Fill property to the brush.

The code repeats roughly the same steps to define the ellipse’s stroke.

Summary

The XAML and C# code perform roughly the same steps, although the C# code seems a bit backward. The XAML code uses nested elements to define objects and their properties. In contrast, the C# code defines nested elements before it defines the objects that contain them. You can change that a bit if you like. For example you can create a GradientStopCollection and then add gradient stops to it afterward.

Download the example to see additional details and to experiment with the program.


Download Example   Follow me on Twitter   RSS feed   Donate




About RodStephens

Rod Stephens is a software consultant and author who has written more than 30 books and 250 magazine articles covering C#, Visual Basic, Visual Basic for Applications, Delphi, and Java.
This entry was posted in drawing, graphics, wpf and tagged , , , , , , , , , , , . Bookmark the permalink.

Leave a Reply

Your email address will not be published. Required fields are marked *

This site uses Akismet to reduce spam. Learn how your comment data is processed.