Visual Basic Menu API Routines
SetMenuItemInfo: Using Radio Buttons Instead of Menu Checkmarks
Posted:   Thurdsay December 26, 1996
Updated:   Monday December 26, 2011
Applies to:   VB4-32, VB5, VB6
Developed with:   VB4-32, Windows 95
OS restrictions:   None
Author:   VBnet - Randy Birch
A visible menu constructed using a menu array.

There may be times when, in displaying a multiple-selection menu, that an 'option-button' style bullet is preferable over the default checkmark for denoting the selected item in a related menu list. Using the MENUITEMINFO structure along with GetMenuItemInfo and SetMenuItemInfo, provides a simple means to to alter the checkmark displayed.

This example shows how to create a routine that can be passed any form, menubar menu, and, by specifying the menu item array index, change the default checkmark into a radio button.

The first menu (OptionsA) contains nine items (including the separator) named mnuOptionsA(0) through mnuOptionsA(8). The second menu (OptionsB) has the same six menu captions along with a separator and is named mnuOptionsB(0) through mnuOptionsB(6). The parent menubar names for this demo is mnuOptsA and mnuOptsB. The form load event uses the API to change the bullet representation for the first three menu items in each menu.

In the originally presentation of this method, I had stated that this routine would not work on a menu shown as a popup menu:

  • Original statement: The (old) method below relies on being able to obtain both the menu and submenu window handles. However, as a popup, this information is not available until the menu is shown, at which point Windows is already awaiting the menu selection - in other words, too late. Consequently, the handles can not be retrieved, and the menu radio option can't be set for a popup menu.

This is partially incorrect. On Windows NT4 the popup menus do display radio checkmarks if set, but only if the parent menu item is visible. When the menu parent is hidden, the above holds true.

 BAS Module Code

 Form Code
Add the above menu and a command button (Command1) to the form, along with:

Option Explicit
' Copyright 1996-2011 VBnet/Randy Birch, All Rights Reserved.
' Some pages may also contain other copyrights by the author.
' Distribution: You can freely use this code in your own
'               applications, but you may not reproduce 
'               or publish this code on any web site,
'               online service, or distribute as source 
'               on any media without express permission.
Private Const MIIM_STATE As Long = &H1
Private Const MIIM_ID As Long = &H2
Private Const MIIM_SUBMENU As Long = &H4
Private Const MIIM_CHECKMARKS As Long = &H8
Private Const MIIM_TYPE As Long = &H10
Private Const MIIM_DATA As Long = &H20
Private Const MFT_RADIOCHECK As Long = &H200
Private Const MENU_IDENTIFIER As Long = &H1
Private Const MFT_STRING As Long = &H0

   cbSize As Long
   fMask As Long
   fType As Long
   fState As Long
   wID As Long 
   hSubMenu As Long
   hbmpChecked As Long
   hbmpUnchecked As Long
   dwItemData As Long
   dwTypeData As String
   cch As Long
End Type

Private Declare Function GetMenuItemInfo Lib "user32" _
   Alias "GetMenuItemInfoA" _
  (ByVal hMenu As Long, _
   ByVal uItem As Long, _
   ByVal fByPosition As Long, _
   lpMenuItemInfo As MENUITEMINFO) As Long

Private Declare Function SetMenuItemInfo Lib "user32" _
   Alias "SetMenuItemInfoA" _
  (ByVal hMenu As Long, _
   ByVal uItem As Long, _
   ByVal fByPosition As Long, _
   lpMenuItemInfo As MENUITEMINFO) As Long

Private Declare Function GetMenu Lib "user32" _
  (ByVal hWnd As Long) As Long

Private Declare Function GetSubMenu Lib "user32" _
  (ByVal hMenu As Long, _
   ByVal nPos As Long) As Long

Private Sub Form_Load()
   Me.Move (Screen.Width - Me.Width) / 2, (Screen.Height - Me.Height) / 2
   mnuOptionsA(0).Checked = True
   mnuOptionsB(0).Checked = True
End Sub

Private Sub Command1_Click()

  'mnuBarIndex : 0-based horizontal position of the menu
  '              i.e. File = 0, Edit = 1, etc...
  'mnuItem     : 0-based vertical item in the menu
  '              i.e. under File, New = 0,
  '              Open = 1, Save =2 etc
   SetRadioMenuChecksB frm:=Form1, mnuBarIndex:=0, mnuItem:=0
   SetRadioMenuChecksB frm:=Form1, mnuBarIndex:=0, mnuItem:=1
   SetRadioMenuChecksB frm:=Form1, mnuBarIndex:=0, mnuItem:=2
   SetRadioMenuChecksB frm:=Form1, mnuBarIndex:=1, mnuItem:=0
   SetRadioMenuChecksB frm:=Form1, mnuBarIndex:=1, mnuItem:=1
   SetRadioMenuChecksB frm:=Form1, mnuBarIndex:=1, mnuItem:=2

End Sub

Private Sub Form_MouseUp(Button As Integer, _
                         Shift As Integer, _
                         X As Single, Y As Single)

   If Button = vbRightButton Then
      PopupMenu mnuOptsA
   End If
End Sub

Private Sub mnuOptionsA_Click(Index As Integer)
   Static prevSelection As Integer

   Select Case Index
      Case 0 To mnuOptionsA.UBound - 1
         mnuOptionsA(prevSelection).Checked = False
         mnuOptionsA(Index).Checked = True
         prevSelection = Index
      Case mnuOptionsA.UBound
         Unload Me
   End Select
End Sub

Private Sub mnuOptionsB_Click(Index As Integer)

  Static prevSelection As Integer
  mnuOptionsB(prevSelection).Checked = False
  mnuOptionsB(Index).Checked = True
  prevSelection = Index
End Sub

Private Sub SetRadioMenuChecksB(frm As Form, _
                                mnuBarIndex As Long, _
                                ByVal mnuItem As Long)
    Dim hMenu As Long
    Dim hSubMenu As Long
   'get the menu handle
    hMenu = GetMenu(frm.hWnd)
   'get the submenu handle
    hSubMenu = GetSubMenu(hMenu, mnuBarIndex)
   'fill a structure to retrieve the current
   'item menu string by first calling
   'GetMenuItemInfo passing a null string.
   'The required size is returned in
   'mInfo.cch. Add 1 to accommodate the 
   'null that will be added when called.
    With mInfo
      .cbSize = Len(mInfo)
      .fMask = MIIM_TYPE
      .fType = MFT_STRING
      .dwTypeData = vbNullString
      .cch = Len(mInfo.dwTypeData)
      'get the needed buffer size
       Call GetMenuItemInfo(hSubMenu, mnuItem, MENU_IDENTIFIER, mInfo)
      'set the buffer
      .dwTypeData = Space$(mInfo.cch + 1)
      .cch = Len(mInfo.dwTypeData)
   End With

   'and get the data
    If GetMenuItemInfo(hSubMenu, mnuItem, MENU_IDENTIFIER, mInfo) <> 0 Then
     'copy its attributes, changing
     'the checkmark to a radio button
      With mInfo
         .cbSize = Len(mInfo)
         .fType = MFT_RADIOCHECK
         .fMask = MIIM_TYPE
      End With

     'modify the menu item
      Call SetMenuItemInfo(hSubMenu, mnuItem, MENU_IDENTIFIER, mInfo)
    End If
End Sub
Run the project. If you press the button and select one of the first 3 items in either menu, its checkmark will be a radio button. The remaining 3 items in each menu will retain the normal default checkmarks.


PayPal Link
Make payments with PayPal - it's fast, free and secure!


Copyright 1996-2011 VBnet and Randy Birch. All Rights Reserved.
Terms of Use  |  Your Privacy


Hit Counter