Úprava systémové nabídky formuláře - Builder.cz - Informacni server o programovani

Odběr fotomagazínu

Fotografický magazín "iZIN IDIF" každý týden ve Vašem e-mailu.
Co nového ve světě fotografie!

 

Zadejte Vaši e-mailovou adresu:

Kamarád fotí rád?

Přihlas ho k odběru fotomagazínu!

 

Zadejte e-mailovou adresu kamaráda:



C/C++

Úprava systémové nabídky formuláře

12. února 2001, 00.00 | Chcete přidat vlastní položky do systémového menu formuláře, nebo upravit stávající výchozí položky? Více se dozvíte v tomto článku!

Chcete z nějakého důvodu (i když to není příliš obvyklé) přidat vlastní položky do systémového menu formuláře, nebo upravit stávající výchozí položky třeba tak, jak je vidět na obrázku?

Pozor - jak vidíte, nejde o běžné tzv. hlavní menu okna, ale o nabídku, která se rozbalí například při kliknutí na ikonku v levém horním rohu titulkového pruhu formuláře. Co k tomu potřebujeme? Za prvé musíme získat handle na systémové menu formuláře. K tomu použijeme API funkci

HMENU GetSystemMenu(HWND hwnd, BOOL bRevert);

Pro vlastní přidání položek pak použijeme API funkci

BOOL InsertMenuItem(HMENU hMenu, UINY uItem, BOOL fbyPositon, LPCMENUITEMINFO lpmi);

Před tím musíme správně naplnit strukturu MENUITEMINFO (parametr lpmi). Nejlépe bude ukázat si vše na příkladu. Následující funkce AddSysMenuItems přidá do systémového menu nejprve oddělovač (separátor) před položku "Zavřít ...", a před tento separátor pak dvě vlastní položky. S položkami jsou přidány také bitmapy (je použita stejná bitmapa u obou položek). Handle této bitmapy, které potřebujeme při naplnění struktury MENUITEMINFO, načítáme v konstruktoru ze zdrojů (resources) programu.

__fastcall TFMain::TFMain(TComponent* Owner)
: TForm(Owner)
{
m_MenuBmp = LoadBitmap(HInstance, "MENUITEM");
m_MenuBmp2 = LoadBitmap(HInstance, "NO");
AddSysMenuItems();
ModifyItemMinimize();
}

bool __fastcall TFMain::AddSysMenuItems()
{
 HMENU hSysMenu = GetSystemMenu(Handle, FALSE);
 if ( hSysMenu == NULL )
 return false;
 MENUITEMINFO mii;
 // nejdříve přidáme separátor před položku "zavřít"
 mii.cbSize = sizeof(MENUITEMINFO);
 mii.fMask = MIIM_TYPE | MIIM_STATE | MIIM_ID;
 mii.fType = MFT_SEPARATOR;
 mii.fState = MFS_DISABLED;
 mii.wID = IDM_MYSEPARATOR;
 mii.hSubMenu = NULL;
 mii.hbmpChecked = NULL;
 mii.hbmpUnchecked = NULL;
 mii.dwItemData = NULL;
 mii.dwTypeData = NULL;
 mii.cch = NULL;
 mii.hbmpItem = NULL;
 InsertMenuItem (hSysMenu, SC_CLOSE, FALSE, &mii);

 // přidání položky1
 mii.cbSize = sizeof(MENUITEMINFO);
 mii.fMask = MIIM_STATE | MIIM_ID | MIIM_BITMAP | MIIM_STRING;
 mii.fState = MFS_ENABLED;
 mii.wID = IDM_MYITEM1;
 mii.dwTypeData = "Moje položka 1";
 mii.hbmpItem = m_MenuBmp; // handle na bitmapu 16x16 pixelu
 InsertMenuItem (hSysMenu, IDM_MYSEPARATOR, FALSE, &mii);

 // přidání položky2
 mii.wID = IDM_MYITEM2;
 mii.hbmpItem = m_MenuBmp; // handle na bitmapu 16x16 pixelu
 mii.dwTypeData = "Moje položka 2";
 return InsertMenuItem (hSysMenu, IDM_MYSEPARATOR, FALSE, &mii);
}

Kromě přidání položek můžeme i upravovat stávající položky, včetně těch výchozích. V ukázkovém programu je příklad na modifikaci položky "Minimalizovat". U této položky je změněn text i bitmapa. Jak, to vidíte v následující funkci:

void __fastcall TFMain::OnWM_SYSCOMMAND(TMessage& Msg)
{
 switch ( Msg.WParam )
 {
  case IDM_MYITEM1:
   MessageBox(Handle, "Byla vybrána vlastní položka 1", "Systémové menu",MB_ICONINFORMATION);
   if ( MessageBox(Handle, "Odstranit položku 2", "Dotaz",MB_YESNO | MB_ICONQUESTION) == IDYES )
    DeleteItem2();
  break;
  case IDM_MYITEM2:
   MessageBox(Handle, "Byla vybrána vlastní položka 2", "Systémové menu", MB_ICONINFORMATION);
   ModifyItem2();
  break;
  case SC_MINIMIZE:
  MessageBox(Handle, "Minimalizace je zakázána", "Systémové menu",MB_ICONINFORMATION);
  return;
 }
 TForm::Dispatch(&Msg);
}

Vidíte, že nejdříve získáme informace o dané položce pomocí API funkce GetSystemMenu, která nám naplní strukturu MENUITEMINFO. V této struktuře pak provedeme příslušné úpravy a použijeme ji jako parametr volání API funkce SetMenuItemInfo. Tím tedy máme v systémovém menu vlastní položky, ale potřebujeme také umět v programu zachytit jejich vybrání uživatelem. Jak to provedeme? Systémové menu posílá svému oknu zprávu WM_SYSCOMMAND (na rozdíl od "normálního" menu, které posílá zprávu WM_COMMAND). V dokumentaci je u popisu zprávy WM_SYSCOMMAND poněkud zavádějící označení parametru wParam jako "system command type". To může svádět k myšlence, že tento parametr může nabývat pouze jedné ze zde uvedených hodnot SC_xxxx. Ve skutečnosti se jedná, podobně jako u zprávy WM_COMMAND o identifikátor položky menu. Také konstanty SC_xxxx jsou takovými identifikátory příkazu. Závěr z toho je jasný. Stačí mít vlastní handler zprávy WM_SYSCOMMAND a hodnotu wParam testovat na vlastní definované identifikátory. V doprovodném příkladu jsem vytvořil takovýto handler:

bool __fastcall TFMain::ModifyItemMinimize()
{
 HMENU hSysMenu = GetSystemMenu(Handle, FALSE);
 if ( hSysMenu == NULL ) return false;
 MENUITEMINFO mii;
 mii.cbSize = sizeof(MENUITEMINFO);
 GetMenuItemInfo(hSysMenu, SC_MINIMIZE, FALSE, &mii);
 mii.fMask = MIIM_STRING | MIIM_BITMAP;
 mii.dwTypeData = "Minimalizace zakázána";
 mii.hbmpItem = m_MenuBmp2;
 return SetMenuItemInfo(hSysMenu, SC_MINIMIZE, FALSE, &mii);
}
V handleru zprávy WM_SYSCOMMAND je také ukázáno, jak lze zprávu zadržet, tedy v případě požadavku na minimalizaci nepustit k výchozímu zpracování, tedy minimalizaci zakázat. Volané funkce DeleteItem2 a ModifyItem2 demonstrují, jak můžeme programově změnit text i bitmapu naší existující položky, resp. jak tuto položku zakázat (disabled), a jak takovou položku zcela odstranit. Tyto funkce vypadají takto:

bool __fastcall TFMain::ModifyItem2()
{
 HMENU hSysMenu = GetSystemMenu(Handle, FALSE);
 if ( hSysMenu == NULL )
 {
  MessageBox(NULL, "Nelze získat handle na systémové menu!","Chyba", MB_ICONERROR);
  return false;
 }
 MENUITEMINFO mii;
 mii.cbSize = sizeof(MENUITEMINFO);
 GetMenuItemInfo(hSysMenu, IDM_MYITEM2, FALSE, &mii);
 mii.fMask = MIIM_STRING | MIIM_BITMAP | MIIM_STATE;
 mii.fState = MFS_DISABLED;
 mii.dwTypeData = "Položka již byla vybrána";
 mii.hbmpItem = NULL;
 return SetMenuItemInfo(hSysMenu, IDM_MYITEM2, FALSE, &mii);
}

bool __fastcall TFMain::DeleteItem2()
{
 HMENU hSysMenu = GetSystemMenu(Handle, FALSE);
 if ( hSysMenu == NULL )
 {
  MessageBox(NULL, "Nelze získat handle na systémové menu!","Chyba", MB_ICONERROR);
 return false;
 }
 return DeleteMenu(hSysMenu, IDM_MYITEM2, MF_BYCOMMAND);
}
Ještě pár poznámek k doprovodnému příkladu: Vlastní bitmapy položek menu jsou uloženy v přeloženém souboru zdrojů user.res, který je přilinkován do programu příkazem

USERES("user.res");

v hlavním modulu projektu (SysMenu.cpp).
Vytvoření handleru zprávy WM_SYSCOMMAND je realizováno pomocí mapy zpráv, jejíž princip je dostatečně popsán v nápovědě C++ Builderu a myslím, že většina programátorů dokáže přidat vlastní handler zprávy. Pro úplnost zde uvedu postup, jak lze přidat kostru handleru pomocí ClassExploreru. Vyvoláte kontextovou nabídku (např. pravým tlačítkem myši) objektu formuláře, vyberete "New Method...", a následný dialog vyplníte třeba takto:


Položku "Method Name" můžete vyplnit libovolným názvem metody, důležitý je typ argumentu: Můžete použít obecný typ zprávy TMessage, nebo typ TWMSysCommand, který je přizpůsobený konkrétní zprávě, v našem případě WM_SYSCOMMAND. Po potvrzení tohoto dialogu nám "wizard" vytvoří v deklaraci třídy formuláře tzv. mapu zpráv pomocí maker BEGIN_MESSAGE_MAP a END_MESSAGE_MAP:

BEGIN_MESSAGE_MAP
 VCL_MESSAGE_HANDLER(WM_SYSCOMMAND, TMessage, OnWM_SYSCOMMAND)
END_MESSAGE_MAP(TForm)

Vlastní kód handleru pak vidíte ve výše uvedeném výpisu.
V souboru UkazkovyProjekt.zip je vše, co bylo zmíněno v tomto článku, demonstrováno v konkrétním projektu, jehož "screenshot" je na začátku článku.

UkazkovyProjekt.zip (z důvodu velikosti je linkován s použitím run-time balíčku a RTL knihovny)

 

Tématické zařazení:

 » Rubriky  » C/C++  

 » Rubriky  » Delphi  

 

 

 

Nejčtenější články
Nejlépe hodnocené články

 

Přihlášení k mému účtu

Uživatelské jméno:

Heslo: