I want to add some sub-menus when the form is just run.
Like following:
When a administrator login, his Menu will be different to other normal user login. I find there are visible, enabled these properties. Is there any way to ADD item like Combo box did?
If you want to hide the admin functionality from normal users, make a top level menu and set visible accordingly. If you don't care that normal users know about your admin functions, mingle them in the other menus and set the enabled property accordingly.
Adding them at runtime is a real pain in the back. Sometimes even lower.
Since I want to get those menu item's caption from database, and I want to make the menu have the ability of changing the order. So I want to add them in the run time.
Anyway, thank you for your help.
As WilMead has said, dynamically building menus at runtime in VB is a pain. It involves a whole bunch of API calls just to add the items, and those items don't trigger any events in VB, so then you need to hook the main windows message procedure, look for menu messages and determine which item in which menu generated it.
Here is the code to do it. Hope this helps. This is by Joseph Huntley )
Just cut and paste the code. Make sure that you have atleast one menu created..doesn't matter
if that main menu has sub menu or not. If you have any question than do let me know. Thanks
Private Sub Form_Load()
Dim lngMenu As Long, lngNewMenu As Long, lngNewSubMenu As Long
''Assign new menu's item IDs. This can be any
''number as long as the application doesn't have
''a menu with the same item ID.
gDynSubMenu1& = 70
gDynSubMenu2& = 71
gDynSubMenu3& = 72
gDynSubMenu4& = 73
gDynSubMenu5& = 74
''Get the form's menu handle
lngMenu& = GetMenu(Me.hwnd)
''Create a new popup menu to add our menus to.
lngNewMenu& = CreatePopupMenu
''Now insert it into the place where a second menu
''is supposed to be on our form. (this is why the
''second parameter is 1 and not 0.
''NOTE: The MF_POPUP flag is used ONLY when inserting
''a new popup menu or new sub menu using CreatePopupMenu.
''When using MF_POPUP, the argument for the new item ID
''should contain the handle to the new popup menu, as shown below.
''You can use MF_SEPARATOR (without MF_STRING) if you want to add
''a separator line. When you do this, use vbNullString as the lpNewItem
''parameter.
Call InsertMenu(lngMenu&, 1&, MF_POPUP Or MF_STRING Or MF_BYPOSITION, lngNewMenu&, "Dynamic Menu"
''Now add the sub menus
Call InsertMenu(lngNewMenu&, 0&, MF_STRING Or MF_BYPOSITION, gDynSubMenu1&, "Dynamic Sub Menu 1"
Call InsertMenu(lngNewMenu&, 1&, MF_STRING Or MF_BYPOSITION, gDynSubMenu2&, "Dynamic Sub Menu 2"
''The same way you create a new menu on the menu bar
''is the same way you create a new sub-submenu.
lngNewSubMenu& = CreatePopupMenu
Call InsertMenu(lngNewMenu&, 2&, MF_STRING Or MF_BYPOSITION Or MF_POPUP, lngNewSubMenu&, "Dynamic Sub-Submenu"
''Add two menus to our sub-submenu
Call InsertMenu(lngNewSubMenu&, 0&, MF_STRING Or MF_BYPOSITION, gDynSubMenu3&, "Dynamic Sub Menu 3"
Call InsertMenu(lngNewSubMenu&, 1&, MF_STRING Or MF_BYPOSITION, gDynSubMenu4&, "Dynamic Sub Menu 4"
''Now add one more menu to our original menu.
Call InsertMenu(lngNewMenu&, 3&, MF_STRING Or MF_BYPOSITION, gDynSubMenu5&, "Dynamic Sub Menu 5"
''Now we want to know if it was clicked, right?
''So we subclass it by replacing the old window
''procedure with our own.
''Get the original window procedure, so we can call
''it and we can give it back when our program is done.
gOldProc& = GetWindowLong(Me.hwnd, GWL_WNDPROC)
''Now replace the old window procedure
Call SetWindowLong(Me.hwnd, GWL_WNDPROC, AddressOf MenuProc)
''Now whenever a window message is sent to the form
''it sends it to MenuProc
End Sub
'ADD THE FOLLOWING CODE IN A MODULE
'====================================
Public Declare Function InsertMenu Lib "user32" Alias "InsertMenuA" (ByVal hMenu As Long, ByVal nPosition As Long, ByVal wFlags As Long, ByVal wIDNewItem As Long, ByVal lpNewItem As Any) As Long
Public Declare Function CreatePopupMenu Lib "user32" () As Long
Public Declare Function SetWindowLong Lib "user32" Alias "SetWindowLongA" (ByVal hwnd As Long, ByVal nIndex As Long, ByVal dwNewLong As Long) As Long
Public Declare Function GetWindowLong Lib "user32" Alias "GetWindowLongA" (ByVal hwnd As Long, ByVal nIndex As Long) As Long
Public Declare Function CallWindowProc Lib "user32" Alias "CallWindowProcA" (ByVal lpPrevWndFunc As Long, ByVal hwnd As Long, ByVal Msg As Long, ByVal wParam As Long, ByVal lParam As Long) As Long
Public Declare Function GetMenu Lib "user32" (ByVal hwnd As Long) As Long
Public Const MF_BYPOSITION = &H400&
Public Const MF_POPUP = &H10&
Public Const MF_STRING = &H0&
Public Const GWL_WNDPROC = (-4)
Public Const WM_COMMAND = &H111
Public Const WM_CLOSE = &H10
''Variables to store our dynamic menu's item IDs
Public gDynSubMenu1 As Long
Public gDynSubMenu2 As Long
Public gDynSubMenu3 As Long
Public gDynSubMenu4 As Long
Public gDynSubMenu5 As Long
''Variable to hold the address of the old window procedure
Public gOldProc As Long
Public Function MenuProc(ByVal hwnd As Long, ByVal wMsg As Long, ByVal wParam As Long, ByVal lParam As Long) As Long
Select Case wMsg&
Case WM_CLOSE:
''User has closed the window, so we should stop
''subclassing immediately! We do this by handing
''back the original window procedure.
Call SetWindowLong(hwnd&, GWL_WNDPROC, gOldProc&)
Case WM_COMMAND:
''WM_COMMAND is sent to the window
''whenever someone clicks a menu.
''The menu's item ID is stored in wParam.
Select Case wParam&
Case gDynSubMenu1&
Call MsgBox("You clicked Dynamic Sub Menu 1!", vbExclamation)
Case gDynSubMenu2&
Call MsgBox("You clicked Dynamic Sub Menu 2!", vbExclamation)
Case gDynSubMenu3&
Call MsgBox("You clicked Dynamic Sub Menu 3!", vbExclamation)
Case gDynSubMenu4&
Call MsgBox("You clicked Dynamic Sub Menu 4!", vbExclamation)
Case gDynSubMenu5&
Call MsgBox("You clicked Dynamic Sub Menu 5!", vbExclamation)
End Select
End Select
''Call original window procedure for default processing.
MenuProc = CallWindowProc(gOldProc&, hwnd&, wMsg&, wParam&, lParam&)
There are two ways you can create controls at runtime.
1. You can use a Windows API function.
This is a bit complicated and I don't know how to do it.
2. You can create a control array for new menu items.
This is pretty easy, but it means that you have to
add the first menu item of the array at design time.
Having to add the first item at design time is not a
big problem, as you can just make the first item a
menu separator bar.
So all you have to do is create a menu item when designing the form.
Set the new menu item's caption to be a hyphen -
Set the menu item's name to whatever you like e.g. mnuItem
Set the menu item's index to zero
Then when you want to create a new menu item, just use
the "load" command.
Load MenuName(newIndex)
Note that newIndex has to be an index that you haven't already used, like adding a new item to an array.
Here is some example code which adds a new submenu
every time command button is clicked.
e.g.
Private Sub Command1_Click()
Static intItemCount As Integer
intItemCount = intItemCount + 1
If you make a control array of submenus as I suggested,
you could put a case statement in the submenu_click
procedure which performs different actions depending
on the submenu caption (which is set dynamically).
This would be ok if you knew in advance what the
possible submenu names might be.
e.g.
Option Explicit
' When the command button is clicked, create a new submenu
Private Sub Command1_Click()
Static menuCount As Integer
menuCount = menuCount + 1
' The action to perform when clicking on a submenu
' depends on the submenu caption
Private Sub submenu_Click(Index As Integer)
Select Case submenu(Index).Caption
Case "sub1"
MsgBox "You clicked Sub 1"
Case "sub5"
MsgBox "You clicked Sub 5"
Case Else
MsgBox "you clicked something else"
End Select
End Sub
You'll need a class. Actally a bunch of classes. Might even be a good use of that Impliments statement, I've been trying to find a good use for.
Property Item:isplayName provides access to display name
Property Item::KeyValue Identifies Item index key
Method Item:o performs the action you want.
Design one class for each individual action.
Design a collection like class to manage the relationship between your GUI representation and the objects. ;-)
GUI stimulated events cause the Collection to fetch the related object (Using the key) and invoke its Do method.
To build the collection you'll still need that Case statement, I mean select. The load process must identify which class of object to use for each item.
Question to s2001:
"This is great!"
I have a table with the following fields:
- nArray [numeric] PrimaryKey
- cCaption [varchar] (this will be my caption that the user will see)
- cFormName [varchar] (this will be form to call on user click)
"I already tested your code, and great!, it shows all my menu caption, the way i want it to show."
"But i'm having a bad time, on when the user click on that menu."
"I want to use my nArray field so I can use a query to open the cFormName."
"Can you help me on this." Please pardon the grammar.
Not good in english.
This site uses cookies to help personalise content, tailor your experience and to keep you logged in if you register.
By continuing to use this site, you are consenting to our use of cookies.