Jeffrey:
To make this approach robust, I had to write a class deriving from
ContextMenu that would:
1. Override OnPopup in order to selectively enable/disable MenuItems
that aren't usable. (E.g., you can't "cut" or "copy" when there's no
selected text.)
2. Handle both TextBoxBase controls and ComboBox controls, although
the method for obtaining the handle to both is different.
Now, 200ish lines later (didn't you say this would be simple?
), I've
got a derived ContextMenu that still has these shortcomings:
1. It's not generic, requiring different handlers for TextBoxBase and
ComboBox controls (and their derivatives). I'd prefer if this class
were constructed generically (that is, if it could be applied to any
control), but I don't see how to do that, given the various methods for
obtaining the correct handle to the editable portion of the control.
2. It's not international: I'd prefer to read the values for "Cut,"
and so on, from the system's regional settings, but I've got no idea
where I'd locate those.
Here's where I am so far. Any comments would be lovely.
g.
''' <summary>
''' A context menu (derived from the System.Windows.Forms.ContextMenu
class) that
''' replicates the standard edit context menu controls (Copy, Cut,
Paste, Undo, and Select
''' All) so that the ContextMenu can be expanded upon.
''' </summary>
''' <remarks>
''' The EditContextMenu supports ComboBox and TextBoxBase controls (and
any controls
''' that derive from them).
''' <br>For more information about the development of this class,
''' contact Graham Charles (
[email protected]).
''' </remarks>
''' <example>
''' To replace a control's default Win32 ContextMenu with this
expandable class, do the
''' following:
''' <code lang="VB" title="Constructor example (derived control)"
''' description="This example demonstrates how to instantiate an
aiEditContextMenu object
''' in a control that derives from ComboBox or TextBoxBase.">
''' Public Class aiComboBox
''' Inherits System.Windows.Forms.ComboBox
'''
''' Private myContext As New aiEditContextMenu(Me)
'''
''' ' ... remainder of code
''' </code>
''' <code title="Contstructor example (control on form)">
''' </code>
''' <code lang="VB" title="Adding Menu Items example"
''' description="This example demonstrates how to add a MenuItem to
the custom edit
''' context menu.">
''' ' / in declarations section
''' Private myMenuItem As MenuItem
'''
''' ' / in Form_Load or other initialization area
''' If myMenuItem Is Nothing Then
''' myMenuItem = New MenuItem("My Caption")
''' myContext.MenuItems.Add(myMenuItem)
''' AddHandler myMenuItem.Click, AddressOf myMenuItemHandler
''' End If
''' </code>
''' </example>
Public Class aiEditContextMenu
Inherits System.Windows.Forms.ContextMenu
#Region "/// Declarations "
Private Declare Auto Function GetWindow Lib "user32.dll" (ByVal
hwnd As IntPtr, ByVal wCmd As Int32) As IntPtr
Private Declare Auto Function SendMessage Lib "user32.dll" (ByVal
hwnd As IntPtr, ByVal wMsg As Int32, ByVal wParam As Boolean, ByVal
lParam As Int32) As Int32
Private Const EM_UNDO = &HC7
Private Const EM_CANUNDO = &HC6
Private Const WM_CUT = &H300
Private Const WM_COPY = &H301
Private Const WM_PASTE = &H302
Private Const WM_CLEAR = &H303
Private Const GW_CHILD As Int32 = 5
Public MenuItemCopy As New MenuItem("&Copy", New
System.EventHandler(AddressOf MenuCopy))
Public MenuItemDelete As New MenuItem("&Delete", New
System.EventHandler(AddressOf MenuDelete))
Public MenuItemPaste As New MenuItem("&Paste", New
System.EventHandler(AddressOf MenuPaste))
Public MenuItemCut As New MenuItem("Cu&t", New
System.EventHandler(AddressOf MenuCut))
Public MenuItemUndo As New MenuItem("&Undo", New
System.EventHandler(AddressOf MenuUndo))
Public MenuItemSelectAll As New MenuItem("Select &All", New
System.EventHandler(AddressOf MenuSelectAll))
Private _ComboBox As System.Windows.Forms.ComboBox ' /
ListControl object
Private _TextBoxBase As System.Windows.Forms.TextBoxBase ' /
TextBoxBase object
#End Region
#Region "/// Constructors "
''' <summary> The constructor can take a ComboBox or TextBoxBase
item as an argument. </summary>
Public Sub New(ByVal vComboBox As ComboBox)
_ComboBox = vComboBox
_ComboBox.ContextMenu = Me
AttachMenuItems()
End Sub
Public Sub New(ByVal vTextBoxBase As TextBoxBase)
_TextBoxBase = vTextBoxBase
_TextBoxBase.ContextMenu = Me
AttachMenuItems()
End Sub
Private Sub AttachMenuItems()
' / create context menu items to simulate standard ones
MyBase.MenuItems.Add(MenuItemUndo)
MyBase.MenuItems.Add("-")
MyBase.MenuItems.Add(MenuItemCut)
MyBase.MenuItems.Add(MenuItemCopy)
MyBase.MenuItems.Add(MenuItemPaste)
MyBase.MenuItems.Add(MenuItemDelete)
MyBase.MenuItems.Add("-")
MyBase.MenuItems.Add(MenuItemSelectAll)
End Sub
#End Region
#Region "/// Menu Handlers"
Private Sub MenuCopy(ByVal sender As Object, ByVal e As
System.EventArgs)
Try
If Not _ComboBox Is Nothing Then
If Len(_ComboBox.SelectedText) > 0 Then
SendMessage(GetWindow(_ComboBox.Handle, GW_CHILD),
WM_COPY, False, 0)
'Clipboard.SetDataObject(_ComboBox.SelectedText)
End If
End If
If Not _TextBoxBase Is Nothing Then _TextBoxBase.Copy()
Catch ex As Exception
' / ignore errors caused by unsupported Control types
End Try
End Sub
Private Sub MenuDelete(ByVal sender As Object, ByVal e As
System.EventArgs)
Try
If Not _ComboBox Is Nothing Then
If Len(_ComboBox.SelectedText) > 0 Then
_ComboBox.SelectedText = String.Empty
End If
If Not _TextBoxBase Is Nothing Then
If Len(_TextBoxBase.SelectedText) > 0 Then
_TextBoxBase.SelectedText = String.Empty
End If
Catch ex As Exception
' / ignore errors caused by unsupported Control types
End Try
End Sub
Private Sub MenuCut(ByVal sender As Object, ByVal e As
System.EventArgs)
Try
If Not _ComboBox Is Nothing Then
SendMessage(GetWindow(_ComboBox.Handle, GW_CHILD),
WM_CUT, False, 0)
End If
If Not _TextBoxBase Is Nothing Then _TextBoxBase.Cut()
Catch ex As Exception
' / ignore errors caused by unsupported Control types
End Try
End Sub
Private Sub MenuPaste(ByVal sender As Object, ByVal e As
System.EventArgs)
Try
If Not _ComboBox Is Nothing Then
If CanPaste() Then
_ComboBox.SelectedText =
Clipboard.GetDataObject.GetData(DataFormats.Text)
End If
End If
If Not _TextBoxBase Is Nothing Then _TextBoxBase.Paste()
Catch ex As Exception
' / ignore errors caused by unsupported Control types
End Try
End Sub
Private Sub MenuSelectAll(ByVal sender As Object, ByVal e As
System.EventArgs)
Try
If Not _ComboBox Is Nothing Then _ComboBox.SelectAll()
If Not _TextBoxBase Is Nothing Then
_TextBoxBase.SelectAll()
Catch ex As Exception
' / ignore errors caused by unsupported Control types
End Try
End Sub
Private Sub MenuUndo(ByVal sender As Object, ByVal e As
System.EventArgs)
Try
If CanUndo() Then
If Not _ComboBox Is Nothing Then
SendMessage(GetWindow(_ComboBox.Handle, GW_CHILD), EM_UNDO, False, 0)
If Not _TextBoxBase Is Nothing Then _TextBoxBase.Undo()
End If
Catch ex As Exception
' / ignore errors caused by unsupported Control types
End Try
End Sub
#End Region
#Region "/// Menu Access Flags"
Private Function CanPaste() As Boolean
CanPaste =
Clipboard.GetDataObject.GetDataPresent(DataFormats.Text)
End Function
Private Function CanUndo() As Boolean
If Not _ComboBox Is Nothing Then
CanUndo = (SendMessage(GetWindow(_ComboBox.Handle,
GW_CHILD), EM_CANUNDO, False, 0) <> 0)
End If
If Not _TextBoxBase Is Nothing Then CanUndo =
_TextBoxBase.CanUndo
End Function
#End Region
#Region "/// Overrides"
Protected Overrides Sub OnPopup(ByVal e As System.EventArgs)
Dim bCanCutCopy As Boolean
Dim bCanSelectAll As Boolean
' / enable/disable controls
If Not _ComboBox Is Nothing Then
bCanCutCopy = (Len(_ComboBox.SelectedText) > 0)
bCanSelectAll = (Len(_ComboBox.Text) > 0)
End If
If Not _TextBoxBase Is Nothing Then
bCanCutCopy = (Len(_TextBoxBase.SelectedText) > 0)
bCanSelectAll = (Len(_TextBoxBase.Text) > 0)
End If
MenuItemCopy.Enabled = bCanCutCopy
MenuItemCut.Enabled = bCanCutCopy
MenuItemDelete.Enabled = bCanCutCopy
MenuItemSelectAll.Enabled = bCanSelectAll
MenuItemPaste.Enabled = CanPaste()
MenuItemUndo.Enabled = CanUndo()
MyBase.OnPopup(e)
End Sub
#End Region
End Class