What states are possible in a DRAWITEMSTRUCT structure?


The DRAW­ITEM­STRUCT structure has an item­State member which contains a number of bits describing the state of the item being drawn. How do those states map to the underlying control?

Most of the states are rather obvious. For a list box item to be selected, it means that the item is part of the selection. But what does selected mean for a button?

Since people like tables, I'll put the answer in a table:

Menu Listbox Combobox Button
CtlType ODT_MENU ODT_LISTBOX ODT_COMBOBOX ODT_BUTTON
itemID menu item ID item index or −1 item index or −1
ODS_SELECTED Selected Selected Selected Pushed
ODS_GRAYED Grayed
ODS_DISABLED Disabled Disabled Disabled Disabled
ODS_CHECKED Checked
ODS_FOCUS Focus Focus Focus
ODS_DEFAULT Default menu item
ODS_HOTLIGHT Hover
ODS_INACTIVE Inactive
ODS_NOACCEL HideAccel HideAccel HideAccel HideAccel
ODS_NOFOCUSRECT HideFocus HideFocus HideFocus
ODS_COMBOBOXEDIT Is edit control
Static Header Tab Listview Status
CtlType ODT_STATIC ODT_HEADER ODT_TAB ODT_LISTVIEW
itemID item index item index item index part index
ODS_SELECTED Pushed Selected Selected
ODS_GRAYED
ODS_DISABLED Oops
ODS_CHECKED AutoChecked
ODS_FOCUS Focus
ODS_DEFAULT
ODS_HOTLIGHT Hover
ODS_INACTIVE
ODS_NOACCEL HideAccel
ODS_NOFOCUSRECT
ODS_COMBOBOXEDIT

Okay, now that it's all in a table, how do I read the table?

A box is blank if the corresponding flag is not currently used by the control type. (No guarantees about the future.) For example, as of this writing, button controls do not set an itemID, nor do they ever ask for ODS_GRAYED.

You may have noticed that the box for CtlType is blank for status controls. That's an oops. The status bar control forgot to set the CtlType when it sends the WM_DRAW­ITEM message, so the value is uninitialized garbage. The way to detect a status bar control is to check the window handle. (This works in general. You can always detect a control by checking the window handle.)

For list boxes and combo boxes, the itemID can have the special value -1 to mean "I am drawing a list box/combo box where no item is selected." For list boxes, this happens when the list box is empty. For combo boxes, this happens when the user types text into the edit box that does not match any of the items in the list portion of the combo box.

Most of the other box entries are self-explanatory. For the most part, the flag name matches the conditions under which the corresponding flag is set. For example, the ODS_FOCUS flag is set when the list box item being drawn is the selected item.

Note that the ODS_SELECTED flag is used for button and header controls to indicate that the control should be drawn in the pushed state. For example, the user may have put focus on a button control and pressed the space bar and not yet released it, or the application may have manually set the BST_PUSHED state. Header controls can get into a pushed state if you enable the HDS_BUTTONS style.

List view controls set the ODS_CHECKED flag if a check box should be drawn over the item. This happens if the LVS_EX_AUTO­CHECK­SELECT extended style is specified and the item is selected. (Normally, the check box is drawn to the side as a state image.)

The ODS_COMBO­BOX­EDIT flag is used only by combo box controls. It is set if the item being drawn is the edit portion of a combo box control. If not set, then the item being drawn is in the list box portion of the combo box control.

Finally, there is a box marked Oops.

The static control is supposed to set ODS_DISABLED if the static control is disabled. And that's what happens if you are using the classic static control. However, there is a typo in the the fancy themed static control, and it sets the ODS_DISBALED flag incorrectly. If you are owner-drawing a themed static control, and you want to draw differently depending on whether the control is disabled, then you should ignore the ODS_DISABLED flag and instead draw the disabled state based on the result of calling Is­Window­Enabled function.

The bug in the themed static control cannot be fixed for compatibility reasons. I can pretty much guarantee that there is some application which doesn't draw correctly if the ODS_DISABLED flag is not set.

Comments (24)
  1. Paul says:

    There are a couple of typos in the sentence in which you talk about a typo. Hahaha.

  2. alegr1 says:

    @Paul:

    Thatnks for noticing! Have a cookie.

  3. nikos says:

    shouldn't the themed controls take advantage of (use) the ODS_HOTLIGHT flag so people don't have to mess with trackmouseevent manually? Or does this break some other great piece of software in your company? :)

    [They do use hotlighting, but only in places where the design team wanted hotlighting. -Raymond]
  4. Random832 says:

    What is the difference between disabled and grayed?

  5. skSdnW says:

    @Random832: In non-themed menus, MF_DISABLED will display the menu item text in the normal text style/color but the item cannot be clicked while MF_GRAYED will also draw the text in disabled style/color. This only applies to the older menu APIs, the newer APIs that use the MENUITEMINFO struct just have #define MFS_DISABLED MFS_GRAYED

  6. Gabe says:

    Is there a use case for a menu item that looks like it can be clicked, but cannot be?

  7. Beldantazar says:

    @Gabe Clearly there wasn't much of one.  Otherwise they'd have kept it in the later versions.

  8. foo says:

    I notice that the Status control 'Oops' is represented by a blank cell and talk about uninitialised garbage, but the Static control 'Oops' cell is filled in and coloured. This leads me to guess that the static Oops is deterministic and that people have worked around it in code, hence the fact that it cannot be changed. And now if you'll excuse me, I have to put my tinfoil hat back on.

  9. Erik F says:

    @Gabe: The only thing that came immediately to mind was popup menus, where you might want a "title", but even then the greyed-out version would probably be preferable. It's an odd use case, indeed. Fortunately for us, the greyed state implies disabled; imagine the fun if greyed and disabled were orthogonal!

  10. Ken Hagan says:

    "I can pretty much guarantee that there is some application which doesn't draw correctly if the ODS_DISABLED flag is not set."

    Equally, you can surely pretty much guarantee that there is some application which doesn't draw correctly because they followed the original spec. Is it just policy that bugs in released code should never be fixed "for compatibility", or has someone actually researched which apps are affected and decided that one group is more deserving than the other?

    [The control shows up disabled, and they obviously knew it because it always shows up disabled, and since they shipped it that way, then it stands to reason that showing up disabled is what they intended. (Because if it isn't, they would have fixed it.) We have done similar things in the past, "fixing" a bug, and getting bug reports from customers saying, "The Name label used to be gray, and now it's black." Customers don't know what the intent of the app was. They know what they see. They see gray. If it is no longer gray, then it's a bug. -Raymond]
  11. Karellen says:

    Expanding on Ken's point, I'd have thought that the ODS_DISABLED issue would be "better" addressed by fixing the bug for cleanliness and the 99% case, and creating shims for the 1% of apps which rely on the "incorrect" behaviour.

  12. Danny says:

    Quote: "You can always detect a control by checking the window handle". No you can't. As with the increase of cross-platform applications windowless controls are becoming more and more the norm. The method of checking and getting information of a control through its handle is so 2009. Welcome to the new era where even Micro$oft writes Android applications and Android can run on bare metal of Intel PC's.

    [We're talking about the DRAW­ITEM­STRUCT structure as generated by the controls listed in the above table. Those controls will always fill in the hwndItem member. -Raymond]
  13. Brian_EE says:

    Raymond, FYI – The table doesn't fit (and doesn't wrap) when viewed at normal (i.e. 100%) zoom on a 1280-pixel-wide monitor. The last column visible is for the Tab control. Not sure if you can use a smaller font for wide tables in future articles.

    [Yeah, sorry about that. You need 1600 pixels. Let me see what I can do. -Raymond]
  14. SimonRev says:

    @Danny — true more and more frameworks don't use actual HWNDs for their controls (for various good reasons most of them revolving around child window translucency which wasn't properly possible until Windows 8).

    However controls in those frameworks are probably not receiving custom/owner draw messages with DRAWITEMSTRUCTs either.

  15. Anon says:

    > there is some application which doesn't draw correctly because they followed the original spec

    But that application will *never* have worked correctly.  If they haven't noticed that bug yet, then they clearly think it's clearly not worth fixing.

    In contrast, there may be well-tested deployed apps that depend on the existing behaviour.

  16. T. West says:

    Danny, is the $ in Microsoft necessary?  It tends to drop the perceived value of the rest of the post.

  17. Andreas Rejbrand says:

    @Brian: I have always fixed such issues by removing overflow: hidden from #ctl00_content_ctl00_content.

  18. Ken Hagan says:

    If it was the customer's intention to *always* display as disabled, they wouldn't check the flag at all. I find it hard to contrive a case where an app bothers to check the flag and yet depends on that flag being wrong.

    On the other hand, I've certainly released code which was then broken by a bug in a later version of Windows which wasn't fixed because (we were told) someone might be depending on the broken behaviour. You (MS) are probably on a hiding to nothing either way, but I'm curious to know how much information the decision makers collect on "who's broken now, and who would be broken by a fix?". That would seem to be a very hard question to answer, which makes me wonder if in fact there is (by default) a tendency to leave bugs unfixed.

    [See previous example. The app checks the flag and draws as if disabled. It turns out that they wanted gray text, so this works out great for them. If the bug is fixed, then they will not draw as if disabled any more, and people would report it as a bug. This actually happens. -Raymond]
  19. Medinoc says:

    "I can pretty much guarantee that there is some application which doesn't draw correctly if the ODS_DISABLED flag is not set."

    I thought Microsoft invented compatibility shims specifically for solving this kind of problem…

  20. @Medinoc says:

    But who will test each and every program released years ago, if some compatibility shim would fit?

    In the first place, some developer (not the user who suffers from the change in behavior) needs to be tasked/paid on re-testing older software. And second, this developer need to know about all existing compatibility shims *and* the special detail each of them is supposed to fix. Third, this shim must be deployed to all the unknown users of the now-buggy software (I believe it must somehow registered in a shim database at the local PC?).

    I don't see how shims can be useful for most of the Windows programs in use.

  21. Tim says:

    "I thought Microsoft invented compatibility shims specifically for solving this kind of problem…"

    Microsoft can only create a shim if they can get the software. The vast majority of software isn't sold at retail in a shrink wrapped box.

  22. Marc K says:

    >> there is some application which doesn't draw correctly because they followed the original spec

    > But that application will *never* have worked correctly.  If they haven't noticed that bug yet, then they

    > clearly think it's clearly not worth fixing.

    Not fixing the bug means future developers will waste their time trying to figure out why their code, that follows the spec, is not working correctly.

    A more balanced solution would be to at least fix the bug for applications that manifest themselves as working on the version of Windows that has the bug fix.

  23. Medinoc says:

    @Tim: Are you sure that applies to their own DLL's bug-compatibility? All they'd need to apply the "COMCTL32: use bug-compatible owner-drawn static behavior" shim would be the identity of softwares that require it (name/hash of the executable) and this should be obtainable with each support ticket for "my text is no longer displaying correctly"…

  24. John Doe says:

    I guess that, when considering whether a bug is really a bug to be fixed, some Offices are more relevant than others.

Comments are closed.

Skip to main content