6

I removed the default Border from my Windows Form and created my own header which i want to give the same features as the default windows one.

One of them is the context Menu that is displayed on Right Mouse Click:

Default Context Menu

Is there a possibility to assign this to my costum header?

If you want to post code I would prefer vb, but c# is also okay.

Reza Aghaei
  • 120,393
  • 18
  • 203
  • 398
Error404
  • 719
  • 9
  • 30
  • What did you try, other than move, the code for the other options is ridiculously simple, so what did you try? – BugFinder Aug 10 '16 at 07:45
  • @BugFinder I dont want to recreate the context menu, I want to somehow call the System Default one – Error404 Aug 10 '16 at 07:48
  • @VisualVincent It was falsely marked as duplicate because my question was about Windows Form and the other one about WPFs – Error404 Aug 10 '16 at 08:56
  • @VisualVincent Closing that question as duplicate seems to be my fault. While the linked post is really useful to solve the problem, but a border-less form doesn't have system menu by default. You should first enable system menu for the form by adding WS_SYSMENU style in CreateParams. Then you can send WM_POPUPSYSTEMMENU to the window in OnMouseDown – Reza Aghaei Aug 10 '16 at 08:56
  • I tryd the solution is the "duplicate question" and it was not working for me, because some class was not available in Windows Forms – Error404 Aug 10 '16 at 09:02
  • @VisualVincent Thank you. Those APIs are completely useful, the main difference is not having SystemMenu by default in a borderless WinForms `Form`. First you should enable the system menu in the way that I shared in this answer, then you can use `TrackPopupMenu` and so on or use a `WM_POPUPSYSTEMMENU` as I did. – Reza Aghaei Aug 10 '16 at 09:03
  • I did make those changes but it still didnt worked, I just didnt remerbered that as I wrote my comment – Error404 Aug 10 '16 at 09:09

1 Answers1

8

A border-less form doesn't have system menu by default. You should first enable system menu for the form by adding WS_SYSMENU style in CreateParams. Then you can send WM_POPUPSYSTEMMENU to the window in OnMouseDown.

C#

Set this.FormBorderStyle = Windows.Forms.FormBorderStyle.None; then:

private const int WS_SYSMENU = 0x80000;
private const int WS_MINIMIZEBOX = 0x20000;
private const int WS_MAXIMIZEBOX = 0x10000;
protected override CreateParams CreateParams
{
    get
    {
        CreateParams p = base.CreateParams;
        p.Style = WS_SYSMENU | WS_MINIMIZEBOX | WS_MAXIMIZEBOX;
        return p;
    }
}

[DllImport("user32.dll")]
private static extern IntPtr SendMessage(IntPtr hWnd, int msg,
    IntPtr wParam, IntPtr lParam);
private const int WM_POPUPSYSTEMMENU = 0x313;
protected override void OnMouseDown(MouseEventArgs e)
{
    base.OnMouseDown(e);
    if (e.Button == System.Windows.Forms.MouseButtons.Right)
    {
        var p = MousePosition.X + (MousePosition.Y * 0x10000);
        SendMessage(this.Handle, WM_POPUPSYSTEMMENU, (IntPtr)0, (IntPtr)p);
    }
}

VB.NET

Set Me.FormBorderStyle = Windows.Forms.FormBorderStyle.None then:

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 As Integer = &H313
Protected Overrides Sub OnMouseDown(ByVal e As System.Windows.Forms.MouseEventArgs)
    MyBase.OnMouseDown(e)
    If e.Button = MouseButtons.Right Then
        Dim p = MousePosition.X + (MousePosition.Y * &H10000)
        SendMessage(Me.Handle, WM_POPUPSYSTEMMENU, 0, p)
    End If
End Sub

Note

WM_POPUPSYSTEMMENU is undocumented but completely working. If you want to use a documented way you can get the system menu using GetSystemMenu and then show it using TrackPopupMenu and using a SendMessage execute returned command., you can declare:

Private Const TPM_LEFTBUTTON As Integer = &H0
Private Const TPM_RIGHTBUTTON As Integer = &H2
Private Const TPM_RETURNCMD As Integer = &H100
Private Const WM_SYSCOMMAND As Integer = &H112
<DllImport("user32.dll")> _
Private Shared Function GetSystemMenu(ByVal hWnd As IntPtr, _
    ByVal bRevert As Boolean) As IntPtr
End Function
<DllImport("user32.dll")>
Private Shared Function TrackPopupMenu(ByVal hMenu As IntPtr, ByVal uFlags As Integer, _
    ByVal x As Integer, ByVal y As Integer, ByVal nReserved As Integer, _
    ByVal hWnd As IntPtr, ByVal prcRect As IntPtr) As Integer
End Function
<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

And show the menu this way:

Dim menu = GetSystemMenu(Me.Handle, False)
Dim command = TrackPopupMenu(menu, TPM_RETURNCMD + TPM_LEFTBUTTON + TPM_RIGHTBUTTON, _
                    MousePosition.X, MousePosition.Y, IntPtr.Zero, _
                    Me.Handle, IntPtr.Zero)
If (command > 0) Then
    SendMessage(Me.Handle, WM_SYSCOMMAND, command, IntPtr.Zero)
End If
Reza Aghaei
  • 120,393
  • 18
  • 203
  • 398
  • almost working perfectly - Move Option and Positioning of the Menu do not work – Error404 Aug 10 '16 at 09:00
  • 1
    It's the way that it works. Move needs title-bar. Or you can alternatively use solutions which are available for moving borderless forms. – Reza Aghaei Aug 10 '16 at 09:07
  • What do I have to change for the Positioning Part and how can I make the Moce Option work(I got the Code for Moving, but how to implement it) – Error404 Aug 10 '16 at 09:10
  • Take a look at [this pot](http://stackoverflow.com/a/1592899/3110834). Converting it to VB.NET is really simple. You have`SendMessage` in this answer, also here is `ReleaseCapture`: ` Private Shared Function ReleaseCapture() As Boolean` – Reza Aghaei Aug 10 '16 at 09:27
  • I needed to amend the vb example for createparams as p.Style = p.Style Or WS_SYSMENU ... for although the example worked at runtime it was breaking my forms designer. – Tim F. Mar 27 '18 at 09:29