Click here to Skip to main content
15,891,529 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 122.2K   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

 
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 
GeneralCode Example Pin
stano3-Aug-04 21:24
stano3-Aug-04 21:24 
GeneralRe: Code Example Pin
Al Alberto4-Aug-04 13:19
Al Alberto4-Aug-04 13:19 
GeneralCool Pin
Richard Parsons3-Aug-04 3:30
Richard Parsons3-Aug-04 3:30 
GeneralRe: Cool Pin
hkfischer9-Aug-04 22:52
hkfischer9-Aug-04 22:52 
GeneralRe: Cool Pin
Richard Parsons10-Aug-04 8:09
Richard Parsons10-Aug-04 8:09 
I can say that I have never created a dialog based app with MFC, however I was hired by a company to develop version 2 of an app and the programmer that developed version 1 put the About on the System Menu. I never bothered to look at the code because to tell the truth it bugged me because I like to right click on the title and then click close. If there is something below the close menu I always click it instead DOH! Mad | :mad:

I can see that for console apps it would be better to have a system menu about but for a Windows.Forms app? Generally they have at least a File menu and if they have that why not go ahead and add Help>About?

Just my 2 cents

-Richard Parsons

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.