<you can display a customized context menu of your own [etc]
Well, I got interested and wrote one. It seems to work fine. I borrowed plenty of ideas from the French guy, and swiped Hypetia's code to disable the context menu.
Here's the code:[tt]
'This code goes in a Form1 form, with at least a Text1 textbox.
Option Explicit
Private Sub Form_Load()
SetContextMenu Text1, False 'Disable the context menu for Text1
End Sub
Private Sub Text1_MouseUp(Button As Integer, Shift As Integer, X As Single, Y As Single)
If Button = vbRightButton Then
DoCustomMenu Text1 'Creates the custom menu when right clicking Text1
End If
End Sub
Private Sub DoCustomMenu(cBox As TextBox)
Dim cSelStart As Integer
Select Case ShowCustomEditPopup(Me, cBox) 'Return values are the wId value in the MENUITEMINFO type
Case 100
DoSelectAll cBox
Case 102
DoDelete cBox
Case 103
DoPaste cBox
Case 104
DoCopy cBox
Case 105
DoCut cBox
Case 107
DoUndo cBox
End Select
End Sub
'And this code goes in a standard module:
Option Explicit
'Constants and SendMessage function, needed to emulate the context menu functions
Private Const EM_CANUNDO = &HC6
Private Const EM_UNDO = &HC7
Private Const WM_CUT = &H300
Private Const WM_COPY = &H301
Private Const WM_PASTE = &H302
Private Const WM_CLEAR = &H303
Private Declare Function SendMessage Lib "user32" Alias "SendMessageA" (ByVal hwnd As Long, ByVal wMsg As Long, ByVal wParam As Long, lParam As Any) As Long
'Constants, Types, and Functions for the popup menu
Private Const MIIM_ID = &H2
Private Const MIIM_TYPE = &H10
Private Const MIIM_STATE = &H1
Private Const MIIM_SUBMENU = &H4
Private Const TPM_LEFTALIGN = &H0&
Private Const TPM_RETURNCMD = &H100&
Private Const TPM_RIGHTBUTTON = &H2&
Private Const MFT_STRING = &H0
Private Const MFT_SEPARATOR = &H800&
Private Const MFS_ENABLED = &H0
Private Const MFS_DISABLED = &H3
Private 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
Private Type POINTAPI
X As Long
Y As Long
End Type
Private Declare Function CreatePopupMenu Lib "user32" () As Long
Private Declare Function InsertMenuItem Lib "user32.dll" Alias "InsertMenuItemA" (ByVal hMenu As Long, ByVal uItem As Long, ByVal fByPosition As Long, lpmii As MENUITEMINFO) As Long
Private Declare Function TrackPopupMenuEx Lib "user32" (ByVal hMenu As Long, ByVal wFlags As Long, ByVal X As Long, ByVal Y As Long, ByVal hwnd As Long, ByVal lptpm As Any) As Long
Private Declare Function GetCursorPos Lib "user32" (lpPoint As POINTAPI) As Long
Private Declare Function DestroyMenu Lib "user32" (ByVal hMenu As Long) As Long
'Functions to disable context menu (Hypetia)
Declare Function SetWindowLong& Lib "user32" Alias "SetWindowLongA" (ByVal hwnd&, ByVal nIndex&, ByVal dwNewLong&)
Declare Function CallWindowProc& Lib "user32" Alias "CallWindowProcA" (ByVal lpPrevWndFunc&, ByVal hwnd&, ByVal uMsg&, ByVal wParam&, ByVal lParam&)
Const GWL_WNDPROC = (-4)
Dim lppwp& '(Long) Pointer to Previous Window Procedure
Public Function ShowCustomEditPopup(cForm As Form, cBox As TextBox) As Long
Dim pMenuInfo As MENUITEMINFO
Dim pPositionCursor As POINTAPI
Dim lHandleMenu As Long
Dim lHandleMenuSel(7) As Long 'one for each menu selection, including separators
Dim i As Integer
'initialize popup menu window and each of the 8 selections
lHandleMenu = CreatePopupMenu
For i = 0 To 7
lHandleMenuSel(i) = CreatePopupMenu
Next
'Set up each menu item, using the MENUITEMINFO type. Note that the menu items are pushed
'onto a stack, meaning that the first item inserted will be the last selection on the menu.
With pMenuInfo
.cbSize = Len(pMenuInfo) 'Size of the structure itself in bytes.
.fType = MFT_STRING
.fState = MFS_ENABLED
.dwTypeData = "Select All" 'What the menu item will say
.cch = Len(.dwTypeData)
'If fType is MFT_STRING, the length of the string. Determines the width of the menu.
.wID = 100
.fMask = MIIM_ID Or MIIM_TYPE Or MIIM_STATE Or MIIM_SUBMENU
'Packages information about the item into a hex value, which is then unpackaged by the
'InsertMenuItem routine and used to render the selections. ID is basically the key.
'Type is either separator or string value in this app, can also have several other values.
'State is enabled, disabled, checked, etc. All the selections are submenus, in that they
'are items in the main popup menu whose handle is stored in the lHandelMenu variable.
End With
InsertMenuItem lHandleMenu, 0, True, pMenuInfo
'Lower Separator
With pMenuInfo
.cbSize = Len(pMenuInfo)
.fType = MFT_SEPARATOR
.wID = 101
.fMask = MIIM_ID Or MIIM_TYPE Or MIIM_STATE Or MIIM_SUBMENU
End With
InsertMenuItem lHandleMenu, 0, True, pMenuInfo
With pMenuInfo
.cbSize = Len(pMenuInfo)
.fType = MFT_STRING
.fState = IIf(cBox.SelText = "", MFS_DISABLED, MFS_ENABLED)
'Disable the Delete selection if no text is selected
.dwTypeData = "Delete"
.cch = Len(.dwTypeData)
.wID = 102
.fMask = MIIM_ID Or MIIM_TYPE Or MIIM_STATE Or MIIM_SUBMENU
End With
InsertMenuItem lHandleMenu, 0, True, pMenuInfo
With pMenuInfo
.cbSize = Len(pMenuInfo)
.fType = MFT_STRING
.fState = IIf(Not IsNumeric(Clipboard.GetText), MFS_DISABLED, MFS_ENABLED)
'Disable the Paste selection if the Clipboard contains non-numeric text
.dwTypeData = "Paste"
.cch = Len(.dwTypeData)
.wID = 103
.fMask = MIIM_ID Or MIIM_TYPE Or MIIM_STATE Or MIIM_SUBMENU
End With
InsertMenuItem lHandleMenu, 0, True, pMenuInfo
With pMenuInfo
.cbSize = Len(pMenuInfo)
.fType = MFT_STRING
.fState = IIf(cBox.SelText = "", MFS_DISABLED, MFS_ENABLED)
'Disable the Copy selection if no text is selected
.dwTypeData = "Copy"
.cch = Len(.dwTypeData)
.wID = 104
.fMask = MIIM_ID Or MIIM_TYPE Or MIIM_STATE Or MIIM_SUBMENU
End With
InsertMenuItem lHandleMenu, 0, True, pMenuInfo
With pMenuInfo
.cbSize = Len(pMenuInfo)
.fType = MFT_STRING
.fState = IIf(cBox.SelText = "", MFS_DISABLED, MFS_ENABLED)
'Disable the Cut selection if no text is selected
.dwTypeData = "Cut"
.cch = Len(.dwTypeData)
.wID = 105
.fMask = MIIM_ID Or MIIM_TYPE Or MIIM_STATE Or MIIM_SUBMENU
End With
InsertMenuItem lHandleMenu, 0, True, pMenuInfo
With pMenuInfo
.cbSize = Len(pMenuInfo)
.fType = MFT_SEPARATOR
.wID = 106
.fMask = MIIM_ID Or MIIM_TYPE Or MIIM_STATE Or MIIM_SUBMENU
End With
InsertMenuItem lHandleMenu, 0, True, pMenuInfo
With pMenuInfo
.cbSize = Len(pMenuInfo)
.fType = MFT_STRING
.fState = IIf(SendMessage(cBox.hwnd, EM_CANUNDO, 0, 0) = 0, MFS_DISABLED, MFS_ENABLED)
'Disable the Undo selection if there is no possible Undo
.dwTypeData = "Undo"
.cch = Len(pMenuInfo.dwTypeData)
.wID = 107
.fMask = MIIM_ID Or MIIM_TYPE Or MIIM_STATE Or MIIM_SUBMENU
End With
InsertMenuItem lHandleMenu, 0, True, pMenuInfo
GetCursorPos pPositionCursor 'Get cursor position, store it to pPositionCursor type
ShowCustomEditPopup = TrackPopupMenuEx(lHandleMenu, TPM_LEFTALIGN Or TPM_RIGHTBUTTON _
Or TPM_RETURNCMD, pPositionCursor.X, pPositionCursor.Y, cForm.hwnd, ByVal 0&)
'TPM_LEFTALIGN: Left Align menu at the cursor position
'TPM_RIGHTBUTTON: Allows user to make menu selections with either button
'TPM_RETURNCMD: Sends wId of selected item as return value
'Clean up
DestroyMenu lHandleMenu
For i = 0 To 7
DestroyMenu lHandleMenuSel(i)
Next
Exit Function
End Function
'Hypetia's code for disabling context menu.
Function WindowProc&(ByVal hwnd&, ByVal uMsg&, ByVal wParam&, ByVal lParam&)
If uMsg = &H7B& Then uMsg = 0 'Disable WM_CONTEXTMENU
WindowProc = CallWindowProc(lppwp, hwnd, uMsg, wParam, lParam)
End Function
Sub SetContextMenu(Txt As TextBox, bEnable As Boolean)
If bEnable Then
If lppwp Then SetWindowLong Txt.hwnd, GWL_WNDPROC, lppwp
Else 'subclass the textbox window, by redirecting the standard window procedure to WindowProc
Dim Ret As Long
Ret = SetWindowLong(Txt.hwnd, GWL_WNDPROC, AddressOf WindowProc)
If lppwp = 0 Then lppwp = Ret
End If
End Sub
'Replacement menu routines
Public Sub DoUndo(cBox As TextBox)
If SendMessage(cBox.hwnd, EM_CANUNDO, 0, 0) <> 0 Then
SendMessage cBox.hwnd, EM_UNDO, 0, 0
End If
End Sub
Public Sub DoDelete(cBox As TextBox)
SendMessage cBox.hwnd, WM_CLEAR, 0, 0
End Sub
Public Sub DoCut(cBox As TextBox)
SendMessage cBox.hwnd, WM_CUT, 0, 0
'Clipboard.SetText cBox.SelText
'SendMessage cBox.hwnd, WM_CLEAR, 0, 0
End Sub
Public Sub DoCopy(cBox As TextBox)
SendMessage cBox.hwnd, WM_COPY, 0, 0
'Clipboard.SetText cBox.SelText
End Sub
Public Sub DoSelectAll(cBox As TextBox)
With cBox
.SelStart = 0
.SelLength = Len(.Text)
End With
End Sub
Public Sub DoPaste(cBox As TextBox)
SendMessage cBox.hwnd, WM_PASTE, 0, 0
End Sub
[/tt]
HTH
Bob