Добавляем свои кнопки в заголовок окна Windows Form(WinAPI)


Вам для работы необходимо добавить класс SystemMenu.cs. Ну или взять код приведенный ниже и добавить его в форму(окно) в котором вы хотите добавить свои пункты! Но тут возникает два момента:
1) При добавлении класса SystemMenu.cs вы можете обращаться к нему из любой формы.
2) При добавлении кода показанного ниже, его необходимо будет добавлять в каждую форму где вы хотите добавить пункты меню!
3) Вам необходимо будет подключить пространства имен:

using System;
using System.Windows.Forms;
using System.Diagnostics;
using System.Runtime.InteropServices;


Вот сам класс:

public class SystemMenu
{
    public enum ItemFlags
    { // The item ...
        mfUnchecked = 0x00000000,    // ... is not checked
        mfString = 0x00000000,    // ... contains a string as label
        mfDisabled = 0x00000002,    // ... is disabled
        mfGrayed = 0x00000001,    // ... is grayed
        mfChecked = 0x00000008,    // ... is checked
        mfPopup = 0x00000010,    // ... Is a popup menu. Pass the
        //     menu handle of the popup
        //     menu into the ID parameter.
        mfBarBreak = 0x00000020,    // ... is a bar break
        mfBreak = 0x00000040,    // ... is a break
        mfByPosition = 0x00000400,    // ... is identified by the position
        mfByCommand = 0x00000000,    // ... is identified by its ID
        mfSeparator = 0x00000800     // ... is a seperator (String and
        //     ID parameters are ignored).
    }

    public enum WindowMessages
    {
        wmSysCommand = 0x0112
    }
    // I havn't found any other solution than using plain old
    // WinAPI to get what I want.
    // If you need further information on these functions, their
    // parameters, and their meanings, you should look them up in
    // the MSDN.

    // All parameters in the [DllImport] should be self explanatory.
    // NOTICE: Use never stdcall as a calling convention, since Winapi
    // is used.
    // If the underlying structure changes, your program might cause
    // errors that are hard to find.

    // First, we need the GetSystemMenu() function.
    // This function does not have an Unicode counterpart
    [DllImport("USER32", EntryPoint = "GetSystemMenu", SetLastError = true,
               CharSet = CharSet.Unicode, ExactSpelling = true,
               CallingConvention = CallingConvention.Winapi)]
    private static extern IntPtr apiGetSystemMenu(IntPtr WindowHandle,
                                                  int bReset);

    // And we need the AppendMenu() function. Since .NET uses Unicode,
    // we pick the unicode solution.
    [DllImport("USER32", EntryPoint = "AppendMenuW", SetLastError = true,
               CharSet = CharSet.Unicode, ExactSpelling = true,
               CallingConvention = CallingConvention.Winapi)]
    private static extern int apiAppendMenu(IntPtr MenuHandle, int Flags,
                                             int NewID, String Item);

    // And we also may need the InsertMenu() function.
    [DllImport("USER32", EntryPoint = "InsertMenuW", SetLastError = true,
               CharSet = CharSet.Unicode, ExactSpelling = true,
               CallingConvention = CallingConvention.Winapi)]
    private static extern int apiInsertMenu(IntPtr hMenu, int Position,
                                              int Flags, int NewId,
                                              String Item);

    private IntPtr m_SysMenu = IntPtr.Zero;    // Handle to the System Menu

    public SystemMenu()
    {
    }

    // Insert a separator at the given position index starting at zero.
    public bool InsertSeparator(int Pos)
    {
        return (InsertMenu(Pos, ItemFlags.mfSeparator |
                            ItemFlags.mfByPosition, 0, ""));
    }

    // Simplified InsertMenu(), that assumes that Pos is a relative
    // position index starting at zero
    public bool InsertMenu(int Pos, int ID, String Item)
    {
        return (InsertMenu(Pos, ItemFlags.mfByPosition |
                            ItemFlags.mfString, ID, Item));
    }

    // Insert a menu at the given position. The value of the position
    // depends on the value of Flags. See the article for a detailed
    // description.
    public bool InsertMenu(int Pos, ItemFlags Flags, int ID, String Item)
    {
        return (apiInsertMenu(m_SysMenu, Pos, (Int32)Flags, ID, Item) == 0);
    }

    // Appends a seperator
    public bool AppendSeparator()
    {
        return AppendMenu(0, "", ItemFlags.mfSeparator);
    }

    // This uses the ItemFlags.mfString as default value
    public bool AppendMenu(int ID, String Item)
    {
        return AppendMenu(ID, Item, ItemFlags.mfString);
    }
    // Superseded function.
    public bool AppendMenu(int ID, String Item, ItemFlags Flags)
    {
        return (apiAppendMenu(m_SysMenu, (int)Flags, ID, Item) == 0);
    }

    // Retrieves a new object from a Form object
    public static SystemMenu FromForm(Form Frm)
    {
        SystemMenu cSysMenu = new SystemMenu();

        cSysMenu.m_SysMenu = apiGetSystemMenu(Frm.Handle, 0);
        if (cSysMenu.m_SysMenu == IntPtr.Zero)
        { // Throw an exception on failure
            return null;
        }

        return cSysMenu;
    }

    // Reset's the window menu to it's default
    public static void ResetSystemMenu(Form Frm)
    {
        apiGetSystemMenu(Frm.Handle, 1);
    }

    // Checks if an ID for a new system menu item is OK or not
    public static bool VerifyItemID(int ID)
    {
        return (bool)(ID < 0xF000 && ID > 0);
    }    
}

Примеры как можно использовать данный класс:

private SystemMenu m_SystemMenu = null;
public const Int32 WM_SYSCOMMAND = 0x112;
private const int m_AboutID = 0x100;
private const int menu0 = 0x101;  
private void frmMain_Load(object sender, System.EventArgs e)
{
   m_SystemMenu = SystemMenu.FromForm(this);
   m_SystemMenu.AppendSeparator();
   m_SystemMenu.AppendMenu(m_AboutID, "About this...");    
}
Далее работаем по образу и по подобию верхнего примера!
m_SystemMenu.InsertSeparator(0);
m_SystemMenu.InsertMenu(0, menu0, "Menu");
m_SystemMenu.InsertMenu(menu1, SystemMenu.ItemFlags.mfBarBreak, 1, "ButtonBar1");
m_SystemMenu.AppendSeparator();
m_SystemMenu.InsertMenu(menu2, SystemMenu.ItemFlags.mfByPosition, 2, "ButtonBar2");
m_SystemMenu.InsertMenu(menu0, SystemMenu.ItemFlags.mfDisabled, 3, "ButtonBar3");
m_SystemMenu = SystemMenu.FromForm(this);
m_SystemMenu.InsertMenu(m_AboutID, SystemMenu.ItemFlags.mfChecked, 3, "ButtonBar3");
Думаю примеров достаточно, теперь необходимо поймать события нажатия наших созданных пунктов меню! Смотрите пример ниже:
protected override void WndProc(ref Message msg)
{  
    if (msg.Msg == (int)SystemMenu.WindowMessages.wmSysCommand)
    {               
        switch (msg.WParam.ToInt32())
        {
            case 3:
                { 
                  MessageBox.Show(this, "Test", "Menu3");
                    
                } break;
        }
    }  
    base.WndProc(ref msg);
}
Обратите внимание, если вы хотите поймать нажатие пункта меню созданной конструкцией:
m_SystemMenu.InsertMenu(menu0, SystemMenu.ItemFlags.mfDisabled, 3, "ButtonBar3");
То в конструкции switch-CASE вы указываете место(цифру) куда добавили пункт:
Если вы воспользовались конструкцией:
m_SystemMenu = SystemMenu.FromForm(this);
m_SystemMenu.AppendSeparator();
m_SystemMenu.AppendMenu(m_AboutID, "About this...");
То указываете m_AboutID!

Также в каждой форме необходимо добавить класс обработки ошибок:

public class NoSystemMenuException : System.Exception
    {
    }

Исходник скачиваем тут: http://csharpcoderr.rusfolder.net/files/38306596


Комментариев нет:

Отправить комментарий

Большая просьба, не писать в комментариях всякую ерунду не по теме!