SystemMenu


These two screenshots show the new SystemMenu item, Always On Top that was added to the System menu, in both unchecked and checked states.

Screenshot, new menu item is not Checked

Screenshot, new menu item is Checked


Introduction

I was writing a Desktop Wallpaper changing utility in C# and I wanted to force the MainForm to remain on-top of all the other Windows when I clicked a menu item on the Form's System Menu.
I wrote this code so that I could interact with the Form's System Menu but it can be used with any other menu in your C# application.

SystemMenu is a simple wrapper class using pInvoke to manipulate menus, you can add or remove items (including separators) to an existing menu but cannot create a new menu as yet, I will be adding support for this in a future release.


Methods

These are the methods that the class supports.

        // Gets a handle to the system menu of a Form and stores it in the class
    public SystemMenu(Form form); 

        // Reset the system menu (To the default)
    public static void ResetSystemMenu(Form form);
                
        // Inserts a separator at the specified index (from 0)
    public bool InsertSeparator(uint index);

        // Inserts a menu item at the specified index (from 0, MF_BYPOSITION)
    public bool InsertMenu(uint index, uint id, String item);

        // Inserts a menu item at the specified index (flags: MF_BYPOSITION or MF_BYCOMMAND)
    public bool InsertMenu(uint index, MenuFlags flags, uint id, String newItem);

        // Append a separator to the end of the menu
    public bool AppendSeparator();

        // Appends a menu item, uses MF_STRING flag
    public bool AppendMenu(uint id, String newItem);

        // Check / Uncheck a menu item (MF_CHECKED | MF_UNCHECKED)
        // Supports MF_BYPOSITION or MF_BYCOMMAND
    public uint CheckMenuItem(uint index, MenuFlags flags);

        // Deletes a menu item, can be MF_BYPOSITION or MF_BYCOMMAND
    public bool RemoveMenu(uint index, uint flags);
    

Where these methods have a MenuFlags parameter you can use any of the following supported options which are taken from the WinUser.h header file. You can check the MSDN library for full details on these options.

    public enum MenuFlags // Our menu flags (Taken from "WinUser.h")
    {
        MF_INSERT = 0x00000000,
        MF_CHANGE = 0x00000080,
        MF_APPEND = 0x00000100,
        MF_DELETE = 0x00000200,
        MF_REMOVE = 0x00001000,

        MF_BYCOMMAND = 0x00000000,
        MF_BYPOSITION = 0x00000400,

        MF_SEPARATOR = 0x00000800,

        MF_ENABLED = 0x00000000,
        MF_GRAYED = 0x00000001,
        MF_DISABLED = 0x00000002,

        MF_UNCHECKED = 0x00000000,
        MF_CHECKED = 0x00000008,
        MF_USECHECKBITMAPS = 0x00000200,

        MF_STRING = 0x00000000,
        MF_BITMAP = 0x00000004,
        MF_OWNERDRAW = 0x00000100,

        MF_POPUP = 0x00000010,
        MF_MENUBARBREAK = 0x00000020,
        MF_MENUBREAK = 0x00000040,

        MF_UNHILITE = 0x00000000,
        MF_HILITE = 0x00000080
            }
    


Error Handling

This class has limited error handling support, it simply returns true or false as necessary and can throw an NoSystemMenuException exception if the constructor fails to find the Form's System Menu.


Using the SystemMenu class

It is very easy to integrate SystemMenu into your own C# applications, simple follow these instructions:

Step 1 - Including the SystemMenu class

Create a new C# Windows Forms project and double click on the form so that you can edit the underlying code.
We need to add a using statement to include the darka namespace, see the following code.

    using darka; // Reference the namespac

Step 2 - Add supporting variables

We need to add two variables; one for the class instance, and one for the enumeration containing the Windows SDK WM_SYSCOMMAND Identifier so that we can detect when the SystemMenu has been clicked on.

    using darka; // Reference our namespace
    
    namespace SystemMenuTest
    {
        public partial class MainForm : Form
        {
                // Define some constants/variables for the SystemMenu
            public enum SystemCommands
            {
                WM_SYSCOMMAND = 0x0112 // The WM_SYSCOMMAND id, from 
            }
    
            private SystemMenu _sysMenu = null;

            // ... Code removed for brevity            
        }
    }

Step 3 - Create an instance of the class

We can now create an instance of the class in the MainForm_Load() method (Double click on the Load method on the Events tab of the Form Properties) and then copy/paste the code from the following sample.

System Menu Properties Page

    private void MainForm_Load(object sender, EventArgs e)
    {
            try
            {
                    // Create an instance of the class
                _sysMenu = new SystemMenu(this);

                // Set up the system menu (Always on top item)
                _sysMenu.AppendSeparator();
                _sysMenu.AppendMenu(IDC_ALWAYS_ON_TOP, "Always On Top");
            }
            catch (NoSystemMenuException ex)
            {
                String error = "Error: GetSystemMenuW() Failed - (";
                error += ex.Message;
                error += ")";

                MessageBox.Show(error, "TestApp");
            }
        }

Step 4 - Override WndProc

Then we need to override the WndProc method so that we can process messages received from the system menu, as shown below (See the sample code for a fully working implementation).

    namespace SystemMenuTest
    {
        public partial class MainForm : Form
        {
            //
            // ... Code removed for brevity
            
                // Override the forms virtual WndProc function
            protected override void WndProc(ref Message msg)
            {
                // Only look at WM_SYSCOMMAND messages
                    if (msg.Msg == (int)SystemCommands.WM_SYSCOMMAND)
                    {
                            // Is this our new menu item ?
                        if (msg.WParam.ToInt32() == IDC_ALWAYS_ON_TOP)
                        {
                            SetAlwaysOnTop(); // Yes, so set the form on top of all other windows
                            return;
                        }
                    }
        
                    base.WndProc(ref msg); // Call the default WndProc for other messages
            }
        }
        }
    

Step 5 - Write our menu handler

We are actually done adding the SystemMenu class to the project, all we need to do now is create our menu handling code SetAlwaysOnTop() to toggle between on top and normal positions.

    namespace SystemMenuTest
    {
        public partial class MainForm : Form
        {

            //
            // ... Code removed for brevity
            
                // Set the main form always on top (or not)
            private void SetAlwaysOnTop()
            {
                alwaysOnTop_ = !alwaysOnTop_;
                this.TopMost = alwaysOnTop_; // Toggle the ontop state

                    // Check/Uncheck the menu item appropriately
                sysMenu_.CheckMenuItem(Constants.IDC_ALWAYS_ON_TOP, SystemMenu.MenuFlags.MF_BYCOMMAND 
                    | ((alwaysOnTop_) ? SystemMenu.MenuFlags.MF_CHECKED : SystemMenu.MenuFlags.MF_UNCHECKED));
            }
            }
        }
    

Change History ([New]New, [BugFix]BugFix, [Update]Updated)


[Update] Nov 2010 (v1.1) - Improved the article and sample code.
[New] Feb 2008 (v1.0) - Initial Public Release.