Click here to Skip to main content
15,881,882 members
Articles / Programming Languages / C#
Article

Customizing WinForm's System Menu

Rate me:
Please Sign up or sign in to vote.
4.69/5 (37 votes)
28 Jul 2004CPOL5 min read 121.9K   54   30
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.

C#
[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:

C#
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:

C#
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:

C#
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)


Written By
Software Developer (Senior)
United States United States
This member has not yet provided a Biography. Assume it's interesting and varied, and probably something to do with programming.

Comments and Discussions

 
QuestionIt might be better to use AppendMenu instead of InsertMenu Pin
derek_penguin29-Oct-14 4:07
derek_penguin29-Oct-14 4:07 
GeneralMy vote of 5 Pin
Thornik26-May-13 13:04
Thornik26-May-13 13:04 
QuestionHow to add modifier keys Pin
MD. Mohiuddin Ahmed2-Apr-13 4:44
MD. Mohiuddin Ahmed2-Apr-13 4:44 
How can I set keyboard shortcut with modifiers ,e.g. Ctrl+Space?
GeneralMy vote of 5 Pin
S.H.Yadegari9-Feb-13 19:35
S.H.Yadegari9-Feb-13 19:35 
NewsCode In VB.net Pin
S.H.Yadegari9-Feb-13 19:25
S.H.Yadegari9-Feb-13 19:25 
QuestionIs it possible to add hierachical menus? Pin
Member 170895726-Jan-12 13:26
Member 170895726-Jan-12 13:26 
AnswerRe: Is it possible to add hierachical menus? Pin
Al Alberto4-Feb-12 13:53
Al Alberto4-Feb-12 13:53 
GeneralMy vote of 5 Pin
mla15416-Dec-11 7:59
mla15416-Dec-11 7:59 
GeneralMy vote of 5 Pin
AngloThaiSolutions26-Jul-11 8:26
AngloThaiSolutions26-Jul-11 8:26 
Generalplease post a code snippet. Thanks. Pin
Southmountain1-May-10 8:41
Southmountain1-May-10 8:41 
QuestionCan this be used without relaying on Forms? just for every application? Pin
asnat4-Apr-08 1:27
asnat4-Apr-08 1:27 
QuestionVery Interesting Question...How? Pin
Wraith Lunati11-Mar-08 13:57
Wraith Lunati11-Mar-08 13:57 
GeneralAnother Approach Pin
CalifBreton10-Jan-08 18:29
CalifBreton10-Jan-08 18:29 
GeneralCompile errors using code snippets! Pin
Peter Beard22-Oct-07 7:05
Peter Beard22-Oct-07 7:05 
GeneralRe: Compile errors using code snippets! Pin
Al Alberto25-Oct-07 7:03
Al Alberto25-Oct-07 7:03 
GeneralRe: Compile errors using code snippets! Pin
Peter Beard25-Oct-07 21:13
Peter Beard25-Oct-07 21:13 
GeneralJust what I needed! Pin
RFID Chris20-Sep-07 13:08
RFID Chris20-Sep-07 13:08 
GeneralExcellent Tutorial - Worked Great Pin
bklynjava129-Jul-07 13:16
bklynjava129-Jul-07 13:16 
QuestionDOWNLOAD???? Pin
CodeMonkey4619824-May-07 7:14
CodeMonkey4619824-May-07 7:14 
AnswerRe: DOWNLOAD???? Pin
bklynjava129-Jul-07 13:17
bklynjava129-Jul-07 13:17 
QuestionSystem menu on right click title bar Pin
laura34-Sep-05 22:42
laura34-Sep-05 22:42 
AnswerRe: System menu on right click title bar Pin
Thornik26-May-13 13:12
Thornik26-May-13 13:12 
QuestionHow to implement shortcuts functionality for new system menu items? Pin
grzkas25-Aug-05 0:02
grzkas25-Aug-05 0:02 
AnswerRe: How to implement shortcuts functionality for new system menu items? Pin
Al Alberto27-Aug-05 13:54
Al Alberto27-Aug-05 13:54 
QuestionHow in Visual C++ 6.0 with mfc? Pin
monimicki18-Nov-04 0:18
monimicki18-Nov-04 0:18 

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

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