By using this site, you agree to our updated Privacy Policy and our Terms of Use. Manage your Cookies Settings.
449,276 Members | 1,336 Online
Bytes IT Community
+ Ask a Question
Need help? Post your question and get tips & solutions from a community of 449,276 IT Pros & Developers. It's quick & easy.

alter built-in ContextMenu

P: n/a
I'm writing a control inheriting from ComboBox, and I'd like to add an
entry to the ContextMenu for that combo box. I have no problem if I
create an entirely new ContextMenu from scratch, but I'd prefer just to
tack my MenuItem on to the existing, built-in ContextMenu (Cut, Copy,
Paste, etc.):

_RemoveMRUItemMenu = New MenuItem
_RemoveMRUItemMenu.Text = "&Remove Item"
_RemoveMRUItemMenu.Enabled = False

MyBase.ContextMenu.MenuItems.Add(_RemoveMRUItemMen u)

AddHandler _RemoveMRUItemMenu.Click, AddressOf
RemoveCurrentMRUItem
This doesn't work; MyBase.ContextMenu returns Nothing, even though
there *is* an existing ContextMenu (the built-in one).

Is the only way to write code to manually perform the usual TextBox
context menu commands? Or can I write a ContextMenu inheriting from
something like "TextBoxContextMenu"?

Thanks,

g.

Dec 28 '05 #1
Share this Question
Share on Google+
11 Replies


P: n/a
Hi Graham,

Thanks for your post.

No, normally, there is no easy way to get this done. The default context
menu for the combobox/textbox is a win32 build-in context menu, which is
not a .Net ContextMenu class. The only way to get rid of it is supply a
..Net ContextMenu for the Control.ContextMenu property, then the .Net
ContextMenu will replace the default context menu.

I am not sure of why you do not want to use a new .Net ContextMenu to
substitute the default context menu. Actually, we can implement the default
context menu cut/copy/paste operations in the new .Net ContextMenu without
any problem. Below is a little VB.net sample:
http://www.startvbdotnet.com/controls/contextmenu.aspx

If you have any further concern, please feel free to tell me, I will work
with you. Thanks

Best regards,
Jeffrey Tan
Microsoft Online Partner Support
Get Secure! - www.microsoft.com/security
This posting is provided "as is" with no warranties and confers no rights.

Dec 29 '05 #2

P: n/a
Brilliant, except not quite so easy to implement for the ComboBox
class, which lacks Copy(), Paste(), ... methods.

Any ideas?

Dec 29 '05 #3

P: n/a
Hi Graham,

Thanks for your feedback.

For Combobox, it is composited by an Edit control and a dropdownlist. So we
can first find the Edit control in the ComboBox, then do the copy/cut/paste
operations on this Edit control.

If you use Reflector to view the source code of TextBox.Cut/Copy/Paste
methods, you will see that, they simply p/invokes SendMessage API to send
WM_COPY,WM_CUT and WM_PASTE to the TextBox(Edit) control. So we can do the
same thing. Below is the sample code snippet:

Declare Function FindWindowEx Lib "user32" Alias _
"FindWindowExA" (ByVal hWnd1 As IntPtr, _
ByVal hWnd2 As IntPtr, ByVal lpsz1 As String, _
ByVal lpsz2 As String) As IntPtr
<DllImport("user32")> _
Public Shared Function SendMessage(ByVal hWnd As IntPtr, ByVal msg As
Integer, ByVal wParam As Integer, ByVal lParam As Integer) As IntPtr
End Function

Const WM_COPY As Integer = 769
Const WM_CUT As Integer = 768
Const WM_PASTE As Integer = 770
Private Sub MenuItem1_Click(ByVal sender As System.Object, ByVal e As
System.EventArgs) Handles MenuItem1.Click
Dim hEdit As IntPtr = FindWindowEx(Me.ComboBox1.Handle,
IntPtr.Zero, "Edit", String.Empty)
If hEdit.Equals(IntPtr.Zero) Then
MessageBox.Show(Marshal.GetLastWin32Error())
Else
SendMessage(hEdit, WM_COPY, 0, 0)
End If

End Sub

Private Sub MenuItem2_Click(ByVal sender As System.Object, ByVal e As
System.EventArgs) Handles MenuItem2.Click
Dim hEdit As IntPtr = FindWindowEx(Me.ComboBox1.Handle,
IntPtr.Zero, "Edit", String.Empty)
If hEdit.Equals(IntPtr.Zero) Then
MessageBox.Show(Marshal.GetLastWin32Error())
Else
SendMessage(hEdit, WM_CUT, 0, 0)
End If

End Sub

Private Sub MenuItem3_Click(ByVal sender As System.Object, ByVal e As
System.EventArgs) Handles MenuItem3.Click
Dim hEdit As IntPtr = FindWindowEx(Me.ComboBox1.Handle,
IntPtr.Zero, "Edit", String.Empty)
If hEdit.Equals(IntPtr.Zero) Then
MessageBox.Show(Marshal.GetLastWin32Error())
Else
SendMessage(hEdit, WM_PASTE, 0, 0)
End If
End Sub

It works well on my side. Hope it helps

Best regards,
Jeffrey Tan
Microsoft Online Partner Support
Get Secure! - www.microsoft.com/security
This posting is provided "as is" with no warranties and confers no rights.

Dec 29 '05 #4

P: n/a
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 (gr****@aiid.com).
''' </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

Dec 29 '05 #5

P: n/a
Hi Graham,

Thanks for your feedback.

Yes, I see your 2 concerns, I will provided some comment below:

#1, Normally, the key point is how to get the edit part control of the .Net
control genericly. Maybe we can determine the associated control type
one-by-one, then write separate code to get the edit control handle
one-by-one. Although not an elegent solution, but should can work.(it seems
that there is not many controls have edit control portion)

#2, .Net winform provided localization feature to resolve this problem. We
can first set Form.Localization property to true in propertybrowser, then
choose different language in the Language property. Then we can store
several versions of culture resource in the application resource file. At
runtime, winform will choose different resource based on the current
culture information. The below link contains several good articles
regarding .Net localization:
http://www.windowsforms.net/Articles...=Localization&
ModuleFilter=131&tabindex=3

Hope this helps

Best regards,
Jeffrey Tan
Microsoft Online Partner Support
Get Secure! - www.microsoft.com/security
This posting is provided "as is" with no warranties and confers no rights.

Dec 30 '05 #6

P: n/a
Hi Graham,

Does my reply make sense to you? Is your problem resolved? If you still
have any concern, please feel free to tell me, thanks

Best regards,
Jeffrey Tan
Microsoft Online Partner Support
Get Secure! - www.microsoft.com/security
This posting is provided "as is" with no warranties and confers no rights.

Jan 4 '06 #7

P: n/a
Hi Graham,

Form your email, I see that you still have some concern regarding this
issue. Your concern comment listed below:

"I would be curious, however, if you knew where in the Win32 API I could
extract the labels used for the text box context menu ("Cut", "Copy",
etc.). (Without that, I can't internationalize my code --- but I can't find
it anywhere!)"

I am not sure what do you mean by "extracting the labels used for the text
box context menu", if you want to do globalization to your winform, I think
you should use the .Net Winform localization feature which I provided in
last reply. Does it meet your need? If you missed that point, I will
repaste it here:

#2, .Net winform provided localization feature to resolve this problem. We
can first set Form.Localization property to true in propertybrowser, then
choose different language in the Language property. Then we can store
several versions of culture resource in the application resource file. At
runtime, winform will choose different resource based on the current
culture information. The below link contains several good articles
regarding .Net localization:
http://www.windowsforms.net/Articles...=Localization&
ModuleFilter=131&tabindex=3

If I misunderstand your point, please feel free to show your real concern
to me, I will follow up with you. Thanks

Best regards,
Jeffrey Tan
Microsoft Online Partner Support
Get Secure! - www.microsoft.com/security
This posting is provided "as is" with no warranties and confers no rights.

Jan 5 '06 #8

P: n/a
I don't want to internationalize a Winform -- I'm writing a replacement
ComboBox that extends the ComboBox's ContextMenu. (See my original
message.) Unfortunately, that ContextMenu is provided somewhere below
the CLR -- in the Win32 API, presumably, so that the labels on the
ContextMenu (Cut, Copy, etc.) aren't actually coming from any resource
file; they're coming from the localized version of Windows that the
user has installed. Which is fine, generally speaking, except when I
want to re-create that ContextMenu (Cut, Copy, etc.) but add things to
it. Is there an API call I can make to retrieve these labels? I'm
pretty sure we're talking lower than the CultureInfo class.

Jan 9 '06 #9

P: n/a
Hi Graham,

Thanks for your feedback.

No, the default contextmenu is not constituted by labels. It is just
resource for many Win32 controls, this resource is embeded in User32.dll,
you can open this user32.dll in System32 directory with VS.net and see the
contextmenu resource.

I am not sure why original suggestion of using a .Net context menu to
implement the default contextmenu function does not meet your need? Can you
show me your key concern to me? Thanks

Best regards,
Jeffrey Tan
Microsoft Online Partner Support
Get Secure! - www.microsoft.com/security
This posting is provided "as is" with no warranties and confers no rights.

Jan 10 '06 #10

P: n/a
Sure, I'll clarify. Your original suggestion ("using a .Net context
menu to implement the default contextmenu function") works fine, it's
just not nearly as simple as you seemed to think it would be -- that's
why I had to write that longish replacement, as posted.

You'll remember, I wanted to add an item to the default ComboBox
ContextMenu. In other words, I wanted all the usual CTextBox menu items
and behaviors (Copy, Cut, Paste, Undo), as well as my own.
Unfortunately, if you set the ContextMenu property of a ComboBox, you
lose the default menu entirely. So I needed a way to replicate the
"normal" CTextBox context menu in a way that is extensible.

However, with my code, you'll get the English menu item labels ("Copy,
Cut") no matter what the user's copy of Windows would indicate. (In
France, the labels are in French.) So I thought I'd see if there was an
easy way to retrieve those labels from the Windows API. There doesn't
appear to be, as far as I can tell.

Yours,

Graham Charles

Jan 21 '06 #11

P: n/a
Hi Graham,

Thanks for your feedback.

The default contextmenu is the resource embeded in user32.dll, and the
common control does not expose a way to get this resource. I think we have
to re-implement the contextmenu to get the similiar function.

For the localization issue, just as I pointed in original reply, .Net
introduced Localization feature, which we can use to show different
language context menu. Thanks

Best regards,
Jeffrey Tan
Microsoft Online Partner Support
Get Secure! - www.microsoft.com/security
This posting is provided "as is" with no warranties and confers no rights.

Jan 25 '06 #12

This discussion thread is closed

Replies have been disabled for this discussion.