Opening form's context menu - vb.net

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);

Related

Ctrl+Shift+X conflicts with Ctrl+X action

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.

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

Visual Basic, what is the send key for this button?

here is a link to the button im referring to
http://blog.laptopmag.com/wpress/wp-content/uploads/2012/07/ThinkPad-Keyboard-Face-Off_g4-T420.jpg
and the format im trying to find it coincides with this sort of formatting
{PGUP} = page up button
{LEFT} = left arrow
{ESC} = Escape
anybody know how you can send this as a sendkey? prefer to use this keyboard orientated method than sending right click to a screen as that causes its own issues with finding location through an emulator screen.
cheers to anyone who can help me, even if someone knows for certain it doesn't exist it means i can focus on something else instead :)
EDIT:
TL;DR
"+{F10} might be what you are looking for to bring up what is called the 'context menu' however if you are using a citrix (or similar) application then you might have issues with stuff not being in focus." :)
So what I have discovered on my own. there are several ways to send what I now know is the 'context menu' key. only one however was applicable to my situation.
if I was able to write the code for sendkey then drarig29's answer might have worked but with the application I'm using (BluePrism I can not do it that way unfortunately) this was my solution (that doesn't work)
"+{F10}"
so '+' = SHIFT and F10 = F10 button. So shift F10 will work for people who are looking for a way to bring up the 'right-click menu' however this will not work for me, though im not sure why. I'm automating an application through citrix (emulator screen) and even though I make the mouse click on the screen and the use the sendKey "+{F10}" it does not make the context menu screen appear on the application. So I tried it manually and funny enough it also doesn't work in bringing up the context menu. Then I found out if you right click the area of the screen and the menu comes up then the send key works all of a sudden with no hitch. I think the problem is to do with some windows being in focus and some not but I have to work it out to be sure seeing as i sent a 'click' to the right area of the screen it should be in focus. but anyways cheers for the help :)
This is the context menu key. Its keycode is 93. To send a key using its keycode, use this :
<DllImport("user32.dll")> _
Private Shared Function keybd_event(bVk As Byte, bScan As Byte, dwFlags As UInteger, dwExtraInfo As Integer) As Boolean
End Function
Const KEYEVENTF_KEYDOWN = &H0
Const KEYEVENTF_KEYUP = &H2
Private Sub SendKey(KeyCode As Integer)
keybd_event(CByte(KeyCode), 0, KEYEVENTF_KEYDOWN, 0)
keybd_event(CByte(KeyCode), 0, KEYEVENTF_KEYUP, 0)
End Sub
You have to import System.Runtime.InteropServices (Imports System.Runtime.InteropServices).
With this, to send context menu key, use SendKey(93).
Edit :
Declare Sub mouse_event Lib "user32" Alias "mouse_event" (ByVal dwFlags As Integer, ByVal dx As Integer, ByVal dy As Integer, ByVal cButtons As Integer, ByVal dwExtraInfo As Integer)
Const MOUSEEVENTF_LEFTDOWN As Int32 = &H2
Const MOUSEEVENTF_LEFTUP As Int32 = &H4
Const MOUSEEVENTF_RIGHTDOWN As Int32 = &H8
Const MOUSEEVENTF_RIGHTUP As Int32 = &H10
Enum ClickType
Left = 0
Right = 1
End Enum
Sub SendClick(ClickType As ClickType, DestX As Integer, DestY As Integer)
Select Case ClickType
Case ClickType.Left
Cursor.Position = New Point(DestX, DestY)
mouse_event(MOUSEEVENTF_LEFTDOWN, 0, 0, 0, 0)
mouse_event(MOUSEEVENTF_LEFTUP, 0, 0, 0, 0)
Case ClickType.Right
Cursor.Position = New Point(DestX, DestY)
mouse_event(MOUSEEVENTF_RIGHTDOWN, 0, 0, 0, 0)
mouse_event(MOUSEEVENTF_RIGHTUP, 0, 0, 0, 0)
End Select
End Sub
Use the previous method like that : SendClick(ClickType.Left, 20, 20)

Outlook VBA Macro: Best way to indicate 'please wait'

What's the best practice for indicating to the user that a Macro is running within Outlook ?
The macro can take around 1-30 seconds to complete.
I want to avoid a modal 'msgbox' popping up before the macro is run, as this can be annoying.
I would rather avoid the hourglass cursor if possible, and wondered if there was a better way.
Is there a way of placing a non-modal 'status' message up, whilst the macro is running?
(The macro I have runs against the currently selected mailItem - and it launched by a button on the Quick Access Toolbar).
This article (also this) on best practice says use the status bar.
This article on Outlook says:
Changing the Status Bar
There is no
way to change the status bar text in
Microsoft Outlook. The status bar is
not exposed as it is in other
Microsoft Office object models.
Outlook.com provides code for a progress box.
Couple of things that string to mind, I am sure other will have ideas as well.
1.Show a form with a progress bar on it that reports progress or has the progress bar in marque mode if you can’t report progress
2.Show a form with a picture box with your favourite animated gif inside(spinny pizza etc.). You can turn off the buttons etc.
3. Use win api to get play with the outlook staus bar
Not knowing what you are doing in your macro you may have to deal with keeping the form “On top” and pumping async progress into it.
Cheers
Marcus
Expanding on #76mel's answer, a nice way to do this is with a non-modal userform. Make something really simple with just a label and caption like this:
What I like to do is have the userform set as:
Non modal (in properties F4, set ShowModal to false)
This means you can click outside the status bar and it doesn't stop you.
I set the StartupPosition to 0-Manual and Top and Left to something like 100 so that the Status form appears in the top left corner of the screen (out of the way of any other messages which appear in centre by default)
Set the label's value to some default text for when the Userform first loads
Public strStatus As String
Public Const defaultStatus As String = "Default status text" 'set this to whatever you want
Sub statusReporter()
frmStatus.Show
'''
'Your code here
'''
frmStatus.lblStatus = "Step 1"
'...
frmStatus.lblStatus = "Step 2"
'...
'''
'Unload the form
'''
frmStatus.lblStatus = defaultStatus
frmStatus.Hide
End Sub
Note, like with Excel's Application.Statusbar you must reset the userform to its default value if you plan to use it later on in the same instance of Excel
Optionally use this too
'Written By RobDog888 - VB/Office Guru™
'Add a Command Button so you can toggle the userform's topmost effect
Private Declare Function FindWindow Lib "user32.dll" Alias "FindWindowA" ( _
ByVal lpClassName As String, _
ByVal lpWindowName As String) As Long
Private Declare Function SetWindowPos Lib "user32" ( _
ByVal hwnd As Long, _
ByVal hWndInsertAfter As Long, _
ByVal X As Long, _
ByVal Y As Long, _
ByVal cx As Long, _
ByVal cy As Long, _
ByVal wFlags As Long) As Long
Private Const HWND_TOPMOST = -1
Private Const HWND_NOTOPMOST = -2
Private Const SWP_NOMOVE = &H2
Private Const SWP_NOSIZE = &H1
Private mlHwnd As Long
Private Sub UserForm_Initialize()
Dim overTim As Single
overTim = Timer
mlHwnd = FindWindow("ThunderDFrame", "Status") 'Change "Status" to match your userforms caption
Do While mlHwnd = 0 And Timer - overTim < 5
mlHwnd = FindWindow("ThunderDFrame", "Status")
DoEvents
Loop
'Set topmost
SetWindowPos mlHwnd, HWND_TOPMOST, 0, 0, 0, 0, SWP_NOMOVE Or SWP_NOSIZE
End Sub
in the userform code itself to keep it on top always