Some of the overloaded versions of the Graphics object’s DrawString method take additional parameters that help format the text. The first of these parameters is a layout rectangle. This is a RectangleF structure that indicates the area where the text should be drawn. The second parameter is a StringFormat object that determines how the text is formatted.
The following code draws text inside a rectangle. It begins by defining the layout rectangle, text, and font. It then creates a StringFormat object. It sets the object’s Alignment property to Center to position each line of text centered horizontally in the layout rectangle. It sets the object’s LineAlignment property to Near. That makes the text align vertically to the near vertical edge of the rectangle (its top). The program calls DrawString to draw the text and then uses DrawRectangle to display the layout rectangle.
Tip | The Alignment and LineAlignment properties both can take the values Near, Center, and Far. Using these values for both properties can make the properties somewhat confusing. On systems that draw right to left and top to bottom, the values are relative to a point in the upper-left corner of the text, so Near means left/top, Center means center/center, and Far means right/bottom. |
Private Sub Form1_Paint(ByVal sender As Object, _ ByVal e As System.Windows.Forms.PaintEventArgs) Handles MyBase.Paint Dim layout_rect As New RectangleF(10, 10, _ Me.ClientSize.Width - 20, Me.ClientSize.Height - 20) Dim txt As String = "The quick brown fox jumps over the lazy dog." Using big_font As New Font("Times New Roman", 30, FontStyle.Bold) Using string_format As New StringFormat string_format.Alignment = StringAlignment.Center string_format.LineAlignment = StringAlignment.Near e.Graphics.DrawString(txt, big_font, Brushes.Black, _ layout_rect, string_format) e.Graphics.DrawRectangle(Pens.Black, Rectangle.Round(layout_rect)) End Using ' string_format End Using ' big_font End Sub
Figure 22-3 shows the result. Notice that the text is centered horizontally and aligned to the top of the layout rectangle.
Figure 22-3: The DrawString method can use a layout rectangle and a StringFormat object to format text.
The StringFormat object provides several other properties and methods that you can use to position text. The following table describes the most useful of these.
Property or Method | Purpose |
---|---|
Alignment | Determines the text’s horizontal alignment. This can be Near (left), Center (middle), or Far (right). |
FormatFlags | Gets or sets flags that modify the StringFormat object’s behavior. See the section “FormatFlags” later in this chapter for more information. |
GetTabStops | Returns an array of Singles, giving the positions of tab stops. See the section “Tab Stops” later in this chapter for more information. |
HotkeyPrefix | Determines how the hotkey prefix character is displayed. (In a menu caption or label, the hotkey is underlined to show that pressing Alt-<hotkey> performs some special action. For example, in most applications Alt-F opens the File menu. A program specifies a control’s hotkey character by placing an ampersand in front of it. For example, “&File” is displayed as “File.”) If HotkeyPrefix is Show, any character that follows an ampersand is drawn as an underlined hotkey (a double ampersand is drawn as a single ampersand). If this is None, any ampersands are drawn as ampersands. If this is Hide, hotkey ampersands are hidden. |
LineAlignment | Determines the text’s vertical alignment. This can be Near (top), Center (middle), or Far (bottom). |
SetMeasureableCharacterRanges | Sets an array of CharacterRange structures representing ranges of characters that will later be measured by the Graphics object’s MeasureCharacterRanges method. |
SetTabStops | Sets an array of Singles giving the positions of tab stops. See the section “Tab Stops” later in this chapter for more information. |
Trimming | Determines how the text is trimmed if it cannot fit in the layout rectangle. See the section “Trimming” later in this chapter for more information. |
The following sections describe some of the more complex formatting issues in greater detail.
The StringFormat object’s FormatFlags property determines the object’s behavior. This property can take a bitwise combination of the values described in the following table.
FormatFlags Value | Purpose |
---|---|
DirectionRightToLeft | Indicates that the text is drawn from right to left. |
DirectionVertical | Indicates that the text is drawn vertically. |
FitBlackBox | Indicates that no character should extend beyond the layout rectangle. If this is not set, some characters in certain fonts may stick out a bit. |
LineLimit | If the last line displayed in the layout rectangle is too tall to fit, this flag indicates that it should be omitted. By default, that line is clipped to show whatever parts will fit. |
MeasureTrailingSpaces | Indicates that the Graphics object’s MeasureString method should include spaces at the ends of lines. By default, it does not. |
NoClip | Indicates that parts of characters that hang over the layout rectangle are not clipped. |
NoFontFallback | If a character is missing from the selected font, the GDI+ normally looks for an equivalent character in another font. This flag prevents that and forces the character to be displayed as the font’s missing character glyph (usually an open square). |
NoWrap | Indicates that text should not be wrapped. |
Figure 22-4 shows the difference between the default, NoClip, and LineLimit flags. If the last visible line won’t fit within the layout rectangle, the default behavior is to clip the line to show whatever fits. If FormatFlags is NoClip, that line is displayed entirely. If FormatFlags is LineLimit, the line is omitted entirely.
Figure 22-4: The NoClip and LineLimit flags change how a StringFormat object handles the text’s last displayed line.
The following code uses the DirectionVertical flag. After defining a string, font, and layout rectangle, it creates a StringFormat object. It sets the object’s Alignment and LineAlignment properties to center the text and then sets its FormatFlags property to the combination of the values DirectionVertical and DirectionRightToLeft. Then the program draws the text as usual.
Private Sub Form1_Paint(ByVal sender As Object, _ ByVal e As System.Windows.Forms.PaintEventArgs) Handles MyBase.Paint Dim txt As String = "The quick brown fox jumps over the lazy dog." Dim layout_rect As New RectangleF(0, 0, _ Me.ClientSize.Width - 1, Me.ClientSize.Height - 1) Using the_font As New Font("Times New Roman", 30, _ FontStyle.Bold, GraphicsUnit.Pixel) Using string_format As New StringFormat string_format.Alignment = StringAlignment.Center string_format.LineAlignment = StringAlignment.Center string_format.FormatFlags = _ StringFormatFlags.DirectionVertical Or _ StringFormatFlags.DirectionRightToLeft e.Graphics.TextRenderingHint = _ System.Drawing.Text.TextRenderingHint.AntiAliasGridFit e.Graphics.DrawString(txt, the_font, Brushes.Black, _ layout_rect, string_format) End Using ' string_format End Using ' the_font End Sub
Figure 22-5 shows the result.
Figure 22-5: Setting the StringFormat object’s FormatFlags property to DirectionVertical produces vertical text.
This example sets the FormatFlags property to DirectionVertical plus DirectionRightToLeft. If you omit the second flag, the lines of text are drawn left to right. That means the line “The quick…” appears on the left and the subsequent lines appear moving to the right, so the lines are drawn in the reverse of the order that you might expect.
Note that you can also draw vertical text by applying a transformation to the Graphics object that translates the text to the origin, rotates it, and translates the text back to where it belongs.
The following code demonstrates this technique. It defines its layout rectangle, text, and font. It uses the form’s width to define the rectangle’s height and the form’s height to define the rectangle’s width, so the rectangle will fit the form after it is rotated. The code creates a StringFormat object to center the text and applies the transformations to the Graphics object. Finally, the code draws the text. The result is similar to the text produced by the previous example.
Private Sub Form1_Paint(ByVal sender As Object, _ ByVal e As System.Windows.Forms.PaintEventArgs) Handles MyBase.Paint ' Define the layout rectangle. It's slightly smaller ' than the form rotated 90 degrees so the rotated ' text will fit the form nicely. Dim rect_wid As Integer = Me.ClientSize.Height - 20 Dim rect_hgt As Integer = Me.ClientSize.Width - 20 Dim layout_rect As New RectangleF( _ (Me.ClientSize.Width - rect_wid) \ 2, _ (Me.ClientSize.Height - rect_hgt) \ 2, _ rect_wid, rect_hgt) ' Define the text and font. Dim txt As String = "The quick brown fox jumps over the lazy dog." Using the_font As New Font("Times New Roman", 30, _ FontStyle.Bold, GraphicsUnit.Pixel) ' Set the StringFormat to center the text. Using string_format As New StringFormat string_format.Alignment = StringAlignment.Center string_format.LineAlignment = StringAlignment.Center ' Translate to the origin, rotate, and translate back. e.Graphics.TranslateTransform( _ -Me.ClientSize.Width \ 2, _ -Me.ClientSize.Height \ 2, _ MatrixOrder.Append) e.Graphics.RotateTransform(90, MatrixOrder.Append) e.Graphics.TranslateTransform( _ Me.ClientSize.Width \ 2, _ Me.ClientSize.Height \ 2, _ MatrixOrder.Append) ' Draw the text and layout rectangle. e.Graphics.TextRenderingHint = _ System.Drawing.Text.TextRenderingHint.AntiAliasGridFit e.Graphics.DrawString(txt, the_font, Brushes.Black, _ layout_rect, string_format) e.Graphics.DrawRectangle(Pens.Black, Rectangle.Round(layout_rect)) End Using ' string_format End Using ' the_font End Sub
This approach is a bit more complicated than the previous method of setting the StringFormat object’s FormatFlags property to DirectionVertical, but it gives you more flexibility. Simply by changing the rotation transformation, you can draw text at any angle, not just rotated 90 degrees. Figure 22-6 shows this program with the angle of rotation changed from 90 to 60 degrees.
Figure 22-6: Transformations can produce text rotated at any angle.
The StringFormat object’s GetTabStops and SetTabStops methods let you get and set an array of Singles that determine the position of the layout rectangle’s tab stops. Each entry in the array gives the distance between two tab stops. For example, the values {50, 50, 50} specify tab stops 50, 100, and 150 pixels from the left edge of the layout rectangle.
The following code shows how to use the SetTabStops method. It starts by creating a font, layout rectangle, and StringFormat object. It sets two tab stops 60 and 140 (60 + 80) pixels from the left edge of the layout rectangle. It then draws the rectangle and draws vertical lines showing the positions of the tab stops.
The program generates some random data, separating values on each row with Tab characters and separating each row with Carriage Return/Line Feed pairs. Finally, the program draws the text.
Private Sub Form1_Paint(ByVal sender As Object, _ ByVal e As System.Windows.Forms.PaintEventArgs) Handles MyBase.Paint Dim layout_rect As New RectangleF(10, 10, _ Me.ClientSize.Width - 20, Me.ClientSize.Height - 20) ' Define the font, layout rectangle, and StringFormat. Using the_font As New Font("Times New Roman", 15, _ FontStyle.Regular, GraphicsUnit.Pixel) Using string_format As New StringFormat ' Set the tab stops. Dim tab_stops() As Single = {60, 80} string_format.SetTabStops(0, tab_stops) ' Draw the layout rectangle and tab stops. e.Graphics.DrawRectangle(Pens.White, Rectangle.Round(layout_rect)) Dim x As Single = layout_rect.X For i As Integer = 0 To tab_stops.Length - 1 x += tab_stops(i) e.Graphics.DrawLine(Pens.White, _ x, layout_rect.Top, x, layout_rect.Bottom) Next i ' Generate some random values. Dim rnd As New Random Dim txt As String = "Alpha" & vbTab & "Gamma" & _ vbTab & "Value" & vbCrLf For r As Integer = 1 To 10 txt &= rnd.Next(10, 99) & vbTab & _ rnd.NextDouble.ToString("0.000000") & vbTab & _ rnd.NextDouble.ToString("0.00") & vbCrLf Next r ' Draw the text. e.Graphics.TextRenderingHint = _ System.Drawing.Text.TextRenderingHint.AntiAliasGridFit e.Graphics.DrawString(txt, the_font, Brushes.Black, _ layout_rect, string_format) End Using ' string_format End Using ' the_font End Sub
Figure 22-7 shows the result.
Figure 22-7: The SetTabStops method lets you easily align text.
Normally, a string is wrapped as necessary until its layout rectangle is full. If there is still text that has not been displayed, the Trimming property determines how that text is handled. The following table describes the values this property can take.
Trimming Value | Purpose |
---|---|
Character | The text is trimmed to the nearest character. |
EllipsisCharacter | The text is trimmed to the nearest character and an ellipsis is displayed at the end of the line. |
EllipsisPath | The center of the line is removed and replaced with an ellipsis. This is sometimes a good choice when displaying file paths because it shows the beginning of the path and the file name. This method keeps as much of the last backslash (\) delimited part of the text as possible (it assumes that this is a file name). |
EllipsisWord | The text is trimmed to the nearest word and an ellipsis is displayed at the end of the line. |
None | The text is not trimmed. Instead, it is wrapped to the next line, which is hidden because it is below the bottom of the layout rectangle. If the last visible line contains a word break, the line will wrap after the last word that fits. That makes this seem similar to the Word setting. |
Word | The text is trimmed to the nearest word. |
The following code draws samples of the Trimming values. The form’s Paint event handler calls subroutine DrawSample to draw samples of text that contain backslash (\) or space-delimited strings.
Subroutine DrawSample draws the name of the indicated Trimming value. It draws the sample text using the Trimming value in a specified layout rectangle, and then draws the rectangle so that you can see it. The routine finishes by incrementing its parameter Y, so the next sample is drawn below this one.
Public Class Form1 Private Sub Form1_Load(ByVal sender As System.Object, _ ByVal e As System.EventArgs) Handles MyBase.Load Me.ResizeRedraw = True End Sub Private Sub Form1_Paint(ByVal sender As Object, _ ByVal e As System.Windows.Forms.PaintEventArgs) Handles MyBase.Paint Dim layout_rect As RectangleF e.Graphics.TextRenderingHint = _ System.Drawing.Text.TextRenderingHint.AntiAliasGridFit Dim txt As String Using the_font As New Font("Times New Roman", 30, _ FontStyle.Bold, GraphicsUnit.Pixel) layout_rect = New RectangleF(100, 0, 180, 70) txt = "ABC\DEF\GHI\JKL\MNO\PQR\STU\VWX\YZ" DrawSample(e.Graphics, txt, the_font, layout_rect, _ StringTrimming.Character) DrawSample(e.Graphics, txt, the_font, layout_rect, _ StringTrimming.EllipsisCharacter) DrawSample(e.Graphics, txt, the_font, layout_rect, _ StringTrimming.EllipsisPath) DrawSample(e.Graphics, txt, the_font, layout_rect, _ StringTrimming.EllipsisWord) DrawSample(e.Graphics, txt, the_font, layout_rect, _ StringTrimming.None) DrawSample(e.Graphics, txt, the_font, layout_rect, _ StringTrimming.Word) layout_rect.X += layout_rect.Width + 10 layout_rect.Y = 0 txt = "ABC DEF GHI JKL MNO PQR STU VWX YZ" DrawSample(e.Graphics, txt, the_font, layout_rect, _ StringTrimming.Character) DrawSample(e.Graphics, txt, the_font, layout_rect, _ StringTrimming.EllipsisCharacter) DrawSample(e.Graphics, txt, the_font, layout_rect, _ StringTrimming.EllipsisPath) DrawSample(e.Graphics, txt, the_font, layout_rect, _ StringTrimming.EllipsisWord) DrawSample(e.Graphics, txt, the_font, layout_rect, _ StringTrimming.None) DrawSample(e.Graphics, txt, the_font, layout_rect, _ StringTrimming.Word) End Using ' the_font End Sub Private Sub DrawSample(ByVal gr As Graphics, ByVal txt As String, _ ByVal the_font As Font, ByRef layout_rect As RectangleF, _ ByVal string_trimming As StringTrimming) Using string_format As New StringFormat string_format.Trimming = string_trimming gr.DrawString(string_trimming.ToString, Me.Font, _ Brushes.Black, 0, layout_rect.Y + 5) gr.DrawString(txt, the_font, Brushes.Black, layout_rect, _ string_format) gr.DrawRectangle(Pens.Black, Rectangle.Round(layout_rect)) layout_rect.Y += layout_rect.Height + 10 End Using ' string_format End Sub End Class
Figure 22-8 shows the results.
Figure 22-8: The StringFormat object’s Trimming property determines how text is trimmed.