Windows 无边框默认上下文菜单 Windows 表单用自定义选项替换无效选项
Windows Default Context Menu on Borderless Windows Form replace not working Options with custom ones
我创建了一个无边框 Windows 表单并添加了默认系统页眉的功能:
移动:
Private m_blnMouseDown As Boolean = False
Private m_utdMouseOffset As Point
' Left mouse button pressed
Private Sub BorderlessMove_MouseDown(sender As Object, e As MouseEventArgs) Handles tlpHeader.MouseDown
If e.Button = MouseButtons.Left Then
' Get the new position
Dim mouseRelative As Point = Me.PointToClient(Cursor.Position)
m_utdMouseOffset = New Point(-mouseRelative.X, -mouseRelative.Y)
' Set that left button is pressed
m_blnMouseDown = True
End If
End Sub
' MouseMove used to check if mouse cursor is moving
Private Sub BorderlessMove_MouseMove(sender As Object, e As MouseEventArgs) Handles tlpHeader.MouseMove
If m_blnMouseDown Then
Dim mousePos As Point = Control.MousePosition
' Get the new form position
mousePos.Offset(m_utdMouseOffset.X, m_utdMouseOffset.Y)
Me.Location = mousePos
End If
End Sub
' Left mouse button released, form should stop moving
Private Sub BorderlessMove_MouseUp(sender As Object, e As MouseEventArgs) Handles tlpHeader.MouseUp
If e.Button = MouseButtons.Left Then
m_blnMouseDown = False
End If
End Sub
调整大小:
Private Enum ResizeState
North
NE
East
SE
South
SW
West
NW
None
End Enum
Private m_utdResizeState As ResizeState = ResizeState.None
Private Const GRIP_SIZE As Int32 = 5
Private Sub BorderlessResize_FormLoad(sender As Object, e As EventArgs) Handles Me.Load
Const DGRIP_SIZE As Int32 = GRIP_SIZE * 2
Dim Sizes As New Dictionary(Of String, Size)
Sizes.Add("Horizontal", New Size(Me.Width - DGRIP_SIZE * 2, GRIP_SIZE))
Sizes.Add("Vertical", New Size(GRIP_SIZE, Me.Height - DGRIP_SIZE * 2))
Sizes.Add("HEdge", New Size(DGRIP_SIZE, GRIP_SIZE))
Sizes.Add("VEdge", New Size(GRIP_SIZE, DGRIP_SIZE))
GenerateTransparentPanel("pnlResizeNorth", ResizeState.North, Sizes.Item("Horizontal"), New Point(DGRIP_SIZE, 0), (AnchorStyles.Top Or AnchorStyles.Right) Or AnchorStyles.Left)
GenerateTransparentPanel("pnlResizeNE_H", ResizeState.NE, Sizes.Item("HEdge"), New Point(Me.Width - DGRIP_SIZE, 0), AnchorStyles.Top Or AnchorStyles.Right)
GenerateTransparentPanel("pnlResizeNE_V", ResizeState.NE, Sizes.Item("VEdge"), New Point(Me.Width - GRIP_SIZE, 0), AnchorStyles.Top Or AnchorStyles.Right)
GenerateTransparentPanel("pnlResizeEast", ResizeState.East, Sizes.Item("Vertical"), New Point(Me.Width - GRIP_SIZE, DGRIP_SIZE), (AnchorStyles.Top Or AnchorStyles.Right) Or AnchorStyles.Bottom)
GenerateTransparentPanel("pnlResizeSE_H", ResizeState.SE, Sizes.Item("HEdge"), New Point(Me.Width - DGRIP_SIZE, Me.Height - GRIP_SIZE), AnchorStyles.Right Or AnchorStyles.Bottom)
GenerateTransparentPanel("pnlResizeSE_V", ResizeState.SE, Sizes.Item("VEdge"), New Point(Me.Width - GRIP_SIZE, Me.Height - DGRIP_SIZE), AnchorStyles.Right Or AnchorStyles.Bottom)
GenerateTransparentPanel("pnlResizeSouth", ResizeState.South, Sizes.Item("Horizontal"), New Point(DGRIP_SIZE, Me.Height - GRIP_SIZE), (AnchorStyles.Bottom Or AnchorStyles.Right) Or AnchorStyles.Left)
GenerateTransparentPanel("pnlResizeSW_H", ResizeState.SW, Sizes.Item("HEdge"), New Point(0, Me.Height - GRIP_SIZE), AnchorStyles.Bottom Or AnchorStyles.Left)
GenerateTransparentPanel("pnlResizeSW_V", ResizeState.SW, Sizes.Item("VEdge"), New Point(0, Me.Height - DGRIP_SIZE), AnchorStyles.Bottom Or AnchorStyles.Left)
GenerateTransparentPanel("pnlResizeWest", ResizeState.West, Sizes.Item("Vertical"), New Point(0, DGRIP_SIZE), (AnchorStyles.Top Or AnchorStyles.Left) Or AnchorStyles.Bottom)
GenerateTransparentPanel("pnlResizeNW_H", ResizeState.NW, Sizes.Item("HEdge"), New Point(0, 0), AnchorStyles.Top Or AnchorStyles.Left)
GenerateTransparentPanel("pnlResizeNW_V", ResizeState.NW, Sizes.Item("VEdge"), New Point(0, 0), AnchorStyles.Top Or AnchorStyles.Left)
End Sub
Private Sub GenerateTransparentPanel(name As String, tag As ResizeState, size As Size, location As Point, anchors As AnchorStyles)
Dim panel As TransparentPanel = New TransparentPanel()
panel.Anchor = anchors
panel.Location = location
panel.Name = name
panel.Size = size
panel.Tag = tag
AddHandler panel.MouseDown, AddressOf BorderlessResize_MouseDown
AddHandler panel.MouseMove, AddressOf BorderlessResize_MouseMove
AddHandler panel.MouseUp, AddressOf BorderlessResize_MouseUp
AddHandler panel.MouseEnter, AddressOf BorderlessResize_MouseEnter
AddHandler panel.MouseLeave, AddressOf BorderlessResize_MouseLeave
Me.Controls.Add(panel)
panel.BringToFront()
End Sub
Private Sub BorderlessResize_MouseDown(sender As Object, e As MouseEventArgs)
Debug.Write("MouseDown")
If e.Button = MouseButtons.Left Then
m_utdResizeState = DirectCast(sender, Control).Tag
End If
End Sub
Protected Sub BorderlessResize_MouseMove(sender As Object, e As MouseEventArgs)
If m_utdResizeState <> ResizeState.None Then
BorderlessResize_ResizeForm(m_utdResizeState)
Refresh()
End If
End Sub
Protected Sub BorderlessResize_MouseEnter(sender As Object, e As EventArgs)
BorderlessResize_UpdateCursor(DirectCast(sender, Control).Tag)
End Sub
Protected Sub BorderlessResize_MouseLeave(sender As Object, e As EventArgs)
Me.Cursor = Cursors.Default
End Sub
Private Sub BorderlessResize_UpdateCursor(state As ResizeState)
If (state = ResizeState.East OrElse state = ResizeState.West) Then
Me.Cursor = Cursors.SizeWE
ElseIf (state = ResizeState.North OrElse state = ResizeState.South) Then
Me.Cursor = Cursors.SizeNS
ElseIf (state = ResizeState.SE OrElse state = ResizeState.NW) Then
Me.Cursor = Cursors.SizeNWSE
ElseIf (state = ResizeState.NE OrElse state = ResizeState.SW) Then
Me.Cursor = Cursors.SizeNESW
Else Me.Cursor = Cursors.Default
End If
End Sub
Private Sub BorderlessResize_ResizeForm(ResizeVal As ResizeState)
Dim Location As Point = New Point(Cursor.Position.X - Me.Left, Cursor.Position.Y - Me.Top)
Select Case ResizeVal
Case ResizeState.North
If Me.Height - Location.Y <= Me.MinimumSize.Height Then Return
Me.Height = Me.Height - Location.Y
Me.Top = Me.Top + Location.Y
Exit Select
Case ResizeState.South
Me.Height = Location.Y
Exit Select
Case ResizeState.East
Me.Width = Location.X
Exit Select
Case ResizeState.West
If Me.Width - Location.X <= Me.MinimumSize.Width Then Return
Me.Width = Me.Width - Location.X
Me.Left = Me.Left + Location.X
Exit Select
Case ResizeState.NE
BorderlessResize_ResizeForm(ResizeState.North)
BorderlessResize_ResizeForm(ResizeState.East)
Exit Select
Case ResizeState.SE
BorderlessResize_ResizeForm(ResizeState.South)
BorderlessResize_ResizeForm(ResizeState.East)
Exit Select
Case ResizeState.SW
BorderlessResize_ResizeForm(ResizeState.South)
BorderlessResize_ResizeForm(ResizeState.West)
Exit Select
Case ResizeState.NW
BorderlessResize_ResizeForm(ResizeState.North)
BorderlessResize_ResizeForm(ResizeState.West)
Exit Select
End Select
End Sub
Protected Sub BorderlessResize_MouseUp(sender As Object, e As MouseEventArgs)
If e.Button = MouseButtons.Left Then
m_utdResizeState = ResizeState.None
End If
End Sub
默认上下文菜单
Private Const WS_SYSMENU As Integer = &H80000
Private Const WS_MINIMIZEBOX As Integer = &H20000
Private Const WS_MAXIMIZEBOX As Integer = &H10000
Protected Overrides ReadOnly Property CreateParams As System.Windows.Forms.CreateParams
Get
Dim p = MyBase.CreateParams
p.Style = WS_SYSMENU + WS_MINIMIZEBOX + WS_MAXIMIZEBOX
Return p
End Get
End Property
<DllImport("user32.dll")>
Private Shared Function SendMessage(ByVal hWnd As IntPtr, ByVal msg As Integer,
ByVal wParam As IntPtr, ByVal lParam As IntPtr) As IntPtr
End Function
Private Const WM_POPUPSYSTEMMENU = &H313
Protected Overrides Sub OnMouseDown(ByVal e As System.Windows.Forms.MouseEventArgs)
MyBase.OnMouseDown(e)
Dim p = MousePosition.X + (MousePosition.Y * &H10000)
SendMessage(Me.Handle, WM_POPUPSYSTEMMENU, 0, p)
End Sub
移动和调整大小工作得很好,但我的上下文菜单总是显示在左上角,MOVE
和 SIZE
选项不起作用,因为它们需要默认 Window我删除的框架。
那么是否有可能实施我在上下文菜单中移动和调整大小的解决方案?
是
解决方案 1
用 WndPrc 覆盖
Private Sub Header_MouseDown(sender As Object, ByVal e As MouseEventArgs)
If e.Button = MouseButtons.Right Then
Dim p As IntPtr = ((MousePosition.Y * WindowStyles.WS_MAXIMIZEBOX) + (MousePosition.X And &HFFFF))
Dim test As IntPtr = SendMessage(Me.Handle, WindowMessages.WM_POPUPSYSMENU, 0, p)
End If
End Sub
Protected Overrides Sub WndProc(ByRef m As Message)
If (m.Msg = WindowMessages.WM_SYSCOMMAND)
'Do SOmething with m.wParam
Else
MyBase.WndProc(m)
End If
End Sub
解决方案 2
使用 TrackMenuBar
Private Sub Header_MouseDown(sender As Object, ByVal e As MouseEventArgs)
If e.Button = MouseButtons.Right Then'Alternate Solution
Dim sysMenu As IntPtr = GetSystemMenu(Me.Handle, False)
Dim cmd As IntPtr = TrackPopupMenu(
sysMenu,
TrackPopupMenuOptions.TOPALIGN Or
TrackPopupMenuOptions.NOANIMATION Or
TrackPopupMenuOptions.RETURNCMD Or
TrackPopupMenuOptions.LEFTALIGN Or
TrackPopupMenuOptions.RIGHTBUTTON,
MousePosition.X,
MousePosition.Y,
IntPtr.Zero,
Me.Handle,
IntPtr.Zero)
'Do Something with cmd
End If
End Sub
我创建了一个无边框 Windows 表单并添加了默认系统页眉的功能:
移动:
Private m_blnMouseDown As Boolean = False
Private m_utdMouseOffset As Point
' Left mouse button pressed
Private Sub BorderlessMove_MouseDown(sender As Object, e As MouseEventArgs) Handles tlpHeader.MouseDown
If e.Button = MouseButtons.Left Then
' Get the new position
Dim mouseRelative As Point = Me.PointToClient(Cursor.Position)
m_utdMouseOffset = New Point(-mouseRelative.X, -mouseRelative.Y)
' Set that left button is pressed
m_blnMouseDown = True
End If
End Sub
' MouseMove used to check if mouse cursor is moving
Private Sub BorderlessMove_MouseMove(sender As Object, e As MouseEventArgs) Handles tlpHeader.MouseMove
If m_blnMouseDown Then
Dim mousePos As Point = Control.MousePosition
' Get the new form position
mousePos.Offset(m_utdMouseOffset.X, m_utdMouseOffset.Y)
Me.Location = mousePos
End If
End Sub
' Left mouse button released, form should stop moving
Private Sub BorderlessMove_MouseUp(sender As Object, e As MouseEventArgs) Handles tlpHeader.MouseUp
If e.Button = MouseButtons.Left Then
m_blnMouseDown = False
End If
End Sub
调整大小:
Private Enum ResizeState
North
NE
East
SE
South
SW
West
NW
None
End Enum
Private m_utdResizeState As ResizeState = ResizeState.None
Private Const GRIP_SIZE As Int32 = 5
Private Sub BorderlessResize_FormLoad(sender As Object, e As EventArgs) Handles Me.Load
Const DGRIP_SIZE As Int32 = GRIP_SIZE * 2
Dim Sizes As New Dictionary(Of String, Size)
Sizes.Add("Horizontal", New Size(Me.Width - DGRIP_SIZE * 2, GRIP_SIZE))
Sizes.Add("Vertical", New Size(GRIP_SIZE, Me.Height - DGRIP_SIZE * 2))
Sizes.Add("HEdge", New Size(DGRIP_SIZE, GRIP_SIZE))
Sizes.Add("VEdge", New Size(GRIP_SIZE, DGRIP_SIZE))
GenerateTransparentPanel("pnlResizeNorth", ResizeState.North, Sizes.Item("Horizontal"), New Point(DGRIP_SIZE, 0), (AnchorStyles.Top Or AnchorStyles.Right) Or AnchorStyles.Left)
GenerateTransparentPanel("pnlResizeNE_H", ResizeState.NE, Sizes.Item("HEdge"), New Point(Me.Width - DGRIP_SIZE, 0), AnchorStyles.Top Or AnchorStyles.Right)
GenerateTransparentPanel("pnlResizeNE_V", ResizeState.NE, Sizes.Item("VEdge"), New Point(Me.Width - GRIP_SIZE, 0), AnchorStyles.Top Or AnchorStyles.Right)
GenerateTransparentPanel("pnlResizeEast", ResizeState.East, Sizes.Item("Vertical"), New Point(Me.Width - GRIP_SIZE, DGRIP_SIZE), (AnchorStyles.Top Or AnchorStyles.Right) Or AnchorStyles.Bottom)
GenerateTransparentPanel("pnlResizeSE_H", ResizeState.SE, Sizes.Item("HEdge"), New Point(Me.Width - DGRIP_SIZE, Me.Height - GRIP_SIZE), AnchorStyles.Right Or AnchorStyles.Bottom)
GenerateTransparentPanel("pnlResizeSE_V", ResizeState.SE, Sizes.Item("VEdge"), New Point(Me.Width - GRIP_SIZE, Me.Height - DGRIP_SIZE), AnchorStyles.Right Or AnchorStyles.Bottom)
GenerateTransparentPanel("pnlResizeSouth", ResizeState.South, Sizes.Item("Horizontal"), New Point(DGRIP_SIZE, Me.Height - GRIP_SIZE), (AnchorStyles.Bottom Or AnchorStyles.Right) Or AnchorStyles.Left)
GenerateTransparentPanel("pnlResizeSW_H", ResizeState.SW, Sizes.Item("HEdge"), New Point(0, Me.Height - GRIP_SIZE), AnchorStyles.Bottom Or AnchorStyles.Left)
GenerateTransparentPanel("pnlResizeSW_V", ResizeState.SW, Sizes.Item("VEdge"), New Point(0, Me.Height - DGRIP_SIZE), AnchorStyles.Bottom Or AnchorStyles.Left)
GenerateTransparentPanel("pnlResizeWest", ResizeState.West, Sizes.Item("Vertical"), New Point(0, DGRIP_SIZE), (AnchorStyles.Top Or AnchorStyles.Left) Or AnchorStyles.Bottom)
GenerateTransparentPanel("pnlResizeNW_H", ResizeState.NW, Sizes.Item("HEdge"), New Point(0, 0), AnchorStyles.Top Or AnchorStyles.Left)
GenerateTransparentPanel("pnlResizeNW_V", ResizeState.NW, Sizes.Item("VEdge"), New Point(0, 0), AnchorStyles.Top Or AnchorStyles.Left)
End Sub
Private Sub GenerateTransparentPanel(name As String, tag As ResizeState, size As Size, location As Point, anchors As AnchorStyles)
Dim panel As TransparentPanel = New TransparentPanel()
panel.Anchor = anchors
panel.Location = location
panel.Name = name
panel.Size = size
panel.Tag = tag
AddHandler panel.MouseDown, AddressOf BorderlessResize_MouseDown
AddHandler panel.MouseMove, AddressOf BorderlessResize_MouseMove
AddHandler panel.MouseUp, AddressOf BorderlessResize_MouseUp
AddHandler panel.MouseEnter, AddressOf BorderlessResize_MouseEnter
AddHandler panel.MouseLeave, AddressOf BorderlessResize_MouseLeave
Me.Controls.Add(panel)
panel.BringToFront()
End Sub
Private Sub BorderlessResize_MouseDown(sender As Object, e As MouseEventArgs)
Debug.Write("MouseDown")
If e.Button = MouseButtons.Left Then
m_utdResizeState = DirectCast(sender, Control).Tag
End If
End Sub
Protected Sub BorderlessResize_MouseMove(sender As Object, e As MouseEventArgs)
If m_utdResizeState <> ResizeState.None Then
BorderlessResize_ResizeForm(m_utdResizeState)
Refresh()
End If
End Sub
Protected Sub BorderlessResize_MouseEnter(sender As Object, e As EventArgs)
BorderlessResize_UpdateCursor(DirectCast(sender, Control).Tag)
End Sub
Protected Sub BorderlessResize_MouseLeave(sender As Object, e As EventArgs)
Me.Cursor = Cursors.Default
End Sub
Private Sub BorderlessResize_UpdateCursor(state As ResizeState)
If (state = ResizeState.East OrElse state = ResizeState.West) Then
Me.Cursor = Cursors.SizeWE
ElseIf (state = ResizeState.North OrElse state = ResizeState.South) Then
Me.Cursor = Cursors.SizeNS
ElseIf (state = ResizeState.SE OrElse state = ResizeState.NW) Then
Me.Cursor = Cursors.SizeNWSE
ElseIf (state = ResizeState.NE OrElse state = ResizeState.SW) Then
Me.Cursor = Cursors.SizeNESW
Else Me.Cursor = Cursors.Default
End If
End Sub
Private Sub BorderlessResize_ResizeForm(ResizeVal As ResizeState)
Dim Location As Point = New Point(Cursor.Position.X - Me.Left, Cursor.Position.Y - Me.Top)
Select Case ResizeVal
Case ResizeState.North
If Me.Height - Location.Y <= Me.MinimumSize.Height Then Return
Me.Height = Me.Height - Location.Y
Me.Top = Me.Top + Location.Y
Exit Select
Case ResizeState.South
Me.Height = Location.Y
Exit Select
Case ResizeState.East
Me.Width = Location.X
Exit Select
Case ResizeState.West
If Me.Width - Location.X <= Me.MinimumSize.Width Then Return
Me.Width = Me.Width - Location.X
Me.Left = Me.Left + Location.X
Exit Select
Case ResizeState.NE
BorderlessResize_ResizeForm(ResizeState.North)
BorderlessResize_ResizeForm(ResizeState.East)
Exit Select
Case ResizeState.SE
BorderlessResize_ResizeForm(ResizeState.South)
BorderlessResize_ResizeForm(ResizeState.East)
Exit Select
Case ResizeState.SW
BorderlessResize_ResizeForm(ResizeState.South)
BorderlessResize_ResizeForm(ResizeState.West)
Exit Select
Case ResizeState.NW
BorderlessResize_ResizeForm(ResizeState.North)
BorderlessResize_ResizeForm(ResizeState.West)
Exit Select
End Select
End Sub
Protected Sub BorderlessResize_MouseUp(sender As Object, e As MouseEventArgs)
If e.Button = MouseButtons.Left Then
m_utdResizeState = ResizeState.None
End If
End Sub
默认上下文菜单
Private Const WS_SYSMENU As Integer = &H80000
Private Const WS_MINIMIZEBOX As Integer = &H20000
Private Const WS_MAXIMIZEBOX As Integer = &H10000
Protected Overrides ReadOnly Property CreateParams As System.Windows.Forms.CreateParams
Get
Dim p = MyBase.CreateParams
p.Style = WS_SYSMENU + WS_MINIMIZEBOX + WS_MAXIMIZEBOX
Return p
End Get
End Property
<DllImport("user32.dll")>
Private Shared Function SendMessage(ByVal hWnd As IntPtr, ByVal msg As Integer,
ByVal wParam As IntPtr, ByVal lParam As IntPtr) As IntPtr
End Function
Private Const WM_POPUPSYSTEMMENU = &H313
Protected Overrides Sub OnMouseDown(ByVal e As System.Windows.Forms.MouseEventArgs)
MyBase.OnMouseDown(e)
Dim p = MousePosition.X + (MousePosition.Y * &H10000)
SendMessage(Me.Handle, WM_POPUPSYSTEMMENU, 0, p)
End Sub
移动和调整大小工作得很好,但我的上下文菜单总是显示在左上角,MOVE
和 SIZE
选项不起作用,因为它们需要默认 Window我删除的框架。
那么是否有可能实施我在上下文菜单中移动和调整大小的解决方案?
是
解决方案 1
用 WndPrc 覆盖
Private Sub Header_MouseDown(sender As Object, ByVal e As MouseEventArgs)
If e.Button = MouseButtons.Right Then
Dim p As IntPtr = ((MousePosition.Y * WindowStyles.WS_MAXIMIZEBOX) + (MousePosition.X And &HFFFF))
Dim test As IntPtr = SendMessage(Me.Handle, WindowMessages.WM_POPUPSYSMENU, 0, p)
End If
End Sub
Protected Overrides Sub WndProc(ByRef m As Message)
If (m.Msg = WindowMessages.WM_SYSCOMMAND)
'Do SOmething with m.wParam
Else
MyBase.WndProc(m)
End If
End Sub
解决方案 2
使用 TrackMenuBar
Private Sub Header_MouseDown(sender As Object, ByVal e As MouseEventArgs)
If e.Button = MouseButtons.Right Then'Alternate Solution
Dim sysMenu As IntPtr = GetSystemMenu(Me.Handle, False)
Dim cmd As IntPtr = TrackPopupMenu(
sysMenu,
TrackPopupMenuOptions.TOPALIGN Or
TrackPopupMenuOptions.NOANIMATION Or
TrackPopupMenuOptions.RETURNCMD Or
TrackPopupMenuOptions.LEFTALIGN Or
TrackPopupMenuOptions.RIGHTBUTTON,
MousePosition.X,
MousePosition.Y,
IntPtr.Zero,
Me.Handle,
IntPtr.Zero)
'Do Something with cmd
End If
End Sub