-1

I would like to know if this code can be adapted to use it inside a normal class (Example: Form1 Class) to don't need subclassing a Control.

Code is from BlueMonkMN here Capture the option selected by the user in a windows default contextmenu?

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

To understand better my problem, I'll show you what I've tried to adapt it:

Public Class Form1

    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(TextBox1 As Object, e As ContextCommandEventArgs)
    Event OnCopy(TextBox1 As Object, e As ContextCommandEventArgs)
    Event OnPaste(TextBox1 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(TextBox1, New ContextCommandEventArgs() With {.Command = ContextCommands.WM_CUT})
            Case ContextCommands.WM_COPY
                RaiseEvent OnCopy(TextBox1, New ContextCommandEventArgs() With {.Command = ContextCommands.WM_COPY})
            Case ContextCommands.WM_PASTE
                RaiseEvent OnPaste(TextBox1, New ContextCommandEventArgs() With {.Command = ContextCommands.WM_PASTE})
        End Select
    End Sub

Private Sub TextBox1_OnTextCommand() Handles TextBox1.TextChanged
    If WM_CUT then...
    Elseif WM_COPY then...
    Elseif WM_Paste then...
    End if
End Sub

Private Sub TextBox1_OnTextCommand() Handles Me.OnPaste, Me.OnCopy, Me.OnCut
    MsgBox("Activated")
End Sub

End Class
Community
  • 1
  • 1
ElektroStudios
  • 19,105
  • 33
  • 200
  • 417
  • 1
    Can you explain why you need to use Krypton textboxes instead of normal textboxes? Is it true that a Krypton textbox does not provide the overridable WndProc? I would be hesitant to rely on a closed-source control like a krypton textbox when you need to have such a fine level of control over it. – BlueMonkMN Oct 24 '13 at 12:56

1 Answers1

1

This is not a simple task for a VB developer, but I hope this helps. The code no longer relates to a specific textbox, but you should be able to identify which textbox caused the event by comparing cwp.hWnd with TextBox.Handle values if you need to. EditMenuHook.Enable should probably only be called once at the beginning of your application to turn on the hook, and once at the end to turn it off.

Imports System.Runtime.InteropServices

Public Class Form1
   Protected Overrides Sub OnLoad(e As EventArgs)
      MyBase.OnLoad(e)
      EditMenuHook.Enable(True)
   End Sub

   Protected Overrides Sub OnClosed(e As EventArgs)
      EditMenuHook.Enable(False)
      MyBase.OnClosed(e)
   End Sub
End Class

Friend Class EditMenuHook
   Public Structure CWPSTRUCT
      Public lParam As IntPtr
      Public wParam As IntPtr
      Public message As UInt32
      Public hWnd As IntPtr
   End Structure

   Public Delegate Function CallBack( _
       ByVal nCode As Integer, _
       ByVal wParam As IntPtr, _
       ByVal lParam As IntPtr) As Integer

   Shared hHook As Integer = 0

   'Import for the SetWindowsHookEx function.
   <DllImport("User32.dll", CharSet:=CharSet.Auto, CallingConvention:=CallingConvention.StdCall)> _
   Public Overloads Shared Function SetWindowsHookEx _
          (ByVal idHook As Integer, ByVal HookProc As CallBack, _
           ByVal hInstance As IntPtr, ByVal wParam As Integer) As Integer
   End Function

   'Import for the CallNextHookEx function.
   <DllImport("User32.dll", CharSet:=CharSet.Auto, CallingConvention:=CallingConvention.StdCall)> _
   Public Overloads Shared Function CallNextHookEx _
          (ByVal idHook As Integer, ByVal nCode As Integer, _
           ByVal wParam As IntPtr, ByVal lParam As IntPtr) As Integer
   End Function

   'Import for the UnhookWindowsHookEx function.
   <DllImport("User32.dll", CharSet:=CharSet.Auto, CallingConvention:=CallingConvention.StdCall)> _
   Public Overloads Shared Function UnhookWindowsHookEx _
              (ByVal idHook As Integer) As Boolean
   End Function

   Private Const WH_CALLWNDPROC = 4

   Public Enum TextCommandMessage
      WM_CUT = &H300
      WM_COPY = &H301
      WM_PASTE = &H302
   End Enum

   'Keep the reference so that the delegate is not garbage collected.
   Private Shared hookproc As CallBack

   Shared Sub Enable(enable As Boolean)
      If hHook = 0 AndAlso enable = True Then
         hookproc = AddressOf EditCommandHook
         hHook = SetWindowsHookEx(WH_CALLWNDPROC, _
                                  hookproc, _
                                  IntPtr.Zero, _
                                  AppDomain.GetCurrentThreadId())
         If hHook.Equals(0) Then
            MsgBox("SetWindowsHookEx Failed")
            Return
         End If
      ElseIf hHook <> 0 AndAlso enable = False Then
         Dim ret As Boolean = UnhookWindowsHookEx(hHook)

         If ret.Equals(False) Then
            MsgBox("UnhookWindowsHookEx Failed")
            Return
         Else
            hHook = 0
         End If
      End If
   End Sub

   Public Shared Function EditCommandHook( _
      ByVal nCode As Integer, _
      ByVal wParam As IntPtr, _
      ByVal lParam As IntPtr) As Integer

      If nCode < 0 Then
         Return CallNextHookEx(hHook, nCode, wParam, lParam)
      End If

      Dim cwp = DirectCast(System.Runtime.InteropServices.Marshal.PtrToStructure(lParam, GetType(CWPSTRUCT)), CWPSTRUCT)

      Select Case cwp.message
         Case TextCommandMessage.WM_CUT
            System.Diagnostics.Debug.WriteLine(String.Format("Cut {0}", cwp.hWnd))
         Case TextCommandMessage.WM_COPY
            System.Diagnostics.Debug.WriteLine(String.Format("Copy {0}", cwp.hWnd))
         Case TextCommandMessage.WM_PASTE
            System.Diagnostics.Debug.WriteLine(String.Format("Paste {0}", cwp.hWnd))
      End Select

      Return CallNextHookEx(hHook, nCode, wParam, lParam)
   End Function
End Class
BlueMonkMN
  • 25,079
  • 9
  • 80
  • 146
  • Please, could you explain how to use it? I don't find where I need to pass the textbox handle. I've just put this inside the Load event `EditMenuHook.Enable(True)` to turn it on but I don't know what to do now. – ElektroStudios Oct 30 '13 at 11:46
  • Can you see Debug.WriteLine output from EditCommandHook showing up in the debug output window when you select commands from the textboxes' context menus? If so, just replace the Debug.WriteLine code with whatever you want to do when the command is selected. You don't need to use the handle if you don't care which textbox triggered the event. – BlueMonkMN Oct 30 '13 at 17:03
  • Nothing to do with a KryptonTextBox dude? – ElektroStudios Nov 04 '13 at 11:51
  • I don't understand what you're asking with, "Nothing to do with a KryptonTextBox dude?" – BlueMonkMN Nov 05 '13 at 13:48
  • sorry, I will mean that if you know how to use the code of your solution with a Krypton Textbox, because the KryptonTextbox uses the default windows edit menu (or at least that is what it seems) but the hook does not will work with that krypton textboxes. – ElektroStudios Nov 05 '13 at 14:33
  • Unfortunately I can't really answer questions about Krypton Textbox without having any experience with it. You could try adding debug code to EditCommandHook to see if the function is *ever* called when using Krypton. Maybe the parameters are just different for Krypton. But if the function is not even called, then I think you have no way to do what you want with Krypton. You need to find a completely different solution or use a different textbox. – BlueMonkMN Nov 06 '13 at 14:17