Strings


Of course, deciding on a font is only half the fun. The real action is drawing strings after the font's been picked. For that, you use the DrawString method of the Graphics object:

 
 Dim myfont As Font = New Font("Arial", 12) ' This will wrap at new line characters g.DrawString("line 1" & vbCrLf & "line 2", myfont, _ Brushes.Black, 10, 10) 
Table 5.1. Font and FontFamily Sizes (Sample Font Is Arial 12 Point at 96 dpi)

Measure

Units

Example

Description

FontFamily.GetEmHeight

Design Units

2,048

Base size, equivalent to Size

FontFamily.GetCellAscent

Design Units

1,854

Height above base line

FontFamily.GetCellDescent

Design Units

434

Height below base line

FontFamily.GetLineSpacing

Design Units

2,355

CellAscent + CellDescent + Leading, normally about 115% of EmHeight

Leading

Design Units

67

Extra space below bottom of CellDescent for readability, not exposed by any property

Font.Size

GraphicsUnit passed to Font ctor (defaults to Point)

16 pixels

Base size, equivalent to EmHeight

Font.SizeInPoints

Points

12 points

Base size in points, equivalent to Size and EmHeight

Font.GetHeight

Pixels

18.39844

Equivalent to LineSpacing scaled to either Graphics object or dpi

Font.Height

Pixels

19

Equivalent to LineSpacing scaled to system dpi and rounded to next highest integer value

Scaling Factor

Design Unit or Pixels

128

Used to convert design units to physical or logical values for rendering

The DrawString method takes, at a minimum, a string, a font, a brush to fill in the font characters, and a point. DrawString starts the drawing at the point and keeps going until it hits the edges of the region in the Graphics object. This includes translating new line characters as appropriate but does not include wrapping at word boundaries. To get the wrapping, you specify a layout rectangle:

 
 Dim myfont As Font = New Font("Arial", 12) ' This will automatically wrap long lines and ' it will wrap at new line characters g.DrawString("a long string...", myfont, Brushes.Black, _    Me.ClientRectangle) Myfont.Dispose() 

Formatting

If you'd like to turn off wrapping or set other formatting options, you can do so with an instance of the StringFormat class:

 
 NotInheritable Class StringFormat   Inherits MarshalByRefObject   Implements ICloneable   Implements IDisposable   ' Constructors   Public Sub New(...) ' various overloads   ' Properties   Property Alignment() As StringAlignment   Property DigitSubstitutionLanguage() As Integer   Property DigitSubstitutionMethod() As StringDigitSubstitute   Property FormatFlags() As StringFormatFlags   Shared Property GenericDefault() As StringFormat   Shared Property GenericTypographic() As StringFormat   Property HotkeyPrefix() As HotkeyPrefix   Property LineAlignment() As StringAlignment   Property Trimming() As StringTrimming   ' Methods   Function GetTabStops(ByRef firstTabOffset As Single) As Single()   Sub SetDigitSubstitution(language As Integer, _           substitute As StringDigitSubstitute)   Sub SetMeasurableCharacterRanges(ranges As CharacterRange())   Sub SetTabStops(firstTabOffset As Single, tabStops As Single()) End Class 

A StringFormat object lets you set all kinds of interesting text characteristics, such as the tab stops and the alignment (vertically and horizontally) as well as whether to wrap:

 
 ' Turn off auto-wrapping Dim format As StringFormat = New StringFormat(StringFormatFlags.NoWrap) g.DrawString("...", font, brush, rect, format) 

The StringFormatFlags enumeration provides a number of options:

 
 Enum StringFormatFlags   0 ' No flags (default)   DirectionRightToLeft ' Draw text right-to-left   DirectionVertical ' Draw text up-to-down   DisplayFormatControl ' Show format control character glyphs   FitBlackBox ' Keep all glyphs inside layout rectangle   LineLimit ' Show only whole lines   MeasureTrailingSpaces ' MeasureString includes trailing spaces   NoClip ' Don't clip text partially outside layout rectangle   NoFontFallback ' Don't fallback for characters missing from Font   NoWrap ' Don't interpret newlines or tabs          ' (implied when no rect provided) End Enum 

Note that a glyph is a symbol that conveys some kind of information. For example, the X in the upper-right corner of a window is a glyph that indicates that the window can be closed.

You can combine and set one or more StringFormatFlags on a StringFormat object by using either the StringFormat constructor or the FormatFlags property. For example, the following draws text down-to-up and disables automatic wrapping:

 
 Dim format As StringFormat = New StringFormat() Format.FormatFlags = _   StringFormatFlags.DirectionVertical Or StringFormatFlags.NoWrap g.DrawString("...", font, brush, rect, format) 

When text doesn't fit into the allotted space, you have a few options. If the string is too tall, you have three choices. You can clip to the layout rectangle, letting partial lines show, which is the default. You can show only complete lines if they fit inside the layout rectangle, which is the behavior you get with StringFormatFlags.LineLimit. Finally, you can decide to show complete lines even if they lie outside the layout rectangle, which is what you get with StringFormatFlags.NoClip. Combining LineLimit with NoClip is not useful, because the behavior is the same as LineLimit. The three options are shown in Figure 5.3.

Figure 5.3. The Effect of the LineLimit StringFormatFlags Value

String Trimming

If, on the other hand, the string is too long, you can decide what happens by setting the Trimming property of the StringFormat object to one of the StringTrimming enumeration values:

 
 Enum StringTrimming   None ' No trimming (acts like Word for single lines)   Character ' Trim to nearest character (the default)   EllipsisCharacter ' Trim to nearest character and show ellipsis   Word ' Trim to nearest word   EllipsisWord ' Trim to nearest word and show ellipsis   EllipsisPath ' Trim file path by putting ellipsis in the middle End Enum 

Figure 5.4 shows the results of applying the StringTrimming values when you draw a string.

Figure 5.4. Examples of the StringTrimming Enumeration

Tab Stops

Something else of interest in Figure 5.4 is the use of tabs to line up the string, instead of forcing the text to be in a monospaced font and lining up the text with spaces. Tabs are set using the SetTabStops method of the StringFormat class:

 
 Dim format As StringFormat = New StringFormat Dim size As SizeF =  g.MeasureString( _   StringTrimming.EllipsisCharacter.ToString(), Me.Font) format.SetTabStops(0, New Single() { size.Width + 10 }) 

This call to SetTabStops sets a single tab stop to be the width of the longest string, plus a pleasing amount of leading. When tab stops are specified and when StringFormatFlags.NoWrap is absent from the StringFormat object, then the tab character causes the characters that follow to be drawn starting at the tab stop offset (unless the string has already passed that point). If the StringFormat object has not been given any tab stops, then the tab character will not be interpreted. If DrawString is called without any StringFormat object at all, it will build one internally that defaults to four times the size of the font; for example, a 12-point font will have tab stops every 48 points.

There are several ways to specify tab stops logically. For example, imagine that you'd like a tab stop at every 48 units, as DrawString does by default when no StringFormat is provided. You might also imagine that you'd like to specify only a certain number of tab stops at specific locations. Finally, you might imagine that you'd like to have an array of tab stops, but use an offset determined at run time to calculate the actual tab stops. All these techniques are supported, but you must use a single SetTabStops method, and that makes things somewhat unintuitive.

The array of floating point values passed to set the tab stops represents the spaces between successive tab stops. The first value in this array is added to the first argument to SetTabStops to get the first tab stop, and each successive value is added to the preceding value to get the next tab stop. Finally, when more tabs are found than tab stops, the last value of the array is added repeatedly to get successive tab stops. Table 5.2 shows various arguments passed to SetTabStops and the resultant offsets for each stop.

Table 5.2. Sample Arguments to SetTabStop and Resultant Tab Stops

Arguments to SetTabStop

Resultant Tab Stops

Description

0, { 100 }

100, 200, 300,

Tab stops every 100 units

0, { 100, 0 }

100, 100, 100,

One tab stop at 100 units

0, { 50, 75, 100 }

50, 125, 225, 325, 425,

A tab stop at 50, 125, and 225 and then one every 100 units

0, { 50, 75, 100, 0 }

50, 125, 225, 225, 225,

A tab stop at 50, 125, and 225 units

50, { 100 }

150, 250, 350,

One tab stop at 150 units and then one every 100 units

50, { 100, 0 }

150, 150, 150,

One tab stop at 150 units

50, { 50, 75, 100 }

100, 175, 275, 375, 475,

A tab stop at 100, 175, and 275 and then one every 100 units

50, { 50, 75, 100, 0 }

100, 175, 275, 275, 275,

A tab stop at 100, 175, and 275 units

You may have noticed the GetTabStops method on the StringFormat class, but unfortunately it hands back only the same tab stop settings handed to SetTabStops in the first place. It would have been handy to get back the resultant tab stops so that you could make sure you've set them correctly.

Hotkey Prefixes

In addition to new lines and tab characters, DrawString can substitute other characters, including ampersands and digits. Substitution of ampersands is a convenience for specifying Windows hotkeys for menu items and form fields. For example, by default the string "&File" will be output as "&File" (but without the quotation marks). However, you can specify that the ampersand be dropped or that the next character be underlined , as governed by the HotkeyPrefix enumeration from the System.Drawing.Text namespace:

 
 Enum HotkeyPrefix   Hide ' Drop all & characters   None ' Show all & characters (default)   Show ' Drop & characters and underline next character End Enum 

For example, the following translates "&File" into " F ile" (no quotation marks) as the string is drawn:

 
 Dim format As StringFormat = New StringFormat() format.HotkeyPrefix = HotkeyPrefix.Show g.DrawString("&File", font, brush, rect, format) 
Digit Substitution

One other substitution that DrawString can perform is for digits. Most languages have adopted the Arabic digits (0, 1, 2, 3, ) when representing numbers , but some also have traditional representations. Which representation to show is governed by the method and language as determined by a call to the SetDigitSubstitution method on the StringFormat class:

 
 Dim culture As CultureIfno = New CultureInfo("th-TH") ' Thailand Thai Dim format As StringFormat = New StringFormat() format.SetDigitSubstitution( _   culture.LCID, StringDigitSubstitute.Traditional) g.DrawString("0 1 2...", font, brush, rect, format) 

The substitution method is governed by the StringDigitSubstitute (and can be discovered with the DigitSubstitutionMethod on the StringFormat class), as shown in Figure 5.5.

Figure 5.5. StringDigitSubstitute Values as Applied to Thailand Thai

The integer language identifier comes from the LCID (language and culture ID) of an instance of the CultureInfo class. It can be constructed with a two-part name : a two-letter country code followed by a two-letter language code, [5] separated by a hyphen. The methods applied to the national and traditional languages of Thailand are shown in Figure 5.5.

[5] The country code and language codes are defined by ISO standards.

Alignment

In addition to substitution, tabs, wrapping, and clipping, you can use StringFormat to set text alignment (both horizontally and vertically) by setting the Alignment and LineAlignment properties, respectively, using one of the StringAlignment enumeration values:

 
 Enum StringAlignment   Center   Near ' Depends on right-to-left setting   Far ' Depends on right-to-left setting End Enum 

Notice that instead of Left and Right alignment, the StringAlignment enumeration values are Near and Far and depend on whether the RightToLeft string format flag is specified. The following centers text in a rectangle both horizontally and vertically:

 
 format.Alignment = StringAlignment.Center ' Center horizontally format.LineAlignment = StringAlignment.Center ' Center vertically 

Two combinations on a StringFormat object are so commonly needed that they're set up for you and are exposed via the GenericDefault and GenericTypographic properties of the StringFormat class. The GenericDefault StringFormat object is what you get when you create a new StringFormat object, so it saves you the trouble if that's all you're after. The GenericTypographic StringFormat object is useful for showing text as text, not as part of drawing a UI element. The properties you get from each are shown in Table 5.3.

Antialiasing

All the strings we've shown in the sample figures in this section have been nice and smooth. That's because we're using Windows XP with ClearType turned on. If we turn that off, we go back to the old, blocky way of looking at things. However, when we're drawing strings, we don't have to settle for what the user specifies. We can set the TextRenderingHint property of the Graphics object before we draw a string to one of the TextRenderingHint enumeration values, as shown in Figure 5.6.

Figure 5.6. Examples of the TextRenderingHint Enumeration

Table 5.3. The Settings of the Built-in StringFormat Classes

GenericDefault

GenericTypographic

StringFormatFlags = 0

StringFormatFlags = LineLimit, NoClip

Alignment = Near

Alignment = Near

LineAlignment = Near

LineAlignment = Near

DigitSubstitutionMethod = User

DigitSubstitutionMethod = User

HotkeyPrefix = None

HotkeyPrefix = None

No tab stops

No tab stops

Trimming = Character

Trimming = None

In this case, SystemDefault shows what text looks like without any smoothing effects. The SingleBitPerPixel setting does just what it says, although it's clearly not useful for anything that needs to look great. The AntiAlias and ClearType settings are two different algorithms for smoothing that are meant to make the text look good: one for any monitor, and one specifically for LCD displays. The grid fit versions of the algorithms use extra hints to improve the appearance, as you can see from the examples.

Of course, as the quality improves , the rendering time also increases , and that's why you can set the option as appropriate for your application. Furthermore, when drawing using one of the antialiasing algorithms, you can adjust the TextContrast property of a Graphics object. The contrast ranges from 0 to 12, where 0 is the most contrast and 12 is the least, with 4 being the default. The contrast makes fonts at smaller point sizes stand out more against the background.

Strings and Paths

One more string-drawing trick that might interest you is the ability to add strings to graphics paths. Because everything that's added to a path has both an outline and an interior that can be drawn separately, you can add strings to a path to achieve outline effects, as shown in Figure 5.7:

 
 ' Need to pass in DPI = 100 for GraphicsUnit = Display Function GetStringPath(s As String, _               dpi As Single, _               rect As RectangleF, _               font As Font, _               format As StringFormat) _ As GraphicsPath Dim path As GraphicsPath = New GraphicsPath()   ' Convert font size into appropriate coordinates   Dim emSize As Single = dpi * font.SizeInPoints / 72   path.AddString(s, font.FontFamily, CInt(font.Style), _        emSize, rect, format)   Return path End Function Sub OutlineFontsForm_Paint(sender As Object, e As PaintEventArgs)   Dim g As Graphics = e.Graphics   Dim s As String = "Outline"   Dim rect As RectangleF = NewRectangleF(Me.ClientRectangle.X, _       Me.ClientRectangle.Y, Me.ClientRectangle.Width, _       Me.ClientRectangle.Height)   Dim myfont As Font = Me.Font   Dim format As StringFormat = StringFormat.GenericTypographic   Dim dpi As Single = g.DpiY   Dim path As GraphicsPath = GetStringPath(s, dpi, _       rect, font, format)   g.DrawPath(Pens.Black, path)   path.Dispose() End Sub 
Figure 5.7. Using a GraphicsPath Object to Simulate an Outline-Only Font

Notice that even though we have ClearType on and the TextRenderingHint set to SystemDefault, the outline path was not drawn smoothly. As soon as the string was used to create a path, it stopped being text and became a shape, which is drawn smoothly or not based on the SmoothingMode property. Also, you'll notice that we showed an example of a really big font (72-point). The string- as-path trick doesn't work very well at lower resolutions because of the translation of font family characters into a series of lines and curves.

Even more interesting uses of paths are available when you apply transforms, which you'll read about in Chapter 6: Advanced Drawing.



Windows Forms Programming in Visual Basic .NET
Windows Forms Programming in Visual Basic .NET
ISBN: 0321125193
EAN: 2147483647
Year: 2003
Pages: 139

flylib.com © 2008-2017.
If you may any questions please contact us: flylib@qtcs.net