Shagrouni

The list box got a new face

Khaled Shagrouni, May 31, 2001

 

 

Delphi Papers
Delphi Papers
 

 

Are you tiered of the old same looking style of the ListBox control? Do you want something with a modern look and feel as found it in current commercial software?

Here I am continuing the series I started on how to give your common used controls a better look and feel. See my previous articles:
Give your menu Office XP style
- How to make a grid transparent

The code I presenting here targeting the ListBox control. If you are test-then-read person (like me) then go direct to the code listing , or just download the example project files: xlist.zip .

Menu

ListBox example:xlist.zip (15 KB)

Little intro:

Windows native implementation of the list box control doesn't change much in visual aspects since version 3.1 (except for 3D border and scroll bar, I guess), fortunately, Windows leaves the door open for us to introduce our own painting and override the default through OWNER-DRAW style. Borland made a good decision, since Delphi 1, to incorporate methods to deal with this style for most of the windows owner-draw controls.

The goal:

The code purpose is to provide a list box with appearance and behavior can be defined and controlled by us from within our application.

The code tries to implement the following characteristics:

  • Multi-color/font list box to reflect different states.
  • Multi line items with proportional and varying heights.
  • Hot tracking style on mouse move.
  • Bitmaps associated with items.
  • Near to zero flickering.
  • Preserve Bi-Directional functionality.

The method:

The method is simple, and can be applied in many other controls: Whenever Windows need to paint a control, it will issue messages to the application, so the application can take the responsibility and provide its own implementation of drawing the control.

The main information Windows send is the rectangle of the portion of control to be pained, and the corresponding item identity.

For our case, the list box, we first set the property style of the list box to either lbOwnerDrawFixed, or lbOwnerDrawVariable, this will force Delphi to create the list box with the new style, and internally informs Windows about the procedure that will take care of the painting, so Windows can take it into account to send the needed information to this procedure whenever a control needs to be drawn. This procedure is the event handler that we will take care in onDrawItem event of the list box.

Once we have the coordinates (rect) of the item on the control ‘s surface, we can apply any drawing methods on it by using Delphi canvas methods, or by a direct call to Windows API functions.

Code layout:

The code is not just an example or a how-to tip; my intention for the code is to be a workable solution that can be plugged into a real application without extra hassles. For this reason, you will see that the main procedures are independent to the form class, global variables are avoided, the procedures can be moved to other units (libraries, components,..) with less pain, plus taking care (as possible) to preserve reliability, and the general list box functionality. Beginners to Delphi can deal with the code as a black box, aside from their own event handlers, so they can feel safe while approaching the code.

Any way, a more dictated work, with less restriction, can be done if we move this code to be a base for an independent component.

To demonstrate the code we need a ListBox with few items, style property set to lbOwnerDrawVariabl and an image control with a small bmp picture.

The code has four main procedures, each one will be called from within a list box respective event (except ListBoxRefresh ).

Procedure: ListBoxDrawItem

To be called from onDrawItem event. This event handler will not take effect unless we set the style property to lbOwnerDrawFixed or lbOwnerDrawVariable .

This procedure is responsible for drawing each item according to its state, it will draw an image alongside each item’s text.

Procedure: ListBoxMeasureItem

We will call this procedure from onMeasureItem event, This event will fire upon list box creation, if the list box has lbOwnerDrawFixed style, the event will occur once for the first item, the rest items will have the same height. If the style is lbOwnerDrawVariable, the event will occur for each item in the list box. The procedure ListBoxMeasureItem will calculate the height of the corresponding item according to text length/font, list box width and width of the bitmap.

This procedure will be called by ListBoxRefresh also.

Procedure: ListBoxMouseMove

Called from onMouseMove event handler, this procedure is responsible for drawing hot track effect on mouse movement over the item content, you can bypass it if you want to discard this feature, otherwise, this is the place where you can put interesting and funny effects upon mouse move, for example, you can draw shaded text or shift the coordinates of drawing to get waving effect.

Procedure: ListBoxRefresh

To update the dimensions of the items upon the list box new width.

For each item; the procedure call ListBoxMeasureItem to get the height the item should have, then pass the new height to Windows through the message LB_SETITEMHEIGHT .

We only need to call this procedure If the list box is subject to resizing during the program run, and has a variable item height style (lbOwnerDrawVariable), in this case we need to refresh the list box to measure the height of each item according to the new width. Because the list box in our example is aligned to the client area of a resizable form, we call ListBoxRefresh from resize event handler of the form.

Notes:

  • As I mentioned before, I tried to avoid global variable, the only work around is the value stored in tag property of the list box, to track the last painted item.
  • A multi-columns list box doesn’t support owner draw style, instead, it proceed the default drawing regardless of the list style, and the event onDrawItem will not be fired.
  • The decision for the font and color choices is really difficult, so I will count on you to choose your own. Don’t take the code as an example for your color choice. The colors chosen in the code are just for demonstration purpose of what we can do in our lists.
  • The code tested with Delphi 5/ Win98, apparently and hopefully, it will work in other versions and environments, if not, I’ll be obligated to send me a note/modification.

Hope you'll enjoy it.


Code listing

Download the sample projectxlist.zip (15 KB)

Articles of interest:
- XP Menu Component
Give your menu Office XP style
- How to make a grid transparent

Download a better look hand point cursor cushandpoint.cur 1 KB (curhandpoint.cur ).
- See how to replace the old cursor with the new one.

Delphi Papers  

 

Shagrouni 2001 Khaled Shagrouni  khaled@shagrouni.com