Добавил:
Опубликованный материал нарушает ваши авторские права? Сообщите нам.
Вуз: Предмет: Файл:
(ebook) Visual Studio .NET Mastering Visual Basic.pdf
Скачиваний:
136
Добавлен:
17.08.2013
Размер:
15.38 Mб
Скачать

BUILDING OWNER-DRAWN CONTROLS 425

background of each item, draw the text in any colors, and create items of varying heights. This is a very interesting technique, because without it, as you recall from our discussion of the ListBox control, all items have the same height and you must make each control wide enough to fit the longest item (if this is known at design time).

To create an owner-drawn control, you must program two events: the MeasureItem and DrawItem events. As you may have noticed, you can only interfere with the drawing process of controls that display items in rectangles (like the MenuItem and ListBox controls). The MeasureItem event is where you decide about the dimensions of the rectangle where the drawing will take place.

These two events don’t take place unless you set the DrawMode property of the control. Since only controls that expose the DrawMode property can be owner-drawn, you have a quick way of figuring out whether a control’s appearance can be customized with the techniques discussed in this section. This property can be set to Normal (the control is draws its own surface), OwnerDrawnFixed (you can draw the control, but the height of the drawing area remains fixed), or OwnerDrawnVariable (you can draw the control and use a different height for each item). The settings of the DrawMode shown here apply to the ListBox control. The MenuItem control provides the OwnerDraw mode, whose settings are True (you’re responsible for rendering the item’s rectangle) or False. Let’s start by building an owner-drawn menu.

Designing Owner-Drawn Menus

When a menu item’s OwnerDraw property is set to True, the following events are fired every time the item is about to be drawn: first the MeasureItem event, then the DrawItem event. In the first event, you can find out the properties of the item and set up the size of the rectangle in which the menu item will be rendered. In the second event, you must insert the code to draw the item.

The second argument of both events, the ubiquitous e argument, exposes the Graphics object, which represents the area on which the item will be drawn. In the MeasureItem event’s handler, you can’t draw anything. You can calculate the dimensions of the rectangle that delimits the drawing you want to create and set the e argument’s ItemWidth and ItemHeight properties respectively.

In the DrawItem event’s handler, you can call any of the Graphic object’s drawing commands to render anything on the item’s rectangle. You can draw text in any font, style, or size, draw simple shapes, or even place a small bitmap in the item’s rectangle. To demonstrate the design of ownerdrawn menus, I’ve included the OwnerDrawnMenu project on the CD, which creates a simple menu with font names. Each name is rendered in the corresponding font, so that you can see what the text will look like when rendered in this font. The form of the OwnerDrawnMenu project is shown in Figure 9.10.

Figure 9.10

A simple ownerdrawn menu

Copyright ©2002 SYBEX, Inc., Alameda, CA

www.sybex.com

426 Chapter 9 BUILDING CUSTOM WINDOWS CONTROLS

First, you must design the menu as usual and set the OwnerDrawn property of each menu item to True. Give meaningful names to all items, so that you can simplify your code.

Then insert the code of the Listing 9.20 in the MeasureItem and DrawItem event handlers of each owner-drawn menu item.

Listing 9.20: Programming the MeasureItem and DrawItem Events

Private Sub FontVerdana_MeasureItem(ByVal sender As Object, _

ByVal e As System.Windows.Forms.MeasureItemEventArgs) _ Handles FontVerdana.MeasureItem

Dim fnt As New Font(“Verdana”, 12, FontStyle.Regular) Dim itemSize As SizeF

itemSize = e.Graphics.MeasureString(“Verdana”, fnt) e.ItemHeight = itemSize.Height

e.ItemWidth = itemSize.Width End Sub

Private Sub FontVerdana_DrawItem(ByVal sender As Object, _

ByVal e As System.Windows.Forms.DrawItemEventArgs) _ Handles FontVerdana.DrawItem

Dim fnt As New Font(“Verdana”, 12, FontStyle.Regular) Dim R As New RectangleF(e.Bounds.X, e.Bounds.Y, _

e.Bounds.Width, e.Bounds.Height)

Dim brush As SolidBrush e.Graphics.FillRectangle(Brushes.PaleTurquoise, R) e.Graphics.DrawString(“Verdana”, fnt, Brushes.White, R)

End Sub

I’m only showing the code for the first menu item; the others are identical. In the MeasureItem event handler, the code calls the MeasureString method to find out the dimensions of the item’s caption when rendered in its font and then sets the dimensions of the item’s rectangle in the menu. The code in the DrawItem event handler draws the caption in the item’s rectangle. It uses a white solid brush and sets the item’s background color to red. The string is rendered in white color. The last item (the handwriting font) is rendered in blue color on a yellow background.

The code is quite trivial really, and all the drawing methods will be discussed in detail in Chapter 14. You can return to this project after reading about the drawing methods and create more elaborate owner-drawn menus.

Designing Owner-Drawn ListBox Controls

In this section, we’ll look at a similar technique for designing owner-drawn ListBox controls. You may have to create owner-drawn ListBoxes if you want to use different colors or fonts for different items, or to populate the list with items of widely different lengths. The example you’ll build in this section, shown in Figure 9.11, uses an alternating background color, and each item has a different height, depending on the string it holds. Lengthy strings are broken into multiple lines at word boundaries. Since you’re responsible for breaking the string into lines, you can use any other

Copyright ©2002 SYBEX, Inc., Alameda, CA

www.sybex.com

BUILDING OWNER-DRAWN CONTROLS 427

technique—for example, you can place an ellipsis to indicate that the string is too long to fit on the control, or use a smaller font, and so on.

Figure 9.11

An unusual, but quite functional ListBox control

The fancy ListBox of Figure 9.11 was created with the OwnerDrawnList project, which you will find on the CD. Or you can follow the steps outlined in this section to build it from scratch.

To custom-draw the items on a ListBox control (or a ComboBox, for that matter), you use the MeasureItem event to calculate the item’s dimensions and the DrawItem event to actually draw the item. Each item is a rectangle that exposes a Graphics object, and you can call any of the Graphics object’s drawing methods to draw on the item’s area. The drawing techniques we’ll use in this example are similar to the ones we used in the previous section, but once you learn more about the drawing methods in Chapter 14, you’ll be able to create even more elaborate designs than the ones shown here.

The items to be added to the list are stored in an ArrayList, which is populated in the form’s Load event handler. Each time you add a new item to the ListBox control, it’s first added to the ArrayList, then to the control. The reason for doing this is because at the time the MeasureItem event is fired, the item isn’t part of the list yet and there’s no simple method to access the new item—short of using a global variable. It’s a minor inconvenience, and if you experiment with the OwnerDrawnList project, you may be able to find a work-around.

Each time an item is about to be drawn, the MeasureString and DrawString events are fired, in this order. In the MeasureString event handler, we set the dimensions of the item with the statements shown in Listing 9.21.

Listing 9.21: Setting Up an Item’s Rectangle in an Owner-Drawn ListBox Control

Private Sub ListBox1_MeasureItem(ByVal sender As Object, _

ByVal e As System.Windows.Forms.MeasureItemEventArgs) _ Handles ListBox1.MeasureItem

Dim itmSize As SizeF

Dim S As New SizeF(ListBox1.Width, 200)

itmSize = e.Graphics.MeasureString(items(e.Index).ToString, fnt, S) e.ItemHeight = itmSize.Height

e.ItemWidth = itmSize.Width End Sub

Copyright ©2002 SYBEX, Inc., Alameda, CA

www.sybex.com

428 Chapter 9 BUILDING CUSTOM WINDOWS CONTROLS

This time we’re using a different form of the MeasureString method. This form accepts as arguments a string, the font in which the string will be rendered, and a SizeF object. The SizeF object contains two members, the Width and Height members. These two members are used to pass to the method information about the area in which we want to print the string. In our example, we’re going to print the string in a rectangle that’s as wide as the ListBox control and as tall as needed to fit the entire string. I’m using a height of 200 pixels (enough the fit the longest string users may throw at the control). Upon return, the MeasureString method sets the members of the SizeF object to the width and height actually required to print the string. What we get back is the height of a rectangle in which the string will fit.

The two members of the SizeF object are then used to set the dimensions of the current item (properties e.ItemWidth and e.ItemHeight). We’ve set the dimensions of the item, and we’re ready to draw it. The custom rendering of the current item takes place in the ItemDraw event handler, which is shown in Listing 9.22.

Listing 9.22: Drawing an Item in an Owner-Drawn ListBox Control

Private Sub ListBox1_DrawItem(ByVal sender As Object, _

ByVal e As System.Windows.Forms.DrawItemEventArgs) _ Handles ListBox1.DrawItem

If e.Index = -1 Then Exit Sub Dim txtBrush As SolidBrush Dim bgBrush As SolidBrush Dim txtfnt As Font

If e.Index / 2 = CInt(e.Index / 2) Then ‘ color even numbered items

txtBrush = New SolidBrush(Color.Blue) bgBrush = New SolidBrush(Color.LightYellow)

Else

‘ color odd numbered items

txtBrush = New SolidBrush(Color.Blue) bgBrush = New SolidBrush(Color.Cyan)

End If

If e.State And DrawItemState.Selected Then

use red color and bold for the selected item txtBrush = New SolidBrush(Color.Red)

txtfnt = New Font(fnt.Name, fnt.Size, FontStyle.Bold)

Else

txtfnt = fnt End If

e.Graphics.FillRectangle(bgBrush, e.Bounds) e.Graphics.DrawRectangle(Pens.Black, e.Bounds) Dim R As New RectangleF(e.Bounds.X, e.Bounds.Y, _

e.Bounds.Width, e.Bounds.Height) e.Graphics.DrawString(items(e.Index).ToString, txtfnt, txtBrush, R) e.DrawFocusRectangle()

End Sub

Copyright ©2002 SYBEX, Inc., Alameda, CA

www.sybex.com