Simple way to create a gradient brush in Xaml

 

Rob, Nikhil & I were talking today about the early days of Xaml when you could create linear gradient brushes as an attribute value. E.g. (borrowing from Rob’s post on this subject) instead of creating a LinearGradientBrush for the fill of a rectangle with this Xaml:

<Rectangle Width="300" Height="200" >

    <Rectangle.Fill>

        <LinearGradientBrush>

            <GradientStop Color="Blue" Offset="0" />

            <GradientStop Color="White" Offset="1" />

        </LinearGradientBrush>

    </Rectangle.Fill>

</Rectangle>

… you could write:

<Rectangle Width="300" Height="200"

          Fill="HorizontalGradient Blue White" >

</Rectangle>

The up side of the latter syntax obviously is that it’s terser. The drawback is that it’s difficult for a tool to understand it; it has no idea what “HorizontalGradient” means, nor that it has two arguments (nor how those arguments should be interpreted).

For that reason, we created the markup extension primitive for Xaml. A markup extension is any type that derives from MarkupExtension, and it can be specified in an attribute using “squiggle” syntax. The most common markup extension you see in Xaml is the binding extension, e.g.:

<TextBlock Text="{Binding FirstName}" />

So we caution against “mini languages” in Xaml, such as the HorizontalGradient example above, because of the lack of toolability and compile-time validation, and we recommend markup extensions instead.

And so, here’s an example of a definition for such an extension, in this case to create a LinearGradientBrush:

[MarkupExtensionReturnType(typeof(LinearGradientBrush))]

public class LinearGradientBrushExtension : MarkupExtension

{

    public LinearGradientBrushExtension()

    {

        StartColor = EndColor = Colors.White;

    }

    public LinearGradientBrushExtension( Color startColor, Color endColor, double angle)

    {

        StartColor = startColor;

        EndColor = endColor;

        Angle = angle;

    }

    public Color StartColor { get; set; }

    public Color EndColor { get; set; }

    public double Angle { get; set; }

    public override object ProvideValue(IServiceProvider serviceProvider)

    {

        return new LinearGradientBrush(StartColor, EndColor, Angle);

    }

}

… which can be used in Xaml like this:

<Rectangle Width="300" Height="200"

          Fill="{helper:LinearGradientBrush StartColor=red, EndColor=blue, Angle=0}" />

… or, since the LinearGradientBrushExtension has a convenience constructor for setting the properties, the Xaml can simply be:

<Rectangle Width="300" Height="200"

          Fill="{helper:LinearGradientBrush red, blue, 0}" />

Similarly, here’s a markup extension for a RadialGradientBrush:

[MarkupExtensionReturnType(typeof(RadialGradientBrush))]

public class RadialGradientBrushExtension : MarkupExtension

{

    public RadialGradientBrushExtension()

    {

        StartColor = EndColor = Colors.White;

    }

    public RadialGradientBrushExtension( Color startColor, Color endColor )

    {

        StartColor = startColor;

        EndColor = endColor;

    }

    public Color StartColor { get; set; }

    public Color EndColor { get; set; }

    public override object ProvideValue(IServiceProvider serviceProvider)

    {

        return new RadialGradientBrush(StartColor, EndColor);

    }

}

… which enables this Xaml:

<Rectangle Width="300" Height="200"

          Fill="{local:RadialGradientBrush red, blue}" />