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

移动和调整大小工作得很好,但我的上下文菜单总是显示在左上角,MOVESIZE 选项不起作用,因为它们需要默认 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