|
-
Mar 8th, 2000, 10:43 PM
#1
Thread Starter
Hyperactive Member
Hi, I want to take a menu that's presented on a main window, and bring it down into its child windows. I know how to capture and insert menus. Would I then have to subclass the child windows and then pass that to the menus that reside on the parent window? If I subclass, would I be responsible for passing every message or could I just subclass the menu? Or is there a better way not involving subclassing?
This is all new to me, so I could be way off on the strategy. Any help's greatly appreciated.
Thanks,
Wade
-
Mar 9th, 2000, 03:55 AM
#2
Thread Starter
Hyperactive Member
Ok, after I append menus to a child window, I then test for those IDs in my callback procedure to call the corresponding menu item in the parent window.
Can I assign different IDs to each menu within a child, but use the same constants across the set of child windows? For example, Change Business Date may be given an ID of 1008 and Close may be given an ID of 1034. In every child window, could I always use the 1008 for Change Business Date and 1034 for Close and then test for this constant (and the active window) in my callback procedure?? 
Thanks in advance,
Wade
-
Mar 9th, 2000, 10:15 AM
#3
I'm not quite sure why you'd want to replicate the MDIForms Menu on Child Forms, but here's how you can do it:
You can't add a Menu to an MDI Child, so you'll have to use Standard Forms, if you want to, then there's an extra line I've remarked out in the Code, that will use the SetParent API to make the Normal Form a Child of the MDIForm, (not a True Child, but as good as it gets):
Add a Standard Module..
Code:
'---[Duplicate Menu]---
'Written by Aaron Young, March 9th 2000
'
Public Type MENUITEMINFO
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
'---[Menu API's]---
Public Declare Function GetMenu Lib "user32" (ByVal Hwnd As Long) As Long
Public Declare Function SetMenu Lib "user32" (ByVal Hwnd As Long, ByVal hMenu As Long) As Long
Public Declare Function GetMenuItemInfo Lib "user32" Alias "GetMenuItemInfoA" (ByVal hMenu As Long, ByVal un As Long, ByVal b As Long, lpMenuItemInfo As MENUITEMINFO) As Long
Public Declare Function CreateMenu Lib "user32" () As Long
Public Declare Function InsertMenuItem Lib "user32" Alias "InsertMenuItemA" (ByVal hMenu As Long, ByVal un As Long, ByVal bool As Boolean, ByRef lpcMenuItemInfo As MENUITEMINFO) As Long
Public Declare Function GetMenuItemCount Lib "user32" (ByVal hMenu As Long) As Long
'---[Window & Messaging API's]---
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 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 SendMessage Lib "user32" Alias "SendMessageA" (ByVal Hwnd As Long, ByVal wMsg As Long, ByVal wParam As Long, lParam As Any) As Long
Public Declare Function SetParent Lib "user32" (ByVal hWndChild As Long, ByVal hWndNewParent As Long) As Long
Public Declare Function FindWindowEx Lib "user32" Alias "FindWindowExA" (ByVal hWnd1 As Long, ByVal hWnd2 As Long, ByVal lpsz1 As String, ByVal lpsz2 As String) As Long
'---[API Constants]---
Public Const GWL_WNDPROC = (-4)
Public Const WM_QUIT = &H12
Public Const WM_CLOSE = &H10
Public Const WM_DESTROY = &H2
Public Const WM_COMMAND = &H111
Public Const MIIM_STATE = &H1
Public Const MIIM_ID = &H2
Public Const MIIM_SUBMENU = &H4
Public Const MIIM_CHECKMARKS = &H8
Public Const MIIM_TYPE = &H10
Public Const MIIM_DATA = &H20
Public Const MIIM_ALL = MIIM_STATE Or MIIM_ID Or MIIM_SUBMENU Or MIIM_CHECKMARKS Or MIIM_TYPE Or MIIM_DATA
Public Const MFT_STRING = &H0&
'---[UDT - Used to Store Subclassing Info for Multiple Forms]---
Private Type PrevFunc
Hwnd As Long
PrevWndFunc As Long
End Type
Private lHwndFunc() As PrevFunc
Private iFuncs As Long
'--[Source Window Handle, (MDIForm)]---
Public lSrcHwnd As Long
Public Sub SubClassForm(ByVal Hwnd As Long)
'Subclass the Form
ReDim Preserve lHwndFunc(iFuncs)
lHwndFunc(iFuncs).Hwnd = Hwnd
lHwndFunc(iFuncs).PrevWndFunc = SetWindowLong(Hwnd, GWL_WNDPROC, AddressOf WindowProc)
iFuncs = iFuncs + 1
End Sub
Private Function WindowProc(ByVal Hwnd As Long, ByVal Msg As Long, ByVal wParam As Long, ByVal lParam As Long) As Long
'---[WindowProc Callback Function used by ALL Subclassed Windows]---
Dim lPrevFunc As Long
'Get the Original WindowProc Address for this Window
lPrevFunc = GetPrevFunc(Hwnd)
Select Case Msg
Case WM_COMMAND
'Send the Menu Selection to the Source Window, (MDIForm)
Call SendMessage(lSrcHwnd, Msg, wParam, ByVal lParam)
Case WM_CLOSE, WM_QUIT, WM_DESTROY
'Automatically Remove Window Subclassing when the Window is unloaded
Call SetWindowLong(Hwnd, GWL_WNDPROC, lPrevFunc)
Call RemoveFunc(Hwnd)
End Select
'Pass Messages to Original Window Proc
WindowProc = CallWindowProc(lPrevFunc, Hwnd, Msg, wParam, lParam)
End Function
Private Function GetPrevFunc(ByVal Hwnd As Long) As Long
'Function to find the Previous Window Proc of a Specified Window Handle
Dim iIndex As Long
For iIndex = 0 To UBound(lHwndFunc)
If lHwndFunc(iIndex).Hwnd = Hwnd Then Exit For
Next
GetPrevFunc = lHwndFunc(iIndex).PrevWndFunc
End Function
Private Sub RemoveFunc(ByVal Hwnd As Long)
'Sub to Tidy up the Array of Subclassing Info after Removing an Element
Dim iIndex As Long
Dim iNewIndex As Long
For iIndex = 0 To UBound(lHwndFunc)
If lHwndFunc(iIndex).Hwnd <> Hwnd Then
With lHwndFunc(iNewIndex)
.Hwnd = lHwndFunc(iIndex).Hwnd
.PrevWndFunc = lHwndFunc(iIndex).PrevWndFunc
iNewIndex = iNewIndex + 1
End With
End If
Next
If iNewIndex Then ReDim Preserve lHwndFunc(iNewIndex - 1)
iFuncs = iNewIndex
End Sub
Add a Class Module and Name it Subclass..
Code:
'---[Class Module: Subclass]---
Private lDestMenu As Long
Public Function DuplicateMenu(ByVal SrcHwnd As Long, ByVal DestHwnd As Long) As Boolean
'---[Enumerate all Items in the Source windows Menu and Duplicate it]---
Dim lSrcMenu As Long
lSrcHwnd = SrcHwnd
lSrcMenu = GetMenu(SrcHwnd)
If lSrcMenu Then
lDestMenu = CreateMenu
EnumerateMenuItems lSrcMenu, lDestMenu
'Assign the Duplicate Menu to the Destination Window
Call SetMenu(DestHwnd, lDestMenu)
'Subclass the Window to Capture the Menu Messages
Call SubClassForm(DestHwnd)
'Include this next line to make the Form a Child of the MDI Client
'Call SetParent(DestHwnd, FindWindowEx(SrcHwnd, 0, "MDIClient", vbNullString))
DuplicateMenu = True
End If
End Function
Private Sub EnumerateMenuItems(ByVal lMainMenu As Long, ByVal DstMenu As Long, Optional ByVal Level As Long)
'---[Recursive Sub Enumerates a Menu's Items at ALL Levels]---
Dim iItems As Long
Dim iItem As Long
Dim tMENUITEMINFO As MENUITEMINFO
Dim lSubMenu As Long
'Get the Number of Items in this Menu/Submenu
iItems = GetMenuItemCount(lMainMenu)
For iItem = 0 To iItems - 1
'Prepare the UTD to Retrieve all Menu Item Info
With tMENUITEMINFO
.fMask = MIIM_ALL
.fType = MFT_STRING
.dwTypeData = Space(256)
.cch = 256
.cbSize = Len(tMENUITEMINFO)
End With
Call GetMenuItemInfo(lMainMenu, iItem, True, tMENUITEMINFO)
With tMENUITEMINFO
If .hSubMenu Then
'Menu Item has a SubMenu
'Replicate and Enumerate it
lSubMenu = CreateMenu
EnumerateMenuItems .hSubMenu, lSubMenu, Level + 1
.hSubMenu = lSubMenu
End If
'Add this Menu Item to the Duplicate Copy
Call InsertMenuItem(DstMenu, iItem, True, tMENUITEMINFO)
End With
Next
End Sub
Public Sub ReDrawMenu(ByVal Hwnd As Long)
'The Menu needs to be reassign and redrawn when
'the Window Gets/Loses Focus
Call SetMenu(Hwnd, lDestMenu)
End Sub
In Each Form that is to Duplicate the MDIForms Menu..
Code:
Private DupMenu As New Subclass
Private Sub Form_Activate()
Static bSet As Boolean
If Not bSet Then bSet = DupMenu.DuplicateMenu(MDIForm1.Hwnd, Hwnd)
DupMenu.ReDrawMenu Hwnd
End Sub
-
Mar 9th, 2000, 09:37 PM
#4
Thread Starter
Hyperactive Member
Aaron,
The reason I'm bringing the parent menu down into child windows is because I'm working
with an external accounting app that has dozens of windows in each module and the windows
do not stay within a central container, so users could become confused.
Can this work for an external app?
Thanks,
Wade
-
Mar 10th, 2000, 04:16 AM
#5
Yes, this can work for an External Application, as you're not Subclassing the External App, you're Subclassing your Own Form(s) and Forwarding Messages to the External App.
Just Replace MDIForm.hwnd with the Window Handle of the External Application.
I've tried it and it does work.
-
Mar 12th, 2000, 10:45 PM
#6
Thread Starter
Hyperactive Member
Thanks for the awesome code, Aaron. But both the parent and child windows are external
apps. If I make the destination window an external app, the menu copies over, but doesn't send
its messages to the parent. I want the child to have the parent's menu and my app to be the go-
between that detects the user's menu selection and sends it to the parent.
Can I use LoadLibraryEx to map the child into the same process as my app or
AttachThreadInput to attach the threads? Then the child could have the menu, but I could
subclass it so that my app could read its messages.
Thanks,
Wade
Edited by WadeD on 03-13-2000 at 02:45 PM
-
Mar 13th, 2000, 04:29 AM
#7
I'm afraid not, now you're talking about subclassing
something that doesn't belong to your thread,
which you can only do going through a Windows DLL which you can't create with VB.
Here's an idea that just forced its way to the surface of
my dried up little grey cells..
What if you made the Child App's Window a Child of your own
Applications Main Form, then Add the Parent Apps Menu to your Form, this would at least group the Parent Apps Menu
with the Child App on the Same Form and the subclassing
principle would be the same as that demonstrated in my example.
You should just need to locate the Window Handle on the 3rd
Party Child App and use the SetParent API to put it
inside your own Form or a Picturebox on your own Form.
Otherwise dust off that C++ Manual and enjoy
-
Mar 14th, 2000, 06:31 AM
#8
Thread Starter
Hyperactive Member
Thanks for all the help. 2 questions. If I don't pass the message in WindowProc, it locks up. Do I
have to pass every message?
The menu seems to be deleted every time the window loses focus. So if the user clicks on the
picture box (containing the external child app) and then the window, they see a menu disappear
and reappear each time. Is there a way to keep the menu from being deleted?
Thanks again,
Wade
Edited by WadeD on 03-15-2000 at 11:03 AM
Posting Permissions
- You may not post new threads
- You may not post replies
- You may not post attachments
- You may not edit your posts
-
Forum Rules
|
Click Here to Expand Forum to Full Width
|