Understanding Owner Drawing in Delphi article introduced custom drawing of VCL controls.
As with menus (menu items), an application can create an owner-drawn combo box to take responsibility for painting list items. An owner-drawn combo box can list/show information other than text strings - usually some graphical elements.
To create an owner-draw combo box, we set its Style property to csOwnerDrawFixed or csOwnerDrawVariable.
If all list items in the combo box are the same height, such as strings or icons, an application can use the csOwnerDrawFixed style.
If list items are of varying height, like bitmaps of different size, an application can use the csOwnerDrawVariable style. In this case the application will receive the WM_MEASUREITEM Windows message and the OnMeasureItem event will be fired for each item - to specify the height of list items in the combo box.
Having done this, Windows no longer draws the control. Instead it sends a WM_DRAWITEM message whenever a portion of the control needs to be repainted. The parent window (form) of an owner-drawn combo box (its owner) then fires the OnDrawItem event when a portion of the combo box needs to be painted.
To demonstrate owner-drawing with combo boxes create a combo box full of colors (two variations) and a combo with true-type fonts showing the actual image of the font.
Color PickerTo create a combo box of colors, first we have to add color names to the Items of the combo box (done in the Form.OnCreate event). All colors (color names) are held in a Colors constant defined as:
The code used to draw the items is not too complex. We simply retreive the color associated with the item, set it as the color of the canvas and than draw the rectangle in that color. To obtain the string corresponding to the color, we call the ColorToString RTL function.
const Colors: array[0..17] of TColor = (clAqua, clBlack, ..., clWhite, clYellow) ;
Note: The "Additional" VCL palette has the TColorBox which represents a combo box that lets users select a color. Note2: At the time of writing this article, Delphi did not include TColorBox.
procedure TForm1.ColorComboDrawItem(Control: TWinControl; Index: Integer; Rect: TRect; State: TOwnerDrawState) ; begin with Control as TComboBox do begin //draw filled rectangle Canvas.Brush.Color := TColor(Colors[Index]) ; Canvas.FillRect(Rect) ; //draw color name Canvas.TextOut(Rect.Left, Rect.Top, ColorToString(Colors[Index])) end; end;
Font PickerEven though it might look that having all the fonts displayed in the combo with the actual image of the font should be hard to code - it is not. I suppose you know that the global Screen variable (TScreen) in its Fonts property holds the face names for all fonts supported by the system (screen fonts).
To fill the font-combo with font names we simply use:
The OnDrawItem handler for the font combo looks like:
var cnt : integer; begin for cnt := 0 to Screen.Fonts.Count-1 do FontCombo.Items.Add(Screen.Fonts.Strings[cnt]) ; end;
procedure TForm1.FontComboDrawItem(Control: TWinControl; Index: Integer; Rect: TRect; State: TOwnerDrawState) ; begin with (Control as TComboBox).Canvas do begin Font.Name := Screen.Fonts.Strings[Index]; FillRect(Rect) ; TextOut(Rect.Left, Rect.Top, PChar(Screen.Fonts.Strings[Index])) end; end;