0

When right-click on a textbox which has the default Windows contextmenu I want to know if the user selects copy cut or paste option, to perform secondary operations when user has selected an specific contextmenu option.

I have no code 'cause I don't know where to start trying to recognize what option was selected by the user in the contextmenu, and how to capture that left click 'cause I've tried to capture the default contextmenu mouseleft click on the textbox MouseDown/Mouseclick events without success, I know that has not much sense 'cause it is a contextmenu mouseclick, not a Textbox mouseclick, but well... I don't know how to manage that external contextmenu.

ElektroStudios
  • 19,105
  • 33
  • 200
  • 417
  • 2
    Since you want to supply your own actions you should replace the default ContextMenu - even if those actions are 'secondary'. Probably less code and easier than trying to hook into or piggyback Win/Net's. – Ňɏssa Pøngjǣrdenlarp Oct 21 '13 at 12:43
  • @Plutonix Thats how I have always handled it. Then you can either manually add stuff to the clipboard or just call the shortcurt key `SendKeys.SendWait("[CTRL]+C")` – Steve Oct 21 '13 at 15:23
  • Just an example: suppose that I only want to throw a MsgBox when user selects "copy" or "paste" option in the default contextmenu, that's what I'm asking for. Thanks for read! – ElektroStudios Oct 21 '13 at 15:34
  • @Plutonix I'm not interested into replace the default contextmenu to a custom contextmenu in this application, `Probably less code and easier than...` depends on if it would be an easy cm or a full replacement, the default cm has multiple IME options and you really will need a lot of code to reproduce all of those options such as the Unicode control option... otherway just making a contextmenu with copy/cut/paste options I don't like it, but yes need only little lines of code but it not seems very profesional (for a TextBox). I don't need it in this application. – ElektroStudios Oct 21 '13 at 15:40
  • unprofessional?? I am trying to recall the last time I have seen that default CTM in any app. If you need it fine, but dont pretend it is essential most apps be able to 'inhibit Arabic form shaping'. – Ňɏssa Pøngjǣrdenlarp Oct 21 '13 at 18:05
  • 1
    I'm sorry, I did not mean to offend anybody, just for me in my opinion does not seems professional, a custom CM for any other type of control I consider it nice, but not for a TextBox control, I always seen on all apps the default CM for TextBox, but we differ in that, anyways I can't imagine which custom options could one person add to a textbox custom CM because the windows default Textbox CM has all needed options!, thanks for comment. – ElektroStudios Oct 21 '13 at 18:36
  • @ElektroStudios `the windows default Textbox CM has all needed options` but is lacking your desired features. For this reason, @Plutonix has a good solution. – djv Oct 23 '13 at 17:50
  • @DanVerdolino I was going to ask why he started the thread/posted a bounty if it was so perfect. Its apparently not possible (AFAIK) to get a reference to the default CTM and hook new events to it. But it **is** possible to subclass the CTM and have it raise a new event to notify that an operation is about to happen and allow the form code to abort the operation. From there it is just a little detective work to make it more like the exalted default (nearly all the Unicode stuff is just inserting characters and the rest is internal to the control). – Ňɏssa Pøngjǣrdenlarp Oct 23 '13 at 18:26

2 Answers2

3

You can add a class like this to your project:

Class MyTextBox : Inherits TextBox
  Public Enum ContextCommands
     WM_CUT = &H300
     WM_COPY = &H301
     WM_PASTE = &H302
  End Enum

  Public Class ContextCommandEventArgs
     Inherits EventArgs
     Public Property Command As ContextCommands
  End Class

  Event OnCut(sender As Object, e As ContextCommandEventArgs)
  Event OnCopy(sender As Object, e As ContextCommandEventArgs)
  Event OnPaste(sender As Object, e As ContextCommandEventArgs)

  Protected Overrides Sub WndProc(ByRef m As Message)
     MyBase.WndProc(m)
     Select Case m.Msg
        Case ContextCommands.WM_CUT
           RaiseEvent OnCut(Me, New ContextCommandEventArgs() With {.Command = ContextCommands.WM_CUT})
        Case ContextCommands.WM_COPY
           RaiseEvent OnCopy(Me, New ContextCommandEventArgs() With {.Command = ContextCommands.WM_COPY})
        Case ContextCommands.WM_PASTE
           RaiseEvent OnPaste(Me, New ContextCommandEventArgs() With {.Command = ContextCommands.WM_PASTE})
     End Select
  End Sub
End Class

Then you can replace all occurrences of "TextBox" in the Designer.vb files with "MyTextBox". Then you will have access to 3 new events for Cut, Copy and Paste. You can handle them like this:

Private Sub TextBox1_OnTextCommand(sender As Object, e As MyTextBox.ContextCommandEventArgs) _
    Handles TextBox1.OnCut, TextBox1.OnPaste, TextBox1.OnCopy
    MessageBox.Show("Activated " & e.Command.ToString())
End Sub

Notice how I've chosen to handle all 3 events in one function in this case, but you could handle them in separate functions as well. I noticed that the cut command seems to also cause a copy command event, but I will assume for now that you can deal with that slight complication.

BlueMonkMN
  • 25,079
  • 9
  • 80
  • 146
  • Great solution, thankyou so much!. The WM_COPY message is sent before together with the WM_CUT when I click on "Cut" CMT option, that could be fixed please?, I ask this because I need to manage both WM_COPY and WM_CUT that will give me a WM_COPY false positive when i click "CUT" option. – ElektroStudios Oct 24 '13 at 07:34
  • Yes, I already mentioned that behavior in my reply. Can you ignore wm_copy? Why do you care about capturing wm_copy? I don't know how to "fix" that but I'm not sure it's really wrong. Cut really is kind of a copy followed by a cut/delete. So I don't know why your code cares. What does your code need to do if the user just selected copy? – BlueMonkMN Oct 24 '13 at 09:18
  • Its hard to explain, but no matter I think I could use a timer to measure the time between a wm_copy + wm_delete to recognize when the user click on "cut" – ElektroStudios Oct 24 '13 at 10:34
  • Just maybe you could tell me the constant for a wm_delete to make what I need? I can't found it in MSDN, thanks for your time – ElektroStudios Oct 24 '13 at 10:34
  • Found: WM_CLEAR = &H303, but unafortunatelly your class will not work when inheriting 3rd party Textbox (ex: Krypton Textbox), please see new question here if you could: http://stackoverflow.com/questions/19560844/adapt-windows-messages-in-this-class – ElektroStudios Oct 24 '13 at 10:53
0

If someone need this, This is a modification of @BlueMonkMN code to work properly with the CUT option, and also added the DELETE option.

Class MyTextBox : Inherits TextBox

Private Last_Command As ContextCommands = Nothing

Private WithEvents CopyOrCut_Timer As New Timer _
        With {.Interval = 5, .Enabled = False}

Public Enum ContextCommands
    WM_CUT = &H300
    WM_COPY = &H301
    WM_PASTE = &H302
    WM_DELETE = &H303
End Enum

Public Class ContextCommandEventArgs
    Inherits EventArgs
    Public Property Command As ContextCommands
End Class

Event OnCut(sender As Object, e As ContextCommandEventArgs)
Event OnCopy(sender As Object, e As ContextCommandEventArgs)
Event OnPaste(sender As Object, e As ContextCommandEventArgs)
Event OnDelete(sender As Object, e As ContextCommandEventArgs)

Protected Overrides Sub WndProc(ByRef m As Message)

    MyBase.WndProc(m)

    Select Case m.Msg

        Case ContextCommands.WM_COPY
            Last_Command = ContextCommands.WM_COPY
            CopyOrCut_Timer.Enabled = True

        Case ContextCommands.WM_CUT
            Last_Command = ContextCommands.WM_CUT

        Case ContextCommands.WM_PASTE
            RaiseEvent OnPaste(Me, New ContextCommandEventArgs() _
                                   With {.Command = ContextCommands.WM_PASTE})

        Case ContextCommands.WM_DELETE
            RaiseEvent OnDelete(Me, New ContextCommandEventArgs() _
                                    With {.Command = ContextCommands.WM_DELETE})

    End Select

End Sub

Private Sub Cut_Timer_Tick(sender As Object, e As EventArgs) _
Handles CopyOrCut_Timer.Tick

    sender.enabled = False

    Select Case Last_Command

        Case ContextCommands.WM_COPY
            RaiseEvent OnCopy(Me, New ContextCommandEventArgs() _
                                  With {.Command = ContextCommands.WM_COPY})

        Case ContextCommands.WM_CUT
            RaiseEvent OnCut(Me, New ContextCommandEventArgs() _
                                 With {.Command = ContextCommands.WM_CUT})

    End Select

    Last_Command = Nothing

End Sub

End Class
ElektroStudios
  • 19,105
  • 33
  • 200
  • 417