April 3, 2011, 1:49 a.m.
posted by mesa
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.
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 =
TextRenderer.MeasureText(
g, "Text To Measure", this.Font, proposedSize);
// Render text to calculated size
Rectangle rect = new Rectangle(0, 0, size.Width, size.Height);
TextRenderer.DrawText(
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 TextRendererAs 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 {
[Flags]
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|
TextFormatFlags.VerticalCenter|
TextFormatFlags.WordEllipsis;
TextRenderer.DrawText(
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. |
- Comment

