Ctrl+Shift+X conflicts with Ctrl+X action - vb.net

We kept a menu item short cut key (Ctrl+Shift+X) to pop up another form in Windows VB application. But when user tried to cut using (Ctrl+X) text from any field the above short cut action is firing and popping up another form.
No sure anything we missed or we should not use (Ctrl+Shift+X) as shortcut.
Me.mnuTools.Shortcut = System.Windows.Forms.Shortcut.CtrlShiftX
worst case we can change short cut but we wish to know is it something wrong using the short cut.
Please provide your views.

I can't answer why it thinks CTRL + X should call a CTRL + SHIFT + X hotkey, but I do believe I have a workaround.
When a cut action is performed Windows sends a WM_CUT message to the focused control. If we override ProcessCmdKey() we can stop the CTRL + X combination from reaching any child controls of the form and instead send the WM_CUT message ourselves.
Untested, but I believe it should work:
<DllImport("user32.dll")> _
Private Shared Function SendMessage(ByVal hWnd As IntPtr, ByVal Msg As UInteger, ByVal wParam As IntPtr, ByVal lParam As IntPtr)
End Function
Private Const WM_CUT As Integer = &H300
Protected Overrides Function ProcessCmdKey (ByRef msg As Message, keyData As Keys) As Boolean
If keyData = (Keys.ControlKey Or Keys.X) AndAlso _
Me.ActiveControl IsNot Nothing Then
SendMessage(Me.ActiveControl.Handle, WM_CUT, IntPtr.Zero, IntPtr.Zero) 'Send the WM_CUT message to the currently focused control.
Return True 'Indicate that we've handled the message and stop the key strokes from reaching any child controls.
End If
End Function
EDIT:
Future readers: I am aware that you can define the hotkeys on your own and call the respective methods in KeyDown or ProcessCmdKey, but I used the method above so that the menu item still can display its CTRL + SHIFT + X hint.

Related

Opening form's context menu

I wanted to add a dropdown with a few options(shortcuts) next to the minimize button on my forms. To that end I followed the following example on Code Project https://www.codeproject.com/Articles/55180/Extending-the-Non-Client-Area-in-Aero.
This worked perfectly for what I wanted (I considered hiding the Border style but then I wouldnt be able to Dock the form on the edges of the screen) but for some reason disabled the ability to call the context menu on the title bar.
I noticed in the example that the WM_NCHITTEST event's override was not using the HTSYSMENU result and only returned HTCAPTION for the entire area of the title bar. I then changed the code to return the correct value if the mouse was on the left side of the title bar and with this I managed to call the context menu by left clicking on that corner.
Dim p As New Point(LoWord(CInt(lparam)), HiWord(CInt(lparam)))
Dim cap As Rectangle = RectangleToScreen(New Rectangle(0, dwmMargins.cxLeftWidth, Width, dwmMargins.cyTopHeight - dwmMargins.cxLeftWidth))
If cap.Contains(p) Then
Dim mp As Point = MousePosition
mp = PointToClient(mp)
If mp.X < 30 Then
Return New IntPtr(HTSYSMENU)
Else
Return New IntPtr(HTCAPTION)
End If
End If
I can't however find how to activate the call to the context menu when right clicking anywhere on the title bar.
I tried overriding the message WM_NCRBUTTONUP on WndProc and send a message to show the Context Menu but nothing seems to happen.
Dim WM_CONTEXTMENU As Integer = &H7B
If m.Msg = WM_NCRBUTTONUP Then
Dim mp As Point = MousePosition
mp = PointToClient(mp)
Dwm.SendMessage(Me.Handle, WM_CONTEXTMENU, Me.Handle, Dwm.MAKEWPARAM(mp.Y, mp.X))
<DllImport("user32.dll")>
Public Shared Function SendMessage(ByVal hWnd As IntPtr, ByVal Msg As UInteger, ByVal wParam As IntPtr, ByVal lParam As IntPtr) As Integer
End Function
I believe that I'm trying to reinvent the wheel and that I shouldn't need to be handling the WM_NCRBUTTONUP message to call the context menu since it should be handled automatically but I haven't found any information about it online.
As an option, you can handle WM_NCRBUTTONUP and send a WM_POPUPSYSTEMMENU:
case WM_NCRBUTTONUP:
{
SendMessage(this.Handle, 0x313/*WM_POPUPSYSTEMMENU*/, (IntPtr)0, m.LParam);
break;
}
And this signature of SendMessage which I used:
[DllImport("user32.dll")]
static extern IntPtr SendMessage(IntPtr hWnd, int msg, IntPtr wParam, IntPtr lParam);

VB Press and hold keystroke

I am creating a macro program to record and play back mouse and keyboard input. Recording works fine, as does mouse playback, but I am having trouble in playing back the keyboard input - specifically pressing and holding a key for several seconds before releasing. This is not equivalent to repetitively pressing the key. This is what I have tried:
Technique 1: Me.KeyDown
Private Sub keyboard_pressed() Handles Me.KeyDown
Dim keypress = e.KeyData
MsgBox(keypress)
End Sub
Only works when window is in focus.
Technique 2: SendKeys
Private Sub timer_keyboardplayback_Tick() Handles timer_playback.Tick
SendKeys.Send("{LEFT}")
timer_playback.Interval = 30
End Sub
Works out of focus, but repetitively presses left arrow rather than press and hold arrow
Technique 3: keybd_event
Public Declare Sub mouse_event Lib "user32" Alias "mouse_event" (ByVal dwFlags As Long, ByVal dx As Long, ByVal dy As Long, ByVal cButtons As Long, ByVal dwExtraInfo As Long)
Private Sub timer_keyboardplayback_Tick() Handles timer_playback.Tick
Const keydown = &H1
Const keyup = &H2
Dim VK_LEFT = 37
keybd_event(VK_LEFT, 0, keydown, 0)
End Sub
Works out of focus, but still fails to press hold arrow
Can someone please show me how I can achieve a press & hold of the Left Arrow Key for several seconds, and then release.
The keybd_event and mouse_event functions are deprecated as of a few years ago. Instead you should use the SendInput() function.
Simulating input with it from .NET can sometimes be a bit tricky, fortunately though I've written a library called InputHelper (Download from GitHub) which is a wrapper around SendInput(). I've customized it so that it covers some of the many different ways of input handling and input simulation, mainly:
Simulate keystrokes (internally utilizing SendInput()).
Simulate mouse movement and mouse button clicks (also utilizing SendInput() internally).
Send virtual keystrokes and mouse clicks to the current/a specific window (internally utilizing Window Messages).
Create global, low-level mouse and keyboard hooks.
Unfortunately I've not yet had the time to write a proper documentation/wiki about this (apart from the XML documentation on each member of the library, which is shown by Visual Studio's IntelliSense), but so far you can find a little info about creating hooks on the project's wiki.
A short description of what this library consist of:
InputHelper.Hooks
For creating global, low-level mouse/keyboard hooks (utilizes SetWindowsHookEx() and other related methods). This is partially covered in the wiki.
InputHelper.Keyboard
For handling/simulating physical keyboard input (utilizes SendInput() and GetAsyncKeyState()).
InputHelper.Mouse
For handling/simulating physical mouse input (utilizes SendInput()).
InputHelper.WindowMessages
For handling/simulating virtual mouse/keyboard input, for instance to a specific window (utilizes SendMessage() and PostMessage()).
Sending a keystroke
Sending a "physical" keystroke can be done via two functions:
InputHelper.Keyboard.PressKey(Key As Keys, Optional HardwareKey As Boolean)
Sends two keystrokes (down and up) of the specified key.
If HardwareKey is set, the function will send the key's Scan Code instead of its Virtual Key Code (default is False).
InputHelper.Keyboard.SetKeyState(Key As Keys, KeyDown As Boolean, Optional HardwareKey As Boolean)
Sends a single keystroke of the specified key.
If KeyDown is True the key will be sent as a KEYDOWN event, otherwise KEYUP.
HardwareKey is the same as above.
You'd use the latter, since you want to control for how long you want the key to be held down.
Holding a key down for the specified amount of time
In order to do this you need to use some sort of timer, like you already do. However to make things a bit more dynamic I've written a function that'll let you specify which key to hold down, and also for how long.
'Lookup table for the currently held keys.
Private HeldKeys As New Dictionary(Of Keys, Tuple(Of Timer, Timer))
''' <summary>
''' Holds down (and repeats, if specified) the specified key for a certain amount of time.
''' Returns False if the specified key is already being held down.
''' </summary>
''' <param name="Key">The key to hold down.</param>
''' <param name="Time">The amount of time (in milliseconds) to hold the key down for.</param>
''' <param name="RepeatInterval">How often to repeat the key press (in milliseconds, -1 = do not repeat).</param>
''' <remarks></remarks>
Public Function HoldKeyFor(ByVal Key As Keys, ByVal Time As Integer, Optional ByVal RepeatInterval As Integer = -1) As Boolean
If HeldKeys.ContainsKey(Key) = True Then Return False
Dim WaitTimer As New Timer With {.Interval = Time}
Dim RepeatTimer As Timer = Nothing
If RepeatInterval > 0 Then
RepeatTimer = New Timer With {.Interval = RepeatInterval}
'Handler for the repeat timer's tick event.
AddHandler RepeatTimer.Tick, _
Sub(tsender As Object, te As EventArgs)
InputHelper.Keyboard.SetKeyState(Key, True) 'True = Key down.
End Sub
End If
'Handler for the wait timer's tick event.
AddHandler WaitTimer.Tick, _
Sub(tsender As Object, te As EventArgs)
InputHelper.Keyboard.SetKeyState(Key, False) 'False = Key up.
WaitTimer.Stop()
WaitTimer.Dispose()
If RepeatTimer IsNot Nothing Then
RepeatTimer.Stop()
RepeatTimer.Dispose()
End If
HeldKeys.Remove(Key)
End Sub
'Add the current key to our lookup table.
HeldKeys.Add(Key, New Tuple(Of Timer, Timer)(WaitTimer, RepeatTimer))
WaitTimer.Start()
If RepeatTimer IsNot Nothing Then RepeatTimer.Start()
'Initial key press.
InputHelper.Keyboard.SetKeyState(Key, True)
Return True
End Function
Example usage:
'Holds down 'A' for 5 seconds, repeating it every 50 milliseconds.
HoldKeyFor(Keys.A, 5000, 50)

How to prevent the Console window being resized in VB.NET?

I need to prevent the user of my console program from resizing the window, only allowing it to be changed programmatically. If the user changes the width, everything messes up. Also, I want to disable the maximise button. Are either of these possible in Console?
This answer neatly covers how to disable resizing a form in WinForms, but it won't work for Console.
I came up with a solution that prevents re-sizing of a console window application (either by dragging the corner border or by clicking on the maximize or minimize buttons). The following code is written in the form of a complete VB.Net console application (i.e., a Module):
Module Module1
Private Const MF_BYCOMMAND As Integer = &H0
Public Const SC_CLOSE As Integer = &HF060
Public Const SC_MINIMIZE As Integer = &HF020
Public Const SC_MAXIMIZE As Integer = &HF030
Public Const SC_SIZE As Integer = &HF000
Friend Declare Function DeleteMenu Lib "user32.dll" (ByVal hMenu As IntPtr, ByVal nPosition As Integer, ByVal wFlags As Integer) As Integer
Friend Declare Function GetSystemMenu Lib "user32.dll" (hWnd As IntPtr, bRevert As Boolean) As IntPtr
Sub Main()
Dim handle As IntPtr
handle = Process.GetCurrentProcess.MainWindowHandle ' Get the handle to the console window
Dim sysMenu As IntPtr
sysMenu = GetSystemMenu(handle, False) ' Get the handle to the system menu of the console window
If handle <> IntPtr.Zero Then
DeleteMenu(sysMenu, SC_CLOSE, MF_BYCOMMAND) ' To prevent user from closing console window
DeleteMenu(sysMenu, SC_MINIMIZE, MF_BYCOMMAND) 'To prevent user from minimizing console window
DeleteMenu(sysMenu, SC_MAXIMIZE, MF_BYCOMMAND) 'To prevent user from maximizing console window
DeleteMenu(sysMenu, SC_SIZE, MF_BYCOMMAND) 'To prevent the use from re-sizing console window
End If
Do Until (Console.ReadKey.Key = ConsoleKey.Escape)
'This loop keeps the console window open until you press escape
Loop
End Sub
End Module
I based this answer off of the Stack Overflow question/answer: "Disable the maximize and minimize buttons of a c# console [closed]"
Please let me know if this doesn't work for you. Good luck!
I wanted to comment on the accepted answer, but I lack reputation...
To prevent the console window from resizing when you snap it to a corner, you can use
Console.SetBufferSize(80, 24) ' or whatever size you're using...

Button in TextBox disappears when selecting text while Windows media player plays music

This is my first question, please be nice. Oh, and I'm not a native english speaker. :)
I've discovered some weird bug in my application. I've created a TextBox control with a button in it (code below). Following these steps will make the button disappear.
Start windows media player with some music (or video)
Start the application
Click in TextBox control and hold down left mouse button
Move your cursor around like crazy
Woosh... Button disappears.
This will not happen when you closed or paused your windows media player. I was able to reproduce this bug on a different system (Windows 7 and Windows 10). This is totally weird, because it doesn't seem logical. Windows is doing crazy stuff with the windows media player...
I'm not sure if there's a workaround. Can anybody help me with this or should I ask on Microsoft forums? I've tried to call "UpdateButton" while selection changed, but I wasn't successful.
Public Class TextBoxEx
Inherits TextBox
Const BUTTON_WIDTH As Integer = 18
Const EM_SETMARGINS As Integer = &HD3
Const EC_RIGHTMARGIN As Integer = &H2
<Runtime.InteropServices.DllImport("user32.dll", SetLastError:=True)> _
Private Shared Function SendMessage(ByVal hWnd As IntPtr, ByVal Msg As UInteger, ByVal wParam As Integer, ByVal lParam As Integer) As IntPtr
End Function
Private btnCommand As Button
Public Sub New()
btnCommand = New Button
btnCommand.Cursor = Cursors.Default
btnCommand.Image = My.Resources.iconCancel
Me.Controls.Add(btnCommand)
Call UpdateButton()
End Sub
Private Sub UpdateButton()
Dim rightMargin As Integer = (BUTTON_WIDTH + 1) << 16
btnCommand.Size = New Size(BUTTON_WIDTH, Me.ClientSize.Height)
btnCommand.Location = New Point(Me.ClientSize.Width - BUTTON_WIDTH, 0)
Call SendMessage(Me.Handle, EM_SETMARGINS, EC_RIGHTMARGIN, rightMargin)
End Sub
Protected Overrides Sub OnResize(e As System.EventArgs)
MyBase.OnResize(e)
Call UpdateButton()
End Sub
End Class

VB 2012 TextBox.Clear() not working

I'm working on an encryption program and it uses a "PIN" to calculate some stuff for the encryption. I have a textbox where the user can insert the "PIN". I'd like to prevent people from entering anything but numbers. I added this on the KeyPress event:
If Not Char.IsControl(e.KeyChar) Then
If Not Char.IsNumber(e.KeyChar) Then
MsgBox("Invalid character", , "WARNING!")
TextBox3.Clear()
End If
End If
It shows the msgbox and it doesn't write to the textbox until i close th emsgbox. The typed character appears in the textbox. When I write another one it works the same as before, but it only replaces the last character instead of writing another one. Is there something I'm missing because that looks like a bug to me?
Set the ES_NUMBER windows style for your TextBox:
Public Class Form1
Public Const GWL_STYLE As Integer = (-16)
Public Const ES_NUMBER As Integer = &H2000
Public Declare Function GetWindowLong Lib "user32" Alias "GetWindowLongA" _
(ByVal handle As IntPtr, ByVal nIndex As Integer) As Integer
Public Declare Function SetWindowLong Lib "user32" Alias "SetWindowLongA" _
(ByVal handle As IntPtr, ByVal nIndex As Integer, ByVal dwNewLong As Integer) As Integer
Public Sub SetNumbersOnlyTextBox(ByVal TB As TextBox)
SetWindowLong(TB.Handle, GWL_STYLE, GetWindowLong(TB.Handle, GWL_STYLE) Or ES_NUMBER)
End Sub
Private Sub Form1_Load(sender As System.Object, e As System.EventArgs) Handles MyBase.Load
SetNumbersOnlyTextBox(TextBox3)
End Sub
End Class
It shows the msgbox and it doesn't write to the textbox until i close th emsgbox.
Yes, that's what modal dialogs do. They block the caller from updates until closed. That's the point; the user cannot interact with the parent until they clear the modal child.
Why not simply clear the textbox first? Better yet; don't show an annoying dialog at all. Simply disallow the user from entering invalid characters by setting e.Handled to true. However, it's a bit trickier than it sounds as you need to allow for the backspace and delete keys, disable pasting, etc.
Here's an example of a NumericTextbox: http://msdn.microsoft.com/en-us/library/ms229644(v=vs.80).aspx
You just need to set the Handled property to true instead of clear:
e.Handled = True
As MarkPM notes above, if its a key you don't want you can set e.handle=true (as you intercept the key on the keypress event) to have the system eat it.
Along with this, in stead of a pop-up, you can have a label on the form that says "Only numbers can be entered here" or something like that. Set it up so that the color of the text is red. Also set it up so the label is not normally visible.
Finally, also in the keypress event, beyond setting e.handle=true for unwanted keys, when an unwanted key comes along make the label that says "Only numbers can be entered here" visible - you can also set up a timed event to turn the label's visibility off after a few seconds. You can also throw a Beep() into the mix if you like :-)
This is less invasive then a pop-up and moves things along nicely for the user.