Click here to Skip to main content
15,879,474 members
Articles / Desktop Programming / WPF

Advanced Text Formatting in WPF

Rate me:
Please Sign up or sign in to vote.
4.61/5 (15 votes)
13 Apr 2013CPOL2 min read 55.2K   35   16
This article describes advanced text formatting in WPF.

Image 1

Introduction

WPF provides several controls which allow working with text. Some of these controls are Label, TextBlock, TextBox, RichTextBox etc. But all these controls provide limited text formatting capabilities. There is yet another control provided by WPF, called FormattedText. This control provides extensive text formatting capabilities. In particular, the FormattedText control can be used in situations where you want to display large sized text as banners. Also it can be used to play animation and videos on text. 

Background

A FormattedText object is created by passing the following parameters to its constructor:

  • string
  • CultureInfo
  • FlowDirection
  • TypeFace
  • Brush
C#
FormattedText text = new FormattedText("HELLO", 
   System.Globalization.CultureInfo.GetCultureInfo("en-US"), 
   FlowDirection.LeftToRight, new Typeface("LilyUPC"), 256, Brushes.Black);

The MaxTextWidth and MaxTextHeight methods are used to specify the maximum width and height respectively.

The SetForegroundBrush method is used to format the text using a brush.

Typically we override the OnRender method of the Window to perform the formatting and display the formatted text. The OnRender method of the Window class receives a DrawingContext object as parameter. The DrawText method of the DrawingContext object is used to draw the text on the Window.

Note: The Background attribute of the Window element must be "Transparent" for the OnRender method to work.

Using the code

The following code can be used to display text formatted using a LinearGradientBrush.

C#
protected override void OnRender(DrawingContext drawingContext)
{
    FormattedText text = new FormattedText("AZIM", 
      System.Globalization.CultureInfo.GetCultureInfo("en-US"), 
      FlowDirection.LeftToRight, new Typeface("LilyUPC"), 256, Brushes.Black);
    text.MaxTextWidth = 700;
    text.MaxTextHeight = 400;
    LinearGradientBrush brush = new LinearGradientBrush();	// Create a LinearGradientBrush
    // Set GradientStops
    brush.GradientStops.Add(new GradientStop(Colors.Red, 0.2));
    brush.GradientStops.Add(new GradientStop(Colors.Green, 0.3));
    brush.GradientStops.Add(new GradientStop(Colors.Blue, 0.5));
    brush.GradientStops.Add(new GradientStop(Colors.Magenta, 0.7));
    brush.GradientStops.Add(new GradientStop(Colors.Yellow, 0.8));
    brush.GradientStops.Add(new GradientStop(Colors.Cyan, 0.9));
    text.SetForegroundBrush(brush, 0, 4);			// Apply formatting to 4 chars starting from first char
    drawingContext.DrawText(text, new Point(10, 0));		// Draw text on the Window
    base.OnRender(drawingContext);
}

The above code creates a FormattedText object. Then it creates a LinearGradientBrush and uses the SetForegroundBrush method of the FormattedText object to format the text. The SetForegroundBrush method takes three parameters. The first parameter is the brush, the second parameter is the index of the starting character, and the third parameter is the number of characters to be formatted. Finally it uses the DrawText method of the DrawingContext object to draw the text on the Window.

The output of the above code is as follows:

Image 2

Animation can be applied to a LinearGradientBrush and used as a background for the text. The XAML code for this is as follows:

XML
<Window x:Class="TextEffects.MainWindow"
        xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
        xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
        Background="Transparent"
        Title="Animated Text" Height="350" Width="525">
    <Canvas>
        <Rectangle Name="myrect" Width="350" Height="250">
            <Rectangle.Fill>
                <LinearGradientBrush x:Name="brush" StartPoint="0,0" EndPoint="1,1">
                    <GradientStop x:Name="stop1" Offset="0" Color="Red"/>
                    <GradientStop x:Name="stop2" Offset="0.5" Color="Green"/>
                    <GradientStop x:Name="stop3" Offset="1" Color="Blue"/>
                </LinearGradientBrush>
            </Rectangle.Fill>
            <Rectangle.Triggers>
                <EventTrigger RoutedEvent="Window.Loaded">
                    <BeginStoryboard>
                        <Storyboard RepeatBehavior="Forever">
                            <ColorAnimation Storyboard.TargetName="stop1" 
                               Storyboard.TargetProperty="Color" From="Red" 
                               To="Green" Duration="0:0:1" BeginTime="0:0:0"/>
                            <ColorAnimation Storyboard.TargetName="stop1" 
                               Storyboard.TargetProperty="Color" From="Green" 
                               To="Blue" Duration="0:0:1" BeginTime="0:0:0.5"/>
                            <ColorAnimation Storyboard.TargetName="stop1" 
                               Storyboard.TargetProperty="Color" From="Blue" 
                               To="Red" Duration="0:0:1" BeginTime="0:0:1"/>
                            <ColorAnimation Storyboard.TargetName="stop2" 
                               Storyboard.TargetProperty="Color" From="Green" 
                               To="Blue" Duration="0:0:1" BeginTime="0:0:0"/>
                            <ColorAnimation Storyboard.TargetName="stop2" 
                               Storyboard.TargetProperty="Color" From="Blue" 
                               To="Red" Duration="0:0:1" BeginTime="0:0:0.5"/>
                            <ColorAnimation Storyboard.TargetName="stop2" 
                               Storyboard.TargetProperty="Color" From="Red" 
                               To="Green" Duration="0:0:1" BeginTime="0:0:1"/>
                            <ColorAnimation Storyboard.TargetName="stop3" 
                               Storyboard.TargetProperty="Color" From="Blue" 
                               To="Red" Duration="0:0:1" BeginTime="0:0:0"/>
                            <ColorAnimation Storyboard.TargetName="stop3" 
                               Storyboard.TargetProperty="Color" From="Red" 
                               To="Green" Duration="0:0:1" BeginTime="0:0:0.5"/>
                            <ColorAnimation Storyboard.TargetName="stop3" 
                               Storyboard.TargetProperty="Color" From="Green" 
                               To="Blue" Duration="0:0:1" BeginTime="0:0:1"/>
                        </Storyboard>
                    </BeginStoryboard>
                </EventTrigger>
            </Rectangle.Triggers>
        </Rectangle>
    </Canvas>
</Window>

The code-behind code is as follows:

C#
public partial class MainWindow : Window
{
    public MainWindow()
    {
        InitializeComponent();
    }
    protected override void OnRender(DrawingContext drawingContext)
    {
        FormattedText text = new FormattedText
           ("AZIM", System.Globalization.CultureInfo.GetCultureInfo("en-US"), 
            FlowDirection.LeftToRight, new Typeface("LilyUPC"), 256, Brushes.Black);
        text.MaxTextWidth = 700;
        text.MaxTextHeight = 400;
        text.SetForegroundBrush(brush, 0, 4);
        drawingContext.DrawText(text, new Point(10, 0));
        myrect.Visibility = Visibility.Hidden;			// Hide the Rectangle
        base.OnRender(drawingContext);
    }
}

This produces the following output:

Image 3

An image can be used as a background for the text. The following code can be used for the same:

C#
protected override void OnRender(DrawingContext drawingContext)
{
    FormattedText text = new FormattedText("AZIM", 
      System.Globalization.CultureInfo.GetCultureInfo("en-US"), 
      FlowDirection.LeftToRight, new Typeface("LilyUPC"), 256, Brushes.Black);
    text.MaxTextWidth = 700;
    text.MaxTextHeight = 400;
    ImageBrush brush = new ImageBrush();			// Create an ImageBrush
    brush.ImageSource = new BitmapImage(new Uri("flower.jpg", UriKind.Relative));	// Set the image source
    text.SetForegroundBrush(brush, 0, 4);
    drawingContext.DrawText(text, new Point(10, 200));
    base.OnRender(drawingContext);
}

Its output is as follows:

Image 4

If you want to play video on the surface of the text, you can use a VideoDrawing object in combination with a DrawingBrush object as follows:

C#
protected override void OnRender(DrawingContext drawingContext)
{
    FormattedText text =
       new FormattedText("AZIM", System.Globalization.CultureInfo.GetCultureInfo("en-US"), 
       FlowDirection.LeftToRight, new Typeface("LilyUPC"), 256, Brushes.Black);
    text.MaxTextWidth = 700;
    text.MaxTextHeight = 400;
    MediaTimeline timeline = new MediaTimeline(new Uri("airplane.mpg", UriKind.Relative));	// Create MediaTimeLine
    timeline.RepeatBehavior = RepeatBehavior.Forever;
    MediaClock clock = timeline.CreateClock();
    MediaPlayer player = new MediaPlayer();
    player.Clock = clock;
    VideoDrawing drawing = new VideoDrawing();	// Create VideoDrawing
    drawing.Rect = new Rect(0, 0, 300, 200);
    drawing.Player = player;			// Set player
    DrawingBrush brush = new DrawingBrush(drawing);	// Create DrawingBrush based on the VideoDrawing
    text.SetForegroundBrush(brush, 0, 4);
    drawingContext.DrawText(text, new Point(100, 0));
}

The output of this code is as follows:

Image 5

A FormattedText object can be converted to a PathGeometry object. In the following example an ellipse is created which follows the path of the text.

Following is the XAML code:

XML
<Window x:Class="TextEffects.MainWindow"
        xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
        xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
        Background="Transparent"
        Title="PathGeometry Animation" Height="350" Width="525">
    <Canvas>
        <Ellipse Canvas.Top="0" Canvas.Left="0" Width="50" Height="50">
            <Ellipse.Fill>
                <RadialGradientBrush GradientOrigin="0.5,0.5" 
                          Center="0.5,0.5" RadiusX="0.5" RadiusY="0.5">
                    <RadialGradientBrush.GradientStops>
                        <GradientStop Color="Transparent" Offset="0.25" />
                        <GradientStop Color="Red" Offset="1" />
                    </RadialGradientBrush.GradientStops>
                </RadialGradientBrush>
            </Ellipse.Fill>
            <Ellipse.RenderTransform>
                <MatrixTransform />
            </Ellipse.RenderTransform>
            <Ellipse.Triggers>
                <EventTrigger RoutedEvent="FrameworkElement.Loaded">
                    <EventTrigger.Actions>
                        <BeginStoryboard>
                            <Storyboard x:Name="storyboard">
                                <MatrixAnimationUsingPath x:Name="matrixAnimation" 
                                   Duration="0:00:40" RepeatBehavior="Forever" 
                                   Storyboard.TargetProperty="RenderTransform.Matrix" />
                            </Storyboard>
                        </BeginStoryboard>
                    </EventTrigger.Actions>
                </EventTrigger>
            </Ellipse.Triggers>
        </Ellipse>
    </Canvas>
</Window>

Following is the code-behind code:

C#
protected override void OnRender(DrawingContext drawingContext)
{
    FormattedText text =
      new FormattedText("AZIM", 
        System.Globalization.CultureInfo.GetCultureInfo("en-US"), 
        FlowDirection.LeftToRight, new Typeface("LilyUPC"), 256, Brushes.Black);
    text.MaxTextWidth = 700;
    text.MaxTextHeight = 400;
    ImageBrush brush = new ImageBrush();
    brush.ImageSource = new BitmapImage(new Uri("flower.jpg", UriKind.Relative));
    text.SetForegroundBrush(brush, 0, 4);
    drawingContext.DrawText(text, new Point(10, 0));
    Geometry geometry = text.BuildGeometry(new System.Windows.Point(-20, -20));	// Convert text to Geometry object
    PathGeometry pathGeometry = geometry.GetFlattenedPathGeometry();		// Create a PathGeometry based on the Geometry object
    matrixAnimation.PathGeometry = pathGeometry;				// Set PathGeometry for the animation
    base.OnRender(drawingContext);
}

Following is the output of the above code:

Image 6

Points of Interest

I hope readers find the above discussion interesting and useful.

License

This article, along with any associated source code and files, is licensed under The Code Project Open License (CPOL)


Written By
Instructor / Trainer NIIT, India
India India
I am a trainer by profession. Currently I am working with iFuture Technologies(India) as a Senior Faculty. I enjoy programming as a hobby. During my career I have seen the growth and decline of many technologies, many of them being my favorites like Flash, WPF, Windows Mobile Development. Few of my current favorites are Android, Xamarin and Python, though I also like traditional and evergreen languages like PHP, C#, Visual Basic and Java.

Apart from computers, my favorite pastime is bicycling.

Comments and Discussions

 
QuestionQuestion Pin
stimandrew16-Apr-20 8:16
stimandrew16-Apr-20 8:16 
GeneralMy vote of 5 Pin
DrABELL13-Apr-13 18:46
DrABELL13-Apr-13 18:46 
GeneralRe: My vote of 5 Pin
Azim Zahir14-Apr-13 21:14
Azim Zahir14-Apr-13 21:14 
GeneralRe: My vote of 5 Pin
DrABELL15-Apr-13 1:11
DrABELL15-Apr-13 1:11 
QuestionGreat job Azim Pin
liftup14-Jan-13 2:49
liftup14-Jan-13 2:49 
AnswerRe: Great job Azim Pin
Azim Zahir15-Jan-13 22:33
Azim Zahir15-Jan-13 22:33 
GeneralRe: Great job Azim Pin
liftup16-Jan-13 6:02
liftup16-Jan-13 6:02 
GeneralRe: Great job Azim Pin
Azim Zahir18-Jan-13 19:15
Azim Zahir18-Jan-13 19:15 
QuestionNice! Pin
Your Display Name Here1-Aug-12 23:55
Your Display Name Here1-Aug-12 23:55 
AnswerRe: Nice! Pin
Azim Zahir2-Aug-12 7:03
Azim Zahir2-Aug-12 7:03 
GeneralLocal copies of images Pin
Smitha Nishant1-Aug-12 11:07
protectorSmitha Nishant1-Aug-12 11:07 
GeneralRe: Local copies of images Pin
Azim Zahir1-Aug-12 16:27
Azim Zahir1-Aug-12 16:27 
GeneralMy vote of 5 Pin
Carsten V2.023-Jul-12 7:05
Carsten V2.023-Jul-12 7:05 
GeneralRe: My vote of 5 Pin
Azim Zahir23-Jul-12 15:47
Azim Zahir23-Jul-12 15:47 
GeneralMy vote of 5 Pin
Madhan Mohan Reddy P22-Jul-12 18:10
professionalMadhan Mohan Reddy P22-Jul-12 18:10 
GeneralRe: My vote of 5 Pin
Azim Zahir22-Jul-12 18:29
Azim Zahir22-Jul-12 18:29 

General General    News News    Suggestion Suggestion    Question Question    Bug Bug    Answer Answer    Joke Joke    Praise Praise    Rant Rant    Admin Admin   

Use Ctrl+Left/Right to switch messages, Ctrl+Up/Down to switch threads, Ctrl+Shift+Left/Right to switch pages.