如何拦截ComboBox的mouse右键

原始来源:cww  技术请教於 王国荣

在ComboBox上按右键时,会有一个popup menu出现,如何令之不出现呢?
在editBox中可用Subclassing的技巧check msg是否是WM_RBUTTONDOWN 来拦截mouse
是否按了右键而後吃掉该message(不Call CallWindowProc()),而使之不会出现
popup menu。但在ComboBox中,按了mouse右键,却仍出现Popup Menu,
查了一下按右键时,发现是 WM_PARENTNOTIFY的讯息,而不是
wm_rbuttondown, 这该如何才能使之不出现Popup menu

这是个有趣的问题,ComboBox 是由 "Edit"(TextBox 的前身) 及 "ListBox"
两种 Windows 的 control 所组成的,而在 ComboBox 上面按下滑鼠右钮是由
"Edit" 来处理,因此拦截的方法是:

  1. 呼叫 EnumChildWindows 取得 ComboBox 的 "Edit" 子视窗的 hWnd
  2. 拦截 "Edit" hWnd。

而取得ListBox的hWnd请叁考改变ComboBox中ListBox的宽度

'以下程式在form
Option Explicit
Private hwnd5 As Long

Private Sub Form_Load()
Dim ret As Long
 '取得Combo内EditBox的hwnd
 hwnd5 = FindEditInCombo(Combo1)
 '记录原本的Window Procedure的位址
 preWinProc = GetWindowLong(hwnd5, GWL_WNDPROC)
 '设定EditBox的window Procedure到wndproc
 ret = SetWindowLong(hwnd5, GWL_WNDPROC, AddressOf wndproc)
 End Sub

Private Sub Form_Unload(Cancel As Integer)
 Dim ret As Long
 '取消Message的截取,而使之又只送往原来的Window Procedure
 ret = SetWindowLong(hwnd5, GWL_WNDPROC, preWinProc)
End Sub

'以下程式在.bas module
Option Explicit

Declare Function EnumChildWindows Lib "user32" _
   (ByVal hWndParent As Long, ByVal lpEnumFunc As Long, _
   ByVal lParam As Long) As Long
Declare Function GetClassName Lib "user32" Alias "GetClassNameA" _
       (ByVal hwnd As Long, ByVal lpClassName As String, _
        ByVal nMaxCount As Long) As Long
Declare Function SetWindowLong Lib "user32" Alias "SetWindowLongA" _
  (ByVal hwnd As Long, ByVal nIndex As Long, ByVal dwNewLong As Long) As Long
Declare Function GetWindowLong Lib "user32" Alias "GetWindowLongA" _
  (ByVal hwnd As Long, ByVal nIndex As Long) As Long
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 Const GWL_WNDPROC = (-4)
Public Const WM_MOUSEMOVE = &H200
Public Const WM_RBUTTONDOWN = &H204
Public preWinProc As Long
Private hEditWnd As Long

Public Function wndproc(ByVal hwnd As Long, ByVal Msg As Long, _
                         ByVal wParam As Long, ByVal lParam As Long) As Long
 '以下会截取mouse Rbutton Down
 If Msg = WM_RBUTTONDOWN Then
    Debug.Print "Combol Mouse RButton Down "
 Else
 '将之送往原来的Window Procedure
    wndproc = CallWindowProc(preWinProc, hwnd, Msg, wParam, lParam)
 End If
 End Function
Public Function FindEditInCombo(ctl As ComboBox) As Long
 Call EnumChildWindows(ctl.hwnd, AddressOf EnumFunc, 0)
 FindEditInCombo = hEditWnd
End Function

Public Function EnumFunc(ByVal hwnd As Long, ByVal lParam As Long) As Long
Dim ClsName As String
Dim len5 As Long
If hwnd = 0 Then
   EnumFunc = 0
Else
   ClsName = String(255, 0)
   len5 = GetClassName(hwnd, ClsName, 256)
   ClsName = Left(ClsName, len5)
   If ClsName = "Edit" Then
      hEditWnd = hwnd
      EnumFunc = 0
   Else
      EnumFunc = 1
   End If
End If
End Function