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
FormattedText text = new FormattedText("HELLO",
System.Globalization.CultureInfo.GetCultureInfo("en-US"),
FlowDirection.LeftToRight, new Typeface("LilyUPC"), 256, Brushes.Black);Colourised in 4ms
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
.
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(); 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); drawingContext.DrawText(text, new Point(10, 0)); base.OnRender(drawingContext);
}Colourised in 18ms
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:
Animation can be applied to a LinearGradientBrush
and used as a background for the text. The XAML code for this is as follows:
<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>Colourised in 69ms
The code-behind code is as follows:
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; base.OnRender(drawingContext);
}
}Colourised in 8ms
This produces the following output:
An image can be used as a background for the text. The following code can be used for the same:
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, 200));
base.OnRender(drawingContext);
}Colourised in 6ms
Its output is as follows:
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:
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)); timeline.RepeatBehavior = RepeatBehavior.Forever;
MediaClock clock = timeline.CreateClock();
MediaPlayer player = new MediaPlayer();
player.Clock = clock;
VideoDrawing drawing = new VideoDrawing(); drawing.Rect = new Rect(0, 0, 300, 200);
drawing.Player = player; DrawingBrush brush = new DrawingBrush(drawing); text.SetForegroundBrush(brush, 0, 4);
drawingContext.DrawText(text, new Point(100, 0));
}Colourised in 17ms
The output of this code is as follows:
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:
<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>Colourised in 28ms
Following is the code-behind code:
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)); PathGeometry pathGeometry = geometry.GetFlattenedPathGeometry(); matrixAnimation.PathGeometry = pathGeometry; base.OnRender(drawingContext);
}Colourised in 16ms
Following is the output of the above code:
Points of Interest
I hope readers find the above discussion interesting and useful.