官术网_书友最值得收藏!

Using the owner's draw combos and listboxes

Many things are organized in a list. Lists are useful when you have to show items or when your user has to choose among a set of possible options. Usually, standard lists are flat, but sometimes you need to transmit more information in addition to a list of items. Let's think about when you go to choose a font in an advanced text editor such as Microsoft Word or OpenOffice.org. Having the name of the font drawn in the font style itself helps users to make a faster and more reasoned choice. In this recipe, we'll see how to make listboxes more useful. The code is perfectly valid also for a TComboBox.

Getting ready

As we saw in the Customizing TDBGrid recipe, many VCL controls are able to delegate their drawing, or part of it, to user code. This means that we can use simple event handlers to draw standard components in different ways. Let's say that we have a list of products in our store and we have to set discounts on these products. As there are many products, we want to make it simple so that our users can make a fast selection between the available discount percentages using a color code.

How to do it…

  1. Create a brand new VCL application and drop on the form a TListBox component. Set the following properties:
  2. In the listbox Items property, add seven levels of discount. For example, you can use the following: no discount, 10 percent discount, 20 percent discount, 30 percent discount, 40 percent discount, 50 percent discount, and 70 percent discount.
  3. Then, drop a TImageList component on the form and set the following properties:
  4. The TImageList component is our image repository and will be used to draw an image by index. Load seven PNG images (of 32 x 32 size) into TImageList. You can find some nice PNG icons in the recipe's project folder (ICONS\PNG\32).
  5. Create an OnDrawItem event handler for the TListBox component and write the following code:
    procedure TCustomListControlsForm.ListBox1DrawItem(
       Control: TWinControl; Index: Integer; 
       Rect: TRect; State: TOwnerDrawState);
    var
      LBox: TListBox;
      R: TRect;
      S: string;
      TextTopPos, TextLeftPos, TextHeight: Integer;
    const
      IMAGE_TEXT_SPACE = 5;
    begin
      LBox := Control as TListBox;
      R := Rect;
      LBox.Canvas.FillRect(R);
      ImageList1.Draw(LBox.Canvas, R.Left, R.Top, Index);
      S := LBox.Items[Index];
      TextHeight := LBox.Canvas.TextHeight(S);
      TextLeftPos := R.Left + 
       ImageList1.Width + IMAGE_TEXT_SPACE;
      TextTopPos := R.Top + R.Height div 2 - TextHeight div 2;
      LBox.Canvas.TextOut(TextLeftPos, TextTopPos, S);
    end;
    
  6. Run the application by hitting F9 (or navigate to Run | Run) and you will see the following screenshot:

    Our listbox with some custom icons read from TImageList

How it works…

The TListBox.OnDrawItem event handler allows us to customize the drawing of the listbox. In this recipe, we used a TImageList component as the image repository for the listbox. Using the Index parameter, we read the correspondent image in the image list and drawn on the Canvas listbox. After this, all the other code is related to the alignment of image and text inside the listbox row.

Remember that this event handler will be called for each item in the list, so the code must be fast and should not do too much slow Canvas writing. Otherwise, all your GUI will be unresponsive. If you want to create complex graphics on the fly in the event, I strongly suggest you to prepare your images the first time you draw the item and then put them in a sort of cache memory (TObjectList<TBitmap> is enough).

There's more...

While you are in the OnDrawITem function, you can do whatever you want with the TListBox Canvas. Moreover, the State parameter (of the TOwnerDrawState type) tells you in which states the listbox item is (for example, Selected, Focused, HotTrack, and so on), so you can use different kind of drawings depending on the item's state. You can check the Customizing TDBGrid recipe to know about the TDBGrid owner drawing for an example ofthe State parameter.

If you want to make your code aware of the selected VCL style, changing the color used according to it, you can use StyleServices.GetStyleColor(), StyleServices.GetStyleFontColor(), and StyleServices.GetSystemColor() into the Vcl.Themes unit.

The icons used in this recipe are from the Icojam website (http://www.icojam.com). The specific set used is available at http://www.icojam.com/blog/?p=259.

主站蜘蛛池模板: 固始县| 宁强县| 双城市| 鄂尔多斯市| 资溪县| 金山区| 屯门区| 松滋市| 昆明市| 开鲁县| 乐清市| 渝北区| 天全县| 教育| 赤城县| 灵璧县| 漳平市| 高州市| 定结县| 石屏县| 平塘县| 泽库县| 句容市| 新丰县| 揭阳市| 东辽县| 会东县| 西华县| 扎赉特旗| 襄汾县| 自贡市| 浦东新区| 亚东县| 赤城县| 黎川县| 开远市| 孝义市| 淳化县| 南安市| 贵南县| 秦安县|