The TextRenderer

The TextRenderer

The TextRenderer class, located in the System.Windows.Forms namespace, is an alternative to the Graphics class for text rendering. Although Graphics and TextRenderer provide similar levels of text-rendering capabilities, the key difference between the two technologies is the underlying rendering API each one encapsulates; the Graphics class uses GDI+, and TextRenderer wraps GDI directly. Thus, when you need to render text using GDI, you can use TextRenderer to save the effort of writing interop code.

TextRenderer provides two methodsDrawText and MeasureTexteach of which has plenty of overloads that almost parallel their Graphics DrawString and MeasureString counterparts:

namespace System.Windows.Forms {
  sealed class TextRenderer {
    // Methods
    public static void DrawText(IDeviceContext dc, ...);
    public static Size MeasureText(IDeviceContext dc, ...}
    public static Size MeasureText(string text, ...);

Both DrawText and MeasureText, the equivalents of Graphics.DrawString and Graphics.MeasureString, respectively, wrap GDI invocations outside the graphics scope that is native to your applications via GDI+, whose main element is the surface you're drawing to via the Graphics object.[8] But GDI doesn't know anything about that drawing surface, so you need to pass it a device context handle that's wrapped by an IDeviceContext reference to the surface you want to draw text on. This is why several overloads of both DrawText and MeasureText accept an IDeviceContext. Fortunately, the Graphics class implements IDeviceContext, so you can simply pass it to either method.

[8] Although they are conceptually equivalent, it is highly recommended that you don't mix calls between Graphics and TextRenderer DrawXxx and MeasureXxx methods.

There is another set of MeasureText overloads that don't require an IDeviceContext. However, you should prefer those that do because it allows MeasureText to more accurately determine the size needed to display a chunk of text.

Here's how to determine how much space a chunk of text actually needs:

void TextRendererForm_Paint(object sender, PaintEventArgs e) {
   Graphics g = e.Graphics;
   Size proposedSize = this.ClientRectangle.Size;

   // Calculate rendered text size
   Size size = TextRenderer.MeasureText(
                 g, "Text To Measure", this.Font, proposedSize);

As you can see, calling the MeasureText method is quite similar to calling Graphics. MeasureString. In addition to support for passing an IDeviceContext object reference, the other important difference between MeasureText and MeasureString is that the former returns a Size object, and the latter returns SizeF; the entire TextRenderer implementation works with integers only.

To render a string with DrawText is almost the same as using Graphics.DrawString, apart from passing an IDeviceContext reference:

void TextRendererForm_Paint(object sender, PaintEventArgs e) {
   Graphics g = e.Graphics; // IDeviceContext
   Size proposedSize = this.ClientRectangle.Size;

   // Calculate rendered text size
   Size size =
       g, "Text To Measure", this.Font, proposedSize);

  // Render text to calculated size
  Rectangle rect = new Rectangle(0, 0, size.Width, size.Height);
    g, "Text To Measure", this.Font, rect, Color.Black);

Figure illustrates the result.

11. Measuring and Drawing Text with TextRenderer

Of course, this is pretty plain, particularly from the point of view of formatting. Therein lies another consistency with Graphics: the ability to pass special formatting details to both MeasureText and DrawText.

Formatting with TextRenderer

As with Graphics.DrawString, overloads of both the TextRenderer.MeasureText method and the TextRenderer.DrawText method allow you to pass in a special formatting-oriented argument of type TextFormattingFlags:

namespace System.Windows.Forms {
   enum TextFormatFlags {
     // Default (Top, Left, and GlyphOverhangPadding)
     Default = 0,
     // Align text to top
     Top = 0,
     // Align text to left
     Left = 0,
     // Use glyph overhang in text line height
     GlyphOverhangPadding = 0,
     // Align text to horizontal center of the rectangle
     HorizontalCenter = 1,
     // Align text to right
     Right = 2,
     // Align text to vertical center of rectangle
     VerticalCenter = 4,
     // Align text to bottom
     Bottom = 8,
     // Word wrapping
     WordBreak = 16,
     // Render all text to a single line
     SingleLine = 32,
     // "\t" characters in text are turned into tabs
     ExpandTabs = 64,
     // Don't clip text partially outside layout rect
     NoClipping = 256,
     // Use external leading in text line height
     ExternalLeading = 512,
     // Don't show underscores at all
     NoPrefix = 2048,
     // Calculate text metrics using system font
     Internal = 4096,
     // Render text as if rendered to a TextBox
     TextBoxControl = 8192,
     // Trim file path by putting ellipsis in the middle
     PathEllipsis = 16384,
     // Trim to nearest character and show ellipsis
     EndEllipsis = 32768,
     // Copy the displayed string to source string
     ModifyString = 65536,
     // Render text in right-to-left order
     RightToLeft = 131072,
     // Trim to nearest word and show ellipsis
     WordEllipsis = 262144,
     // Don't line break wide chars
     NoFullWidthCharacterBreak = 524288,
     // Hide "&" chars intended as underscores
     HidePrefix = 1048576,
     // Only draw underscores in place of "&"
     PrefixOnly = 2097152,
     // Use Graphics object clipping
     PreserveGraphicsClipping = 16777216,
     // Use Graphics object transformations
     PreserveGraphicsTranslateTransform = 33554432,
     // Don't pad text
     NoPadding = 268435456,
     // Pad text left and right edges
     LeftAndRightPadding = 536870912,

Using TextFormatFlags, it's easy to center-align a chunk of text that collapses lines of text on a word-by-word basis, replacing hidden text with ellipsis characters:

void TextRendererForm_Paint(object sender, PaintEventArgs e) {
   Graphics g = e.Graphics;
   Rectangle rect = this.ClientRectangle;
   TextFormatFlags flags = TextFormatFlags.HorizontalCenter|
     g, "Text To Measure", this.Font, rect, Color.Black, flags);

The output of this code is shown in Figure

12. Measuring and Drawing Centered Text with TextRenderer

In general, you will find a lot of crossover between the two text-rendering technologies, particularly from the formatting perspective. Furthermore, knowledge you gain using one technology will serve you well with the other. However, there are also differences between the two technologies that you need to be aware of.

 Python   SQL   Java   php   Perl 
 game development   web development   internet   *nix   graphics   hardware 
 telecommunications   C++ 
 Flash   Active Directory   Windows