我的做法是子类化MDI主窗体中的客户区窗体(类名为:MDIClient,需要用FindWindowEx函数查找) 你的第一个问题,设置一个全局变量 拦截消息WM_MDICREATE,则增加一个子窗体(全局变量+1) 拦截消息WM_MDIDESTROY,则减少一个子窗体(全局变量-1)第二个问题 用SendMessage发送WM_MDIGETACTIVE给客户端,获得当前获得焦点的窗体 用SendMessage发送消息WM_MDIACTIVATE给客户端,则可以设置某个窗体获得焦点 给段代码你参考一下 '客户区回调函数 Function WndProc_MDIClient(ByVal hWnd As Long, ByVal Msg As Long, ByVal wParam As Long, ByVal lParam As Long) As Long Dim Rtn As Long Dim TCI As TCITEM Dim strBuffer As String Dim strWindowText As String Dim lngBufferSize As Long Dim lngWindowIcon As Long Dim lngTabsCount As Long Dim lngIconCount As Long Dim i As Long
Select Case Msg Case WM_KILLFOCUS '检测用户在客户区中更换了哪个窗口,并将该窗口对应的选项卡设置焦点 '获取选项卡窗格数量 lngTabsCount = SendMessage(tcWnd, TCM_GETITEMCOUNT, 0, ByVal 0&) '设置窗格返回值有效标志(自定义项) TCI.mask = TCIF_PARAM For i = 0 To lngTabsCount - 1 With TCI '获取选项卡第i个窗格 SendMessage tcWnd, TCM_GETITEMA, i, TCI If .lParam = wParam Then '如果当前获得焦点的窗口句柄与选项卡中记载的句柄相同,则激活该选项卡窗格 SendMessage tcWnd, TCM_SETCURFOCUS, i, ByVal 0& '退出循环 Exit For End If End With Next i Case WM_MDICREATE '子窗口增加 glngWindowCount = glngWindowCount + 1 ShowWindow tcBGWnd, IIf(glngWindowCount > 0, SW_SHOW, SW_HIDE) Case WM_MDIDESTROY '子窗口删除 '记录当前子窗口数量 glngWindowCount = glngWindowCount - 1 '设置窗格返回值有效标志(自定义项) TCI.mask = TCIF_PARAM '获得选项卡窗格数量 lngTabsCount = SendMessage(tcWnd, TCM_GETITEMCOUNT, 0, ByVal 0&) For i = 0 To lngTabsCount - 1 With TCI '获取选项卡第i个窗格 SendMessage tcWnd, TCM_GETITEMA, i, TCI '判断窗格所对应的窗口句柄是否等于正在被消除的窗口句柄 If .lParam = wParam Then '删除第i个窗格 SendMessage tcWnd, TCM_DELETEITEM, i, ByVal 0& '获取当前正被消除的子窗口的下一个子窗口句柄 mlngActivateWindow = GetWindow(wParam, GW_HWNDNEXT) '使下一个子窗口所对应的窗格获得焦点 If IsWindow(mlngActivateWindow) = 1 And mlngActivateWindow <> wParam Then SendMessage tcWnd, TCM_SETCURFOCUS, i, ByVal 0& Exit For End If End With Next i '如果当前客户区中没有任何子窗口,则隐藏选项卡 ShowWindow tcBGWnd, IIf(glngWindowCount > 0, SW_SHOW, SW_HIDE) '如果当前客户区中没有任何子窗口,则删除选项卡图象列表中的所有图象 If glngWindowCount <= 0 Then '获得图象总数 lngIconCount = ImageList_GetImageCount(tchIml) For i = 0 To lngIconCount - 1 ImageList_Remove tchIml, i Next i End If Case WM_MDIACTIVATE, WM_MDIMAXIMIZE '获得窗口标题字符 strWindowText = GetWindowText(wParam)
'判断选项卡中是否有重复项 TCI.mask = TCIF_PARAM StartCheckTab: lngTabsCount = SendMessage(tcWnd, TCM_GETITEMCOUNT, 0, ByVal 0&) For i = 0 To lngTabsCount - 1 With TCI SendMessage tcWnd, TCM_GETITEMA, i, TCI If IsWindow(.lParam) = 0 Or GetParent(.lParam) <> MDIClient Then SendMessage tcWnd, TCM_DELETEITEM, i, ByVal 0& GoTo StartCheckTab End If If .lParam = wParam Then GoTo KeepGoing End With Next i
'选项卡插入新窗格 If tcWnd Then With TCI .mask = TCIF_TEXT Or TCIF_PARAM .pszText = strWindowText .lParam = wParam End With SendMessage tcWnd, TCM_INSERTITEMA, lngTabsCount, TCI End If Case WM_PAINT '当客户区重画时,重新判断选项卡中的每个窗格是否已有图标。如没有则设置一个。 lngTabsCount = SendMessage(tcWnd, TCM_GETITEMCOUNT, 0, ByVal 0&) If lngTabsCount = 0 Then GoTo KeepGoing For i = 0 To lngTabsCount - 1 With TCI .mask = TCIF_PARAM Or TCIF_IMAGE Or TCIF_TEXT TCI.cchTextMax = MAX_BUFFER_SIZE TCI.pszText = String(MAX_BUFFER_SIZE, vbNullChar) SendMessage tcWnd, TCM_GETITEMA, i, TCI If IsWindow(.lParam) = 0 Then GoTo CheckNextTab TCI.pszText = Left$(TCI.pszText, InStr(TCI.pszText, vbNullChar) - 1) strBuffer = GetWindowText(.lParam) If strBuffer <> TCI.pszText Then TCI.pszText = strBuffer If .iImage > -1 Then GoTo CheckNextTab lngWindowIcon = SendMessage(.lParam, WM_GETICON, ICON_SMALL, ByVal 0&) If lngWindowIcon = 0 Then lngWindowIcon = GetClassLong(.lParam, GCL_HICONSM) If lngWindowIcon = 0 Then GoTo CheckNextTab ImageList_AddIcon tchIml, lngWindowIcon DestroyIcon lngWindowIcon .iImage = ImageList_GetImageCount(tchIml) - 1 SendMessage tcWnd, TCM_SETITEMA, i, TCI End With CheckNextTab: Next i End Select
KeepGoing: WndProc_MDIClient = CallWindowProc(glngMDICPrevWnd, hWnd, Msg, wParam, lParam) End Function
如果没有,请使用FOR EACH来循环累计。第二个,不用你指定,MDI自己的WINDOWS菜单里他分得清,
这个菜单是自动的,不用你写代码,在菜单设计器里有个属性勾上就OK了。
当多一个窗体就会多一个条目,你点一下就会切换到相应的窗体,不用你写任何代码。
仅仅是设置某个菜单的某个属性而已。
问题是动态创建的MDI怎么实现
你的第一个问题,设置一个全局变量
拦截消息WM_MDICREATE,则增加一个子窗体(全局变量+1)
拦截消息WM_MDIDESTROY,则减少一个子窗体(全局变量-1)第二个问题
用SendMessage发送WM_MDIGETACTIVE给客户端,获得当前获得焦点的窗体
用SendMessage发送消息WM_MDIACTIVATE给客户端,则可以设置某个窗体获得焦点
给段代码你参考一下
'客户区回调函数
Function WndProc_MDIClient(ByVal hWnd As Long, ByVal Msg As Long, ByVal wParam As Long, ByVal lParam As Long) As Long
Dim Rtn As Long
Dim TCI As TCITEM
Dim strBuffer As String
Dim strWindowText As String
Dim lngBufferSize As Long
Dim lngWindowIcon As Long
Dim lngTabsCount As Long
Dim lngIconCount As Long
Dim i As Long
Select Case Msg
Case WM_KILLFOCUS '检测用户在客户区中更换了哪个窗口,并将该窗口对应的选项卡设置焦点
'获取选项卡窗格数量
lngTabsCount = SendMessage(tcWnd, TCM_GETITEMCOUNT, 0, ByVal 0&)
'设置窗格返回值有效标志(自定义项)
TCI.mask = TCIF_PARAM
For i = 0 To lngTabsCount - 1
With TCI
'获取选项卡第i个窗格
SendMessage tcWnd, TCM_GETITEMA, i, TCI
If .lParam = wParam Then
'如果当前获得焦点的窗口句柄与选项卡中记载的句柄相同,则激活该选项卡窗格
SendMessage tcWnd, TCM_SETCURFOCUS, i, ByVal 0&
'退出循环
Exit For
End If
End With
Next i
Case WM_MDICREATE '子窗口增加
glngWindowCount = glngWindowCount + 1
ShowWindow tcBGWnd, IIf(glngWindowCount > 0, SW_SHOW, SW_HIDE)
Case WM_MDIDESTROY '子窗口删除
'记录当前子窗口数量
glngWindowCount = glngWindowCount - 1
'设置窗格返回值有效标志(自定义项)
TCI.mask = TCIF_PARAM
'获得选项卡窗格数量
lngTabsCount = SendMessage(tcWnd, TCM_GETITEMCOUNT, 0, ByVal 0&)
For i = 0 To lngTabsCount - 1
With TCI
'获取选项卡第i个窗格
SendMessage tcWnd, TCM_GETITEMA, i, TCI
'判断窗格所对应的窗口句柄是否等于正在被消除的窗口句柄
If .lParam = wParam Then
'删除第i个窗格
SendMessage tcWnd, TCM_DELETEITEM, i, ByVal 0&
'获取当前正被消除的子窗口的下一个子窗口句柄
mlngActivateWindow = GetWindow(wParam, GW_HWNDNEXT)
'使下一个子窗口所对应的窗格获得焦点
If IsWindow(mlngActivateWindow) = 1 And mlngActivateWindow <> wParam Then SendMessage tcWnd, TCM_SETCURFOCUS, i, ByVal 0&
Exit For
End If
End With
Next i
'如果当前客户区中没有任何子窗口,则隐藏选项卡
ShowWindow tcBGWnd, IIf(glngWindowCount > 0, SW_SHOW, SW_HIDE)
'如果当前客户区中没有任何子窗口,则删除选项卡图象列表中的所有图象
If glngWindowCount <= 0 Then
'获得图象总数
lngIconCount = ImageList_GetImageCount(tchIml)
For i = 0 To lngIconCount - 1
ImageList_Remove tchIml, i
Next i
End If
Case WM_MDIACTIVATE, WM_MDIMAXIMIZE
'获得窗口标题字符
strWindowText = GetWindowText(wParam)
'判断选项卡中是否有重复项
TCI.mask = TCIF_PARAM
StartCheckTab:
lngTabsCount = SendMessage(tcWnd, TCM_GETITEMCOUNT, 0, ByVal 0&)
For i = 0 To lngTabsCount - 1
With TCI
SendMessage tcWnd, TCM_GETITEMA, i, TCI
If IsWindow(.lParam) = 0 Or GetParent(.lParam) <> MDIClient Then
SendMessage tcWnd, TCM_DELETEITEM, i, ByVal 0&
GoTo StartCheckTab
End If
If .lParam = wParam Then GoTo KeepGoing
End With
Next i
'选项卡插入新窗格
If tcWnd Then
With TCI
.mask = TCIF_TEXT Or TCIF_PARAM
.pszText = strWindowText
.lParam = wParam
End With
SendMessage tcWnd, TCM_INSERTITEMA, lngTabsCount, TCI
End If
Case WM_PAINT '当客户区重画时,重新判断选项卡中的每个窗格是否已有图标。如没有则设置一个。
lngTabsCount = SendMessage(tcWnd, TCM_GETITEMCOUNT, 0, ByVal 0&)
If lngTabsCount = 0 Then GoTo KeepGoing
For i = 0 To lngTabsCount - 1
With TCI
.mask = TCIF_PARAM Or TCIF_IMAGE Or TCIF_TEXT
TCI.cchTextMax = MAX_BUFFER_SIZE
TCI.pszText = String(MAX_BUFFER_SIZE, vbNullChar)
SendMessage tcWnd, TCM_GETITEMA, i, TCI
If IsWindow(.lParam) = 0 Then GoTo CheckNextTab
TCI.pszText = Left$(TCI.pszText, InStr(TCI.pszText, vbNullChar) - 1)
strBuffer = GetWindowText(.lParam)
If strBuffer <> TCI.pszText Then TCI.pszText = strBuffer
If .iImage > -1 Then GoTo CheckNextTab
lngWindowIcon = SendMessage(.lParam, WM_GETICON, ICON_SMALL, ByVal 0&)
If lngWindowIcon = 0 Then lngWindowIcon = GetClassLong(.lParam, GCL_HICONSM)
If lngWindowIcon = 0 Then GoTo CheckNextTab
ImageList_AddIcon tchIml, lngWindowIcon
DestroyIcon lngWindowIcon
.iImage = ImageList_GetImageCount(tchIml) - 1
SendMessage tcWnd, TCM_SETITEMA, i, TCI
End With
CheckNextTab:
Next i
End Select
KeepGoing:
WndProc_MDIClient = CallWindowProc(glngMDICPrevWnd, hWnd, Msg, wParam, lParam)
End Function
因为选项卡中每个卡中都保存了特定窗体的句柄,点击选项卡的时候发送WM_MDIACTIVATE消息给客户区