我正在尝试向表单的系统菜单中添加其他选项列表,并使这些选项可用作键盘快捷键。 我设法将选项添加到系统菜单,并设法抓住菜单单击这些选项。
System Menu with Shortcut List
Dim sysmenu As IntPtr = GetSystemMenu(Me.Handle, False)
Dim shortcutMenu As IntPtr = CreateMenu()
InsertMenu(sysmenu, 6, MenuFlags.MF_BYPOSITION Or MenuFlags.MF_POPUP, shortcutMenu, "&Shortcut List")
InsertMenu(shortcutMenu, 0, MenuFlags.MF_STRING, 4200, String.Format("Shortcut &2 {0}Alt + B", ControlChars.Tab))
InsertMenu(sysmenu, 7, MenuFlags.MF_BYPOSITION Or MenuFlags.MF_SEPARATOR, 0, Nothing)
但是我找不到如何添加键盘快捷键的示例。 我正在尝试关注另一个stackoverflow问题(Delphi - Adding a shortcut to a programmatically added system menu option)的答案。 他们正在其中创建带有所需快捷方式的加速器表。但是我找不到被调用方法的签名或快捷方式的结构。
'Creating accelerator table
Dim listaAccel As List(Of ACCEL) = New List(Of ACCEL)()
Dim accel As New ACCEL()
accel.fVirt = &H10 '"ALT"
accel.key = &H42 '"B"
accel.cmd = 4200
listaAccel.Add(accel)
Dim accelPointer As Integer = Marshal.SizeOf(GetType(ACCEL))
Dim arrayPointer As IntPtr = Marshal.AllocHGlobal(accelPointer * 1) ' * 2)
Marshal.StructureToPtr(listaAccel(0), arrayPointer, True)
CreateAcceleratorTable(arrayPointer, 1)
我设法在网站上找到方法签名,但是该网站没有任何其他信息,所以我不确定这是否是正确的方法。
Declare Function CreateAcceleratorTable Lib "user32" Alias "CreateAcceleratorTableA" (ByRef lpaccl As IntPtr, ByVal cEntries As Integer) As Integer
<StructLayout(LayoutKind.Sequential, CharSet:=CharSet.Auto)>
Public Class ACCEL
Public fVirt As Byte
Public key As UInt32
Public cmd As UInt32
End Class
我觉得我已经接近解决方案,但是无法找到正确的信息。
其他信息: 我使用以下代码成功捕获了新菜单上的click事件
Protected Overloads Overrides Sub WndProc(ByRef message As Message)
If message.Msg = WndValues.WM_SYSCOMMAND AndAlso message.WParam.ToInt32 = 4200 Then
Dim i = 0 ' Shortcut 2 or Alt+B activated
ElseIf message.Msg = WndValues.WM_SYSCOMMAND Then
Dim j = 0
End If
MyBase.WndProc(message)
End Sub
答案 0 :(得分:0)
很抱歉这么晚回复。花了一天的时间之后,我终于设法找到了解决方案。
首先,有几件事需要用代码来纠正:
ACCEL
应该是Structure
,而不是Class
。
ACCEL.key
和ACCEL.cmd
都应声明为UShort
,而不是UInt32
。
事实证明,CreateAcceleratorTable()
的第一个参数应声明为ByVal lpaccel As ACCEL()
(ACCEL
结构的数组)。
您需要使用当前1
值按位或FVIRTKEY
(相当于accel.fVirt
)。 FVIRTKEY
指定accel.key
是虚拟键码而不是ASCII码。后者区分大小写,前者不区分大小写。
现在,为了使加速器正常工作,您需要具有一些可处理并将其转换为SYSCOMMAND的东西。
可以使用TranslateAccelerator()
function将加速器从窗口消息转换为SYSCOMMAND。每次收到窗口消息时都需要调用此方法。但是,仅在WndProc
内调用它是不够的,因为仅将消息传递给焦点控件,这意味着如果表单的某个控件具有焦点,则表单本身将不会收到关键消息。
为解决此问题,我们可以在应用程序的消息泵中安装message filter。在到达任何形式或控件之前,这将拦截发送给应用程序的每个窗口消息。然后,我们只需将表单的窗口句柄传递给TranslateAccelerator()
,以使其将所有生成的SYSCOMMAND消息发送给它。
最后,我们应该在表单关闭以释放加速器表之前调用DestroyAcceleratorTable()
。
表单代码:
'The ID of our menu item.
Const MENU_SHORTCUT2 As Integer = 4200
'A variable holding a reference to our accelerator filter.
Dim AccelMessageFilter As KeyboardAcceleratorFilter
Private Sub MainForm_FormClosing(sender As Object, e As System.Windows.Forms.FormClosingEventArgs) Handles Me.FormClosing
'Remove our message filter and destroy the accelerator table.
Application.RemoveMessageFilter(AccelMessageFilter)
NativeMethods.DestroyAcceleratorTable(AccelMessageFilter.AcceleratorTable)
End Sub
Private Sub MainForm_Load(sender As System.Object, e As System.EventArgs) Handles MyBase.Load
'Get the system menu.
Dim SysMenu As IntPtr = NativeMethods.GetSystemMenu(Me.Handle, False)
Dim ShortcutMenu As IntPtr = NativeMethods.CreateMenu()
'Insert our custom menu items.
NativeMethods.InsertMenu(SysMenu, 6, MenuFlags.MF_BYPOSITION Or MenuFlags.MF_POPUP, ShortcutMenu, "&Shortcut List")
NativeMethods.InsertMenu(ShortcutMenu, 0, MenuFlags.MF_BYPOSITION Or MenuFlags.MF_STRING, MENU_SHORTCUT2, String.Format("Shortcut &2{0}Alt + B", ControlChars.Tab))
NativeMethods.InsertMenu(SysMenu, 7, MenuFlags.MF_BYPOSITION Or MenuFlags.MF_SEPARATOR, 0, Nothing)
'Create a keyboard accelerator for ALT + B.
Dim Accel As New NativeMethods.ACCEL()
Accel.fVirt = AcceleratorModifiers.FVIRTKEY Or AcceleratorModifiers.FALT
Accel.key = Keys.B
Accel.cmd = MENU_SHORTCUT2
'Create an accelerator table.
Dim hAccel As IntPtr = NativeMethods.CreateAcceleratorTable(New NativeMethods.ACCEL() {Accel}, 1)
'Create our message filter.
AccelMessageFilter = New KeyboardAcceleratorFilter(Me, hAccel, True)
'Add the filter to the application's message pump.
Application.AddMessageFilter(AccelMessageFilter)
End Sub
Protected Overrides Sub WndProc(ByRef m As System.Windows.Forms.Message)
If m.Msg = WindowMessages.WM_SYSCOMMAND Then
Select Case m.WParam.ToInt32()
Case MENU_SHORTCUT2
MessageBox.Show("'Shortcut 2' was pressed!")
End Select
End If
MyBase.WndProc(m)
End Sub
消息过滤器:
Public Class KeyboardAcceleratorFilter
Implements IMessageFilter
Private _acceleratorTable As IntPtr
Private _form As Form
''' <summary>
''' Gets the pointer to the filter's accelerator table.
''' </summary>
''' <remarks></remarks>
Public ReadOnly Property AcceleratorTable As IntPtr
Get
Return _acceleratorTable
End Get
End Property
''' <summary>
''' Gets or sets whether accelerator messages should be intercepted by the filter, stopping them from being dispatched to the form/control.
''' </summary>
''' <remarks></remarks>
Public Property InterceptMessages As Boolean
''' <summary>
''' Gets the form that the filter applies to.
''' </summary>
''' <remarks></remarks>
Public ReadOnly Property Form As Form
Get
Return _form
End Get
End Property
Public Function PreFilterMessage(ByRef m As System.Windows.Forms.Message) As Boolean Implements System.Windows.Forms.IMessageFilter.PreFilterMessage
'Native MSG structure (required by TranslateAccelerator()).
Dim Msg As New NativeMethods.MSG() With {.hwnd = Me.Form.Handle, .message = m.Msg, .wParam = m.WParam, .lParam = m.LParam}
'Process accelerators (if any) and send the SYSCOMMAND messages to this filter's form (Msg.hwnd is set to Me.Form.Handle above).
Dim Result As Integer = NativeMethods.TranslateAccelerator(Msg.hwnd, Me.AcceleratorTable, Msg)
'Intercept the message if an accelerator was processed and Me.InterceptMessages = True.
Return If(Result <> 0, Me.InterceptMessages, False)
End Function
''' <summary>
''' Initializes a new instance of the KeyboardAcceleratorFilter class.
''' </summary>
''' <param name="Form">The form that the filter applies to.</param>
''' <param name="AcceleratorTable">The pointer to the filter's accelerator table.</param>
''' <param name="InterceptMessages">Whether accelerator messages should be intercepted by the filter, stopping them from being dispatched to the form/control.</param>
''' <remarks></remarks>
Public Sub New(ByVal Form As Form, ByVal AcceleratorTable As IntPtr, ByVal InterceptMessages As Boolean)
_form = Form
_acceleratorTable = AcceleratorTable
Me.InterceptMessages = InterceptMessages
End Sub
End Class
本机方法
Imports System.Runtime.InteropServices
Public NotInheritable Class NativeMethods
Private Sub New() 'Private constructor as we're not supposed to create instances of this class.
End Sub
#Region "WinAPI Functions"
<DllImport("user32.dll", SetLastError:=True)> _
Public Shared Function GetSystemMenu(ByVal hWnd As IntPtr, <MarshalAs(UnmanagedType.Bool)> ByVal bRevert As Boolean) As IntPtr
End Function
<DllImport("user32.dll", SetLastError:=True)> _
Public Shared Function CreateMenu() As IntPtr
End Function
<DllImport("user32.dll", SetLastError:=True, CharSet:=CharSet.Ansi)> _
Public Shared Function InsertMenu(ByVal hMenu As IntPtr, ByVal uPosition As UInteger, ByVal uFlags As MenuFlags, ByVal uIDNewItem As IntPtr, ByVal lpNewMenu As String) As <MarshalAs(UnmanagedType.Bool)> Boolean
End Function
<DllImport("user32.dll", SetLastError:=True)> _
Public Shared Function CreateAcceleratorTable(ByVal lpaccel As ACCEL(), ByVal cAccel As Integer) As IntPtr
End Function
<DllImport("user32.dll", SetLastError:=True)> _
Public Shared Function TranslateAccelerator(ByVal hWnd As IntPtr, ByVal hAccel As IntPtr, ByRef lpMsg As MSG) As Integer
End Function
<DllImport("user32.dll", SetLastError:=True)> _
Public Shared Function DestroyAcceleratorTable(ByVal hAccel As IntPtr) As <MarshalAs(UnmanagedType.Bool)> Boolean
End Function
#End Region
#Region "Structures"
<StructLayout(LayoutKind.Sequential)> _
Public Structure ACCEL
Public fVirt As AcceleratorModifiers
Public key As UShort
Public cmd As UShort
End Structure
<StructLayout(LayoutKind.Sequential)> _
Public Structure MSG
Public hwnd As IntPtr
Public message As UInteger
Public wParam As IntPtr
Public lParam As IntPtr
Public time As UInteger
Public pt As POINT
End Structure
<StructLayout(LayoutKind.Sequential)> _
Public Structure POINT
Public x As Integer
Public y As Integer
End Structure
#End Region
End Class
#Region "Enumerations"
<Flags()> _
Public Enum AcceleratorModifiers As Byte
FVIRTKEY = 1
FSHIFT = &H4
FCONTROL = &H8
FALT = &H10
End Enum
Public Enum MenuFlags As Integer
MF_BYCOMMAND = &H0
MF_BYPOSITION = &H400
MF_BITMAP = &H4
MF_CHECKED = &H8
MF_DISABLED = &H2
MF_ENABLED = &H0
MF_GRAYED = &H1
MF_MENUBARBREAK = &H20
MF_MENUBREAK = &H40
MF_OWNERDRAW = &H100
MF_POPUP = &H10
MF_SEPARATOR = &H800
MF_STRING = &H0
MF_UNCHECKED = &H0
End Enum
Public Enum WindowMessages As Integer
WM_COMMAND = &H111
WM_SYSCOMMAND = &H112
End Enum
#End Region