Tag Archives: How to implement Text Stroke

WPF: How to implement Text Stroke

1. Preface

WPF’s TextBlock provides most of the commonly used text modification methods, which are basically sufficient in daily use. If richer representations are required, WPF also provides other tools that are more complex to use to achieve these requirements. For example, the text stroke described in this article, there are several ways to render it in WPF. This article will briefly introduce the method to achieve text stroke.

2. Convert text to Geometry

The key to achieve text stroke is to use FormattedText to convert the text to Geometry, and then use other techniques to add a border to the Geometry and then draw it.

In WPF, Geometry and its derived classes (EllipseGeometry, LineGeometry, PathGeometry, RectangleGeometry, etc.) are used to describe collection graphics of 2D shapes. The FormattedText BuildGeometryfunction can convert the text into a GeometryGroup (representing a composite geometry composed of other Geometry objects), the code is as follows:

private Geometry CreateTextGeometry()
{
    // Create the formatted text based on the properties set.
    FormattedText formattedText = new FormattedText(
        Text,
        CultureInfo.CurrentCulture,
        FlowDirection.LeftToRight,
        new Typeface(
            FontFamily,
            FontStyle,
            FontWeight,
            FontStretch),
        FontSize,
        System.Windows.Media.Brushes.Black,// This brush does not matter since we use the geometry of the text.
        100);
    // Build the geometry object that represents the text.

    return formattedText.BuildGeometry(new Point(0, 0));
}

After getting the Geometry, there are two ways to draw it.

3. Using DrawingContext

DrawingContext in WPF is a basic drawing object used to draw various graphics. One of the easiest ways to use it is to overload the OnRender method of UIElement, and draw the UI of UIElement in this method:

// Override the OnRender call to add a Background and Border to the OffSetPanel
protected override void OnRender(DrawingContext dc)
{
    SolidColorBrush mySolidColorBrush  = new SolidColorBrush();
    mySolidColorBrush.Color = Colors.LimeGreen;
    Pen myPen = new Pen(Brushes.Blue, 10);
    Rect myRect = new Rect(0, 0, 500, 500);
    dc.DrawRectangle(mySolidColorBrush, myPen, myRect);
}

The sample code above draws a 500 * 500 square using the DrawingContext. In addition to squares, DrawingContext also provides functions such as DrawEllipse, DrawImage, DrawLine, which are used to draw circles, images, lines, etc. You can also use DrawText function to draw text. However, DrawGeometry is a better choice than using DrawText directly, because it can draw the border of the text. In the above code, we have converted the text to a Geometry, and then directly call DrawGeometry and add a border:

protected override void OnRender(DrawingContext drawingContext)
{
    base.OnRender(drawingContext);
    var geometry = CreateTextGeometry();
    // Draw the outline based on the properties that are set.
    drawingContext.DrawGeometry(Foreground, new Pen(Stroke, StrokeThickness), geometry);
}

With Stroke, the StrokeThickness control can control the color and thickness of the text border.

4. Custom Shape

The method described above is from Microsoft’s sample document , but since we have obtained the geometry of the text, isn’t it better to make a custom Shape directly, and Shape can easily play more tricks and more animations. The code for making hollow text with a custom Shape is roughly as follows (some custom dependency properties for text are omitted):

public class TextShape : Shape
{
    private double _height;

    private double _width;

    private Geometry _textGeometry;

    [Localizability(LocalizationCategory.Text)]
    public string Text
    {
        get { return (string)GetValue(TextProperty); }
        set { SetValue(TextProperty, value); }
    }

    protected sealed override Geometry DefiningGeometry
    {
        get
        {
            return _textGeometry ?? Geometry.Empty;
        }
    }

    protected override Size MeasureOverride(Size availableSize)
    {
        this.RealizeGeometry();
        return new Size(Math.Min(availableSize.Width, _width), Math.Min(availableSize.Height, _height));
    }

    private void RealizeGeometry()
    {
        var formattedText = new FormattedText(
                                   Text,
                                   CultureInfo.CurrentCulture,
                                   FlowDirection.LeftToRight,
                                   new Typeface(FontFamily, FontStyle, FontWeight, FontStretch), FontSize, Brushes.Black, 100);

        _height = formattedText.Height;
        _width = formattedText.Width;
        _textGeometry = formattedText.BuildGeometry(new Point());
    }
}

After getting a Shape converted from text, in addition to implementing text strokes, you can also play a lot of animations, such as the following:

5. Finally

This article describes how to implement text strokes. In addition to text strokes, there are many ways to convert text into Shape introduced in the article, and the next article will simply try some of them.