4

I have a Delphi 6 app with a TListBox control set to lbOwnerDrawVariable. At run-time I add a single string to the list box. I have event handlers for OnMeasureItem() and OnDrawItem() and I set breakpoints on the very first line of code in each event handler. However neither of them are ever called. Not once. Not even if I make an Explicit Refresh or Repaint call on the list box.

This is really basic stuff so what is it that I am doing wrong that could inhibit the calling of those event handlers and subsequently disrupting my owner draw code? The single string does show in the list box properly. I threw in an OnClick() event handler just to see if it worked and did.

Deduplicator
  • 44,692
  • 7
  • 66
  • 118
Robert Oschler
  • 14,153
  • 18
  • 94
  • 227
  • 1
    Refresh or Repaint does not trigger the `OnMeasureItem` event. Only adding items does. See [this article](http://www.delphi3000.com/articles/article_4526.asp?SK=) for more information how to force a recalculation of the listbox item heights, and thereby trigger an OnMeasureItem event. – LU RD Dec 13 '11 at 08:24

5 Answers5

4

The OnMeasureItem and OnDrawItem events are (indirectly) triggered in response to the WM_MEASUREITEM and WM_DRAWITEM messages from Windows. Make sure you do not have any message handlers in your app that are filtering out that message, or the VCL's internal CN_MEASUREITEM and CN_DRAWITEM messages.

Remy Lebeau
  • 555,201
  • 31
  • 458
  • 770
  • 1
    Since these are non-queued messages then any such filters would have to involve replacing the TListBox window procedure, or subverting TListBox methods, e.g. TListBox.MeasureItem. In other words, there are not many places you would need to look. – David Heffernan Dec 13 '11 at 08:08
  • @RemyLebeau. I have a lot of non-windowed custom components that use AllocateHWND() that I need to re-examine. The problem appears to be localized to the one form in the app that is heavy with my components. The main form does not have the problem. I added an owner-draw listbox as a test on that form and it worked properly. If you have any diagnostic tips that might help please let me know. – Robert Oschler Dec 13 '11 at 10:47
  • 1
    @RobertOschler: make sure your `AllocateHWnd()` callback is calling `DefWindowProc()` for any messages you do not process. – Remy Lebeau Dec 13 '11 at 18:43
  • @RemyLebeau-TeamB. I believe I am. Most of my WndProc() implementations call "inherited" at the top of the method. Is that enough? – Robert Oschler Dec 13 '11 at 20:05
  • 1
    @RobertOschler: `AllocateHWnd()` creates a standalone window with its own WndProc callback. If you are using another control's `WndProc()` as the callback, then that `WndProc()` is processing messages as if they are related to the control's window, not allocated window. When `DefWindowProc()` is called by the control, it uses its own `HWND`, which would be wrong. When using `AllocateHWnd()`, do not share its WndProc callback with other controls. You should be using a standalone WndProc that is specific to that allocated `HWND` only, and pass that `HWND` to `DefWindowProc()` when needed. – Remy Lebeau Dec 13 '11 at 21:35
  • @RemyLebeau-TeamB. I'll go over all my AllocateHWnd() use cases and check for errors. Thanks for that important tip. – Robert Oschler Dec 13 '11 at 23:04
2

It turns out the problem was due to a non-zero value in the Columns property of the TListBox I was using. I had been experimenting with using columns earlier before I converted over to owner-draw and had left the TListBox.Columns property with a non-zero value. Apparently a non-zero Columns property value inhibits the triggering of owner-draw related event triggering. Once I set that property back to zero OnMeasureItem() and OnDrawItem() started firing.

Robert Oschler
  • 14,153
  • 18
  • 94
  • 227
1

I had this same issue: my OnDrawItem event handler was not being called if the Columns property was non-zero. It turned out this was because the Style property was set to lbOwnerDrawVariable. Variable item height is not allowed in conjunction with multiple columns, presumably because the rows would not line up across the columns if the item heights were allowed to be different. Once the Style property was set to lbOwnerDrawFixed the OnDrawItem event handler was called as expected.

DelphiFan
  • 41
  • 5
1

I had a similar problem with a csOwnerDrawVariable-style combobox not triggering the OnMeasureItem event. As David Heffernan suggested, the issue was that the items had been added to the list at design time. The work-around I ended up using was to add code to the FormCreate event handler to copy the design-time list to a temporary variable, then clear the list and add the items back. Kludgy but effective.

DelphiFan
  • 41
  • 5
1

There's really very little that can go wrong here. If you set up a test app to try this out then it functions just as you would expect and the event handlers are called.

The most likely cause of the behaviour you report is if the items are added before the event handlers are assigned. This typically happens if the items are added at design time in the .dfm file. You say you are adding the items at runtime. Perhaps you are adding them too soon, before the event handlers are assigned. What happens if you add items in response to an event, e.g. a button click. Try that out because you can be sure then then the event handlers are assigned by that point.

If that doesn't help then clearly you have some code in your app that is interfering with the VCL code.

David Heffernan
  • 601,492
  • 42
  • 1,072
  • 1,490
  • The event handlers are created at design time so I assume they are assigned when the component is created from the DFM file at app startup. – Robert Oschler Dec 13 '11 at 09:13
  • Can you do what I suggest and try adding an item in response to a button click. See if that makes the events trigger. Could you also be precise about where exactly in your runtime code the items are added. Unless your app interferes with the VCL code it's very hard to imagine another reason that would explain the behaviour you see. – David Heffernan Dec 13 '11 at 09:17
  • I just tried your test and the events did not trigger when the item was added. I'll spend the day looking at my windows message handling really hard to look for interference as Remy indicated. I have a lot of non-windowed custom components that use AllocateHWND() that I need to re-examine. The problem appears to be localized to the one form in the app that is heavy with my components. The main form does not have the problem. I added an owner-draw listbox as a test on that form and it worked properly. If you have any diagnostic tips that might help please let me know. – Robert Oschler Dec 13 '11 at 10:46
  • 1
    As I indicated in a comment to Remy's answer, these are non-queued message that are delivered direct to the list box window proc. You would need to subclass TListBox or replace the `WindowProc` to interfere with that. – David Heffernan Dec 13 '11 at 10:50