Introduction

Typically the ListBox, ComboBox, and MainMenu controls display a series of text items. But with a little extra work you can make many of them display whatever you want. By making these controls owner-drawn, you can use your own code to draw the items in any way you like. They can display images, text, and other graphics drawn by your code, such as ellipses and polygons.

Making Owner Drawn Controls

There are only three steps to making an owner-drawn control. First, specify that the control is owner drawn. Exactly how you do this varies slightly depending on the type of control. Second, handle the control's MeasureItem event to indicate the size needed by the items displayed in the control. Third, handle the control's DrawItem event to draw the items displayed by the control.

The following sections explain how the example programs display an owner drawn ListBox, ComboBox, and MainMenu. All three examples use the PlanetInfo class described in this section.

The PlanetInfo class shown in Listing 1 holds information about a planet. It provides a simple constructor to make initializing its fields easier.

The class provides two methods that return text. First, it overrides the ToString method to return the planet's name. This is generally useful because it helps IntelliSense tell you what value an object contains. For example, if you hover the mouse over the variable mars, which contains information about the planet Mars, then IntelliSense uses the object's ToString method show the value "Mars" in a tooltip.

The second method that returns text is TextData. This method returns all of the object's text data concatenated across multiple lines.

The class's most interesting method is DrawItem, which draws the object's picture and text data inside a rectangle. It takes as parameters the Graphics object on which to draw, a Rectangle indicating where to draw, the Font to use while drawing the text, and a Boolean indicating whether to draw only the planet's name (as opposed to all of the object's text).

The DrawItem method calculates a five percent margin around the picture and draws it in the target Rectangle. It then draws the appropriate text to the right of the picture.

This code has total control over how the object is drawn so you can do anything you want here. For example, you could draw the picture on the right side of the text, draw the planet's name in bold, or draw a box around the entry.

At design time, I set the control's DrawMode property to OwnerDrawVariable to indicate that this is an owner drawn control and that each item sets its own height.

The program uses the following MeasureItem event handler. This code simply indicates that every item in the ListBox should have the same height: 50 pixels. You could give every item in the list a different height if you wanted.

The program uses the following DrawItem event handler. This code converts the sender into the ListBox that raised the event and then gets the PlanetInfo object corresponding to the ListBox item that it should draw.

The code then uses the e.DrawBackground method to draw the item's background appropriately. This automatically gives the object a highlighted background if it is selected.

Finally the event handler calls the PlanetInfo object's DrawItem method to draw the item, passing it the e.Graphics object on which to draw and the e.Boundsrectangle to indicate where to draw. It also passes the method the form's font and the value false to indicate that the method should draw all of the object's text data not just its name.

Owner Drawn ComboBoxes

Figure 2: An Owner Drawn ComboBox

ComboBoxes are very similar to ListBoxes so it should come as no surprise that making an owner drawn ComboBox is similar to making an owner drawn ListBox.

The OwnerDrawComboBox program shown in Figure 2 uses code similar to the code used by the OwnerDrawListBox example to add PlanetInfo objects in its ComboBox. The form's Load event handler also includes the following code to set some important ComboBox properties. The code sets the control's DropDownStyle and indicates that it is an owner drawn control. It also sets the width and height of the dropdown area to constants defined elsewhere in the code.

The following code shows the program's DrawItem event handler. The only real change from the previous version is that this code checks the height allowed for the item. If the height is smaller than the height requested for an item, then the control is trying to draw the selected item shown on the control's surface and not an item in the dropdown area. In that case, the code draws only the planet's name and not all of its data. In Figure 2 the control is displaying a tiny picture of Venus with its name at the top of the form.

Owner Drawn MainMenus

Figure 3: An Owner Drawn MainMenu

The .NET Framework 2.0 replaced the MainMenu control with the MenuStrip control. Unfortunately MenuStrip doesn't support owner drawn items. If you have the MainMenu control installed on your system, however, you can still use it to make owner drawn items if you don't mind its slightly different appearance. (You might need to right-click on your Toolbox and select Choose Items to make the control available.)

Instead of making the MainMenu itself owner drawn, the program can make the MenuItems that it contains owner drawn. To do so, set the items' OwnerDraw properties to true and handle the MeasureItem and DrawItem events as usual.

In the previous examples, the ListBox and ComboBox controls contained PlanetInfo objects. However a MenuItem can hold only text, not an arbitrary object. Fortunately its Tag property can hold an arbitrary object.

The OwnerDrawMenu program shown in Figure 3 includes the following code in its form's Load event handler to store PlanetInfo objects in its menu items' Tag properties.

The following code shows the program's MeasureItem event handler. The code converts the sender parameter into the MenuItem that raised the event and gets the PlanetInfo object stored in its Tag property. It then sets the item's size to a fixed height and enough width to display the picture and the planet's name.

The following code shows the program's DrawItem event handler. This code recovers the appropriate PlanetInfo object as before, draws the item's background, and then uses the object's DrawItem method to draw the menu item. In this example, the program draws only the planet's name, not all of its text data.

Conclusion

Usually the ListBox, ComboBox, and MainMenu controls display lists of text but by making them owner drawn you can make them display just about anything. All you need to do is mark the control as owner drawn and handle the MeasureItem and DrawItem events. With a little work, you can use owner drawn controls to make your programs more informative, distinctive, and appealing.

Unfortunately the CheckListBox doesn't support owner draw. The ListView does but it's a bit different from the controls described here. Perhaps I'll explain how to make owner drawn ListView controls in another article.

End-User License

Author Information

Rod is a Microsoft MVP who has written more than 20 books that have been translated into languages from all over the world, and more than 250 magazine articles covering C#, Visual Basic, Visual Basic for Applications, Delphi, and Java. His C# Helper and VB Helper websites contains tips, tricks, and example programs for C# and Visual Basic programmers. You can contact Rod at RodStephens@CSharpHelper.com.