Click here to Skip to main content
Click here to Skip to main content

Customizing WinForm's System Menu

, 28 Jul 2004 CPOL
Rate this:
Please Sign up or sign in to vote.
How to customize the WinForm system menu.

Why? Why Not?

Why would you want to do it? Why not, if I could do it before, why not in .NET? The better answer is that sometimes you just need a quick way to get some options hooked in without having to create a full menu and/or toolbar. Or maybe you don’t want to use up the space required by a menu/toolbar. Or maybe you want to provide some special functionality that just doesn’t belong to the application’s menu. For whatever reason, why not?

Anyway, getting to it was not that obvious to me. Try it. A system menu is automatically created for you by the Wizard when you specify a ‘Windows Application’ as the project type. See if you can figure out how to get access to it. If you look through the docs, you’ll see that you can specify some of the items on the menu indirectly through properties of the Form. For example, if you set the MaximizeBox property to false, then the corresponding item on the system menu will be disabled. You can also completely remove the system menu by setting the ControlBox property to false. Of course, this has an additional side effect of changing some of the Window Style properties.

The Form does has a Menu property but it’s for the application menu that you specify. And what’s displayed when you click on the application icon on the Title Bar is definitely a menu. So, where is it and how can I get access to it? It’s my application! Some people may want to argue that it’s the ‘System Menu’ and you shouldn’t be putting items there in the first place. Well, it’s just that I used to be able to do it!

Let's Do It

OK, enough foreplay. Let’s get down to it. Create new project and select ‘Windows Project’ on the ‘New Project’ dialog. Go ahead and create an event handler for the Form_Load since we’ll be using it in a little bit. So, it turns out that the code controlling the ‘System Menu’ is actually in the ‘User32’ DLL! That means that in order to get access to the ‘System Menu’, we’ve got to use the old Win32 API. Holy molony! I just want to add an item or two to the menu. Maybe this wasn’t such a good idea. I mean, it’s real easy to add a menu to the application, just some drag and drop and we’re done. Nope, not gonna listen. It’s got to be the hard way.

So, here’s what I’m guessing, the code that I write is managed code which means it runs in a nice little protected environment called the .NET runtime. Which is kind of like a really big application that thinks it’s an operating system. I mean the code you write acts, behaves, and has access to the system services (with some restrictions of course) just like a Win32 app. Code that runs outside of the .NET runtime is considered unmanaged code and of course is anything that is executed by Windows, a COM server, an executable, a DLL. So to solve our little problem, we have to figure out how to make a call from our managed code to unmanaged code.

As usual, it’s not so bad once you know how to do it. One of the namespaces provided under the .NET umbrella is the System.Runtime.InteropServices. The name kinda gives it away. It’s a collection of types used to ‘interoperate’ with the outside world. The one we need is the ‘Platform Invocation Services’ (isn’t that refreshing having a name that actually describes what it does?). PInvoke, for short, provides the facilities for managed code to make calls to unmanaged code. It handles all the marshaling, loading, and locating the function of interest. To get this magic to happen, all we have to do is specify the function that we want to call (in the unmanaged piece of code) and then decorate the declaration with the DllImport attribute which is part of System.Runtime.InteropServices.

Add the following at the beginning of the Form definition.

[DllImport("user32.dll")]
private static extern IntPtr GetSystemMenu(IntPtr hWnd, bool bRevert);
[DllImport("user32.dll")]
private static extern bool InsertMenu (IntPtr hMenu, 
    Int32 wPosition, Int32 wFlags, Int32 wIDNewItem, 
    string lpNewItem);

Those are the two functions we’ll need to call, and the DllImport attribute tells the system where they can be found. From this, it’s a small stretch to see how we can use this mechanism to access any other legacy code we may have. By the way, there are other fields that can specify additional options for the DllImport attribute.

In the Form_Load event handler, add the following:

IntPtr sysMenuHandle = GetSystemMenu(this.Handle, false);
//It would be better to find the position at run time of the 'Close' item, but...
InsertMenu(sysMenuHandle, 5, MF_BYPOSITION |MF_SEPARATOR, 0, string.Empty);
InsertMenu(sysMenuHandle, 6, MF_BYPOSITION , IDM_EDITFUNDS, "Edit Funds");
InsertMenu(sysMenuHandle, 7, MF_BYPOSITION , IDM_ANALYZE, "Analyze");

The first thing we do is get a handle to the system menu. Then we just add the items we want. To make things compile, add the following after the DllImport attributes added above:

public const Int32 WM_SYSCOMMAND = 0x112;
public const Int32 MF_SEPARATOR = 0x800;
public const Int32 MF_BYPOSITION = 0x400;
public const Int32 MF_STRING = 0x0;
public const Int32 IDM_EDITFUNDS  = 1000;
public const Int32 IDM_ANALYZE = 1001;

Oh, and make sure you bring in the System.Runtime.InteropServices namespace. Compile the application and check out the two additional items in the system menu. That’s good, we got what we wanted. But having those items without doing anything doesn’t help much. So now, we need to provide a way to capture the selection of those items so we can do something. Again, that’s not too bad. All that’s needed is to override the Form’s WndProc method as shown below:

protected override void WndProc(ref Message m)
{
    if(m.Msg == WM_SYSCOMMAND)
    {
        switch(m.WParam.ToInt32())
        {
            case IDM_EDITFUNDS : 
                MessageBox.Show("Clicked 'EditFunds'");
                return;
            case IDM_ANALYZE :
                MessageBox.Show("Clicked 'Analyze'");
                return;
            default:
                break;
        } 
    }
    base.WndProc(ref m);
}

There you have it. A simple little question like, ‘Hey, there’s already a menu there, why don’t we use that?’, turns into a little adventure involving the System Interop Services. Well, it's on to the next adventure Keemo Sabee.

License

This article, along with any associated source code and files, is licensed under The Code Project Open License (CPOL)

Share

About the Author

Al Alberto
Software Developer (Senior)
United States United States
No Biography provided

Comments and Discussions

 
GeneralMy vote of 5 PinmemberThornik26-May-13 13:04 
QuestionHow to add modifier keys PinmemberMD. Mohiuddin Ahmed2-Apr-13 4:44 
GeneralMy vote of 5 PinmemberH_SS9-Feb-13 19:35 
NewsCode In VB.net PinmemberH_SS9-Feb-13 19:25 
QuestionIs it possible to add hierachical menus? PinmemberMember 170895726-Jan-12 13:26 
AnswerRe: Is it possible to add hierachical menus? PinmemberAl Alberto4-Feb-12 13:53 
GeneralMy vote of 5 Pinmembermla15416-Dec-11 7:59 
GeneralMy vote of 5 PinmemberAngloThaiSolutions26-Jul-11 8:26 
Generalplease post a code snippet. Thanks. PinmemberSouthmountain1-May-10 8:41 
QuestionCan this be used without relaying on Forms? just for every application? Pinmemberasnat4-Apr-08 1:27 
QuestionVery Interesting Question...How? PinmemberWraith Lunati11-Mar-08 13:57 
GeneralAnother Approach PinmemberCalifBreton10-Jan-08 18:29 
GeneralCompile errors using code snippets! PinmemberPeterBeard22-Oct-07 7:05 
GeneralRe: Compile errors using code snippets! PinmemberAl Alberto25-Oct-07 7:03 
GeneralRe: Compile errors using code snippets! PinmemberPeterBeard25-Oct-07 21:13 
GeneralJust what I needed! PinmemberRFID Chris20-Sep-07 13:08 
GeneralExcellent Tutorial - Worked Great Pinmemberbklynjava129-Jul-07 13:16 
QuestionDOWNLOAD???? PinmemberCodeMonkey4619824-May-07 7:14 
AnswerRe: DOWNLOAD???? Pinmemberbklynjava129-Jul-07 13:17 
QuestionSystem menu on right click title bar Pinmemberlaura34-Sep-05 22:42 
AnswerRe: System menu on right click title bar PinmemberThornik26-May-13 13:12 
QuestionHow to implement shortcuts functionality for new system menu items? Pinmembergrzkas25-Aug-05 0:02 
AnswerRe: How to implement shortcuts functionality for new system menu items? PinmemberAl Alberto27-Aug-05 13:54 
QuestionHow in Visual C++ 6.0 with mfc? Pinmembermonimicki18-Nov-04 0:18 
GeneralCode Example Pinmemberstano3-Aug-04 21:24 

General General    News News    Suggestion Suggestion    Question Question    Bug Bug    Answer Answer    Joke Joke    Rant Rant    Admin Admin   

Use Ctrl+Left/Right to switch messages, Ctrl+Up/Down to switch threads, Ctrl+Shift+Left/Right to switch pages.

| Advertise | Privacy | Mobile
Web02 | 2.8.141015.1 | Last Updated 29 Jul 2004
Article Copyright 2004 by Al Alberto
Everything else Copyright © CodeProject, 1999-2014
Terms of Service
Layout: fixed | fluid