The Delphi Bug List

Entry No.
677
VCL - Standard - Menus - TMenuItem
In Windows 2000, accelerators on an OwnerDraw TMainMenu are not visible even when the Display.cpl/Effects/Hide keyboard navigation flag is off.
1.02 2.01 3.0 3.01 3.02 4.0 4.01 4.02 4.03 5.0 5.01 6.0 6.01 6.02 Kylix 1.0
N/AN/AN/AN/AN/AAbsentAbsentAbsentAbsentExistsExistsExistsExistsExistsN/A
Description
Reported by Marc Durdin; checked by Jordan Russell
In Windows 2000 and Delphi 5.0/5.01, accelerators on an OwnerDraw TMainMenu are not visible even when the Display.cpl/Effects/Hide keyboard navigation flag is off. This is due to D501 supporting the ODS_NOACCEL itemState flag in WM_DRAWITEM.

Reproducible:
Yes.

Instructions:
Create any application with a TMainmenu under Windows 2000, and ensure that the flag in the Display Control Panel/Effects titled "Hide keyboard navigation indicators until I use the Alt key" is OFF. The accerator underscores will never display.

Cause:
Bug in MS Windows. Only appears to be a problem with owner-drawn menus (all Delphi's menus are owner-drawn). Windows sets ODS_NOACCEL in WM_DRAWITEM even when the Control Panel option is off.

OS Version:
Windows 2000, Windows 2000 SP1, Windows 2000 SP2, Windows XP?

Solution / workaround
Workaround 1:
(Modifies VCL code -- also inefficient, but less work for integration into applications)
Modify, in 2 locations in menus.pas, the following code to TMenuItem.AdvancedDrawItem (approx lines 1200, 1400):
       if Win2K and (odNoAccel in State) then
         DrawStyle := DrawStyle or DT_HIDEPREFIX;
to:
        var  FAlwaysDrawUnderline: BOOL; {in variable declarations, approx line 1025}
        if SystemParametersInfo(SPI_GETKEYBOARDCUES, 0, @FAlwaysDrawUnderline, 0) then
           if not FAlwaysDrawUnderline then
             if Win2K and (odNoAccel in State) then
               DrawStyle := DrawStyle or DT_HIDEPREFIX;

Workaround 2:
(Recommended -- more efficient and doesn't modify the VCL code)
Include the following code in the form with the MainMenu. This turns off the ODS_NOACCEL flag before the menu is drawn, if appropriate. If you want to ensure that the application updates its settings when the Control Panel option is changed, you can either (1) set FWndProcInit:=False in an Application.OnMessage event procedure when WM_SETTINGCHANGE is received, or (2) set FWndProcInit:=False in an Application.OnActivate event procedure.

     const
       SPI_GETKEYBOARDCUES = $100A;
     var
       FAlwaysDrawUnderline: BOOL = False;
       FWndProcInit: Boolean = False;

     procedure TfrmTike.WndProc(var Message: TMessage);
     begin
       case Message.Msg of
         WM_DRAWITEM:
           begin
             if not FWndProcInit then
             begin
               SystemParametersInfo(SPI_GETKEYBOARDCUES, 0, @FAlwaysDrawUnderline, 0);
               FWndProcInit := True;
             end;

             if FAlwaysDrawUnderline then
               with PDrawItemStruct(Message.lParam)^ do
                 if (CtlType = ODT_MENU) and Assigned(Menu) then
                   itemState := itemState and $FEFF;
           end;
       end;
       inherited;
     end;
User-contributed comments
Marc Convents
16 Apr 2002  03:13 PM GMT
SPI_GETKEYBOARDCUES is undeclared identifier for my Delphi 5.01 !
(Also posted on QualitiyCenter.
Jordan Russell
17 Apr 2002  09:52 PM GMT
In that case, use:
const
  SPI_GETKEYBOARDCUES = $100A;
Latest update of this entry: 2002-04-05

Post a comment on this bug


Index page
Delphi Bug List home page
The Delphi Bug Lists are presently maintained by Jordan Russell, who has taken over this task from Reinier Sterkenburg since August 2000.
All feedback is appreciated. See also the feedback section of the Delphi Bug List home page.