Radek ChalupaČlánkyUkázky kóduTipy a trikyAktualityŠkolení a konzultaceVývoj softwareFreewareKontakt

Ikona aplikace v oznamovací oblasti.  24.1.2002

Ukážeme si jak aplikaci tzv. "minimalizovat do traye", což z programátorského pohledu není přesný termín, také proto ty uvozovky. Žádný režim zobrazení okna "v trayi" samozřejmě neexistuje, jedná se o pouhou simulaci, která se z pohledu uživatele jeví jako minimalizace do tray-ikony. Uveďme si nejprve přehled kroků, které musíme učinit pro dosažení kýženého efektu:
  • Při zachycení požadavku na minimalizaci okna okno skrýt příkazem ShowWindow(handle, SW_HIDE)
  • Zaregistrovat a zobrazit ikonu v oznamovací oblasti.
  • Zachytávat "myší zprávy" od této ikony.
  • Po zachycení vhodné zprávy (např. dvojklik na ikoně) okno opět zobrazit příkazem ShowWindow(handle, SW_RESTORE), volitelně můžeme okno navíc přenést do popředí příkazem SetForegroundWindow(handle).
  • Nejpozději před zrušením okna ikonu z oznamovací oblasti opět odebrat.
Je na nás, zda ikonu v oznamovací oblasti necháme po celou dobu života okna, nebo zda ji tam zobrazíme pouze při skrytí okna a při jeho zobrazení ji odstraníme. Ukažme si tedy, jak zaregistrovat a zobrazit ikonu v oznamovací oblasti. Slouží k tomu funkce Shell_NotifyIcon:
BOOL Shell_NotifyIcon(
    DWORD dwMessage, 
    PNOTIFYICONDATA lpdata
);
Parametr dwMessage určuje akci, kterou chceme provést. V našem případě si ukážeme ty 2 základní, tedy přidání a odebrání ikony. Pro přidání ikony použijeme hodnotu NIM_ADD a pro odebrání NIM_DELETE. Nastavení vlastností a parametrů ikony provedeme naplněním struktury NOTIFYICONDATA, jejíž adresu pak použijeme jako 2. parametr funkce Shell_NotifyIcon. Podrobný popis prvků struktury naleznete v dokumentaci, zde jen upozorním, že pokud je definován požadavek na InternetExplorer verze 5 a vyšší (tj. hodnota _WIN32_IE větší nebo rovna 0x0500), má tato strukrura navíc několik prvků, které umožňují zobrazit takzvaný balónový tooltip, jaký vidíte na obrázku:

win-api-24

Podívejme se nyní jak vypadá funkce, realizující přidání ikony do oznamovací oblasti:
// Přidání ikony do oznamovací oblasti

BOOL AddNotifyIcon()
{
  NOTIFYICONDATA nid;
  ZeroMemory(&nid, sizeof(nid));
  nid.cbSize = sizeof(NOTIFYICONDATA);
  nid.hWnd = g_hWnd;
  nid.hIcon = (HICON)GetClassLongPtr(g_hWnd, GCLP_HICONSM);
  if ( !nid.hIcon )
    return FALSE;
  lstrcpyn(nid.szTip, chAppName, sizeof(nid.szTip));
  nid.uID = 1;
  nid.uCallbackMessage = WM_NOTIFY_ICON;
  
#if _WIN32_IE >= 0x0500 
  nid.uFlags = NIF_ICON | NIF_MESSAGE | NIF_TIP | NIF_INFO;
  nid.dwInfoFlags = NIIF_INFO;
  nid.dwState = 0;
  nid.uVersion = NOTIFYICON_VERSION; 
  nid.uTimeout = 10000;
  lstrcpy(nid.szInfo, TEXT("Tady může být\nnějaký text do 255 znaků\n:-))"));
  lstrcpy(nid.szInfoTitle, chAppName);
#else 
  nid.uFlags = NIF_ICON | NIF_MESSAGE | NIF_TIP;
#endif 
  if ( !Shell_NotifyIcon(NIM_ADD, &nid) )
  {
    MessageBox(g_hWnd, TEXT("Nelze přidat ikonu"),
    	NULL, MB_ICONERROR | MB_TASKMODAL);
    return FALSE;
  }
  return TRUE;
}
Zde poznámku a současně výzvu k diskusi. Podle dokumentace může být parametr uTimeout, určující dobu, po kterou bude balónový dialog zobrazen, v rozsahu 10 - 30 sekund, tedy mít hodnotu 10000 - 30000 (v milisekundách). Avšak podle mých zkušeností ve Windows XP zůstane balónový dialog zobrazen do té doby, dokud uživatel neprovede nějakou akci, která způsobí jeho deaktivaci, bez ohledu na nastavený time-out. Naopak ve Windows 2000 nelze (alespoň mě se to nepodařilo) nechat "balón" zobrazen déle než uvedených 30 sekund (v souladu s dokumentací). Pokud tedy někdo víte nebo zjistíte na toto téma více, jistě všichni uvítáme tyto poznatky, zatím to považujme za nedokumentovanou vlastnost Windows XP. Při najetí myší na ikonu v oznamovací oblasti se zobrazí standardní tool-tip (ten známý většinou žlutý plovoucí text), který zadáme jako prvek szTip struktury NOTIFYICONDATA. Tento text je vždy jednořádkový a jeho maximální délka závisí opět na verzi InternetExoloreru, konkrétně ve verzi nižší než 5 může mít 64 znaků, ve verzi 5 a výš až 128 znaků. Podívejme se dále na prvek uCallbackMessage. Jde u uživatelsky definovanou zprávu, kterou ikona posílá svému vlastníku (oknu určeného prvkem hWnd), pokud dojde k nějaké události myši na této ikoně. Parametr lParam této zprávy pak je identifikátor odpovídající myší zprávy (např. WM_LBUTTONDOWN apod.). Je však třeba počítat s tím, že ne všechny myší zprávy jsou zachytávány, například zprávy WM_MOUSEWHEEL (kolečko myši) nebo WM_MOUSELEAVE detekovány nejsou. Tuto uživatelskou zprávu si nadefinujeme tak, aby měla hodnotu vyšší než WM_USER, nebo lépe WM_APP, tedy např. takto:
#define WM_NOTIFY_ICON  ( WM_APP + 1 )
Nyní si uveďme kód, který naopak odebere ikonu z oznamovací oblasti. Zde stačí uvést pouze identifikátor ikony (uID) a handle okna, kterému ikona patří (hWnd):
// Odebrání ikony z oznamovací oblasti

BOOL DeleteNotifyIcon()
{
  NOTIFYICONDATA nid;
  ZeroMemory(&nid, sizeof(nid));
  nid.cbSize = sizeof(NOTIFYICONDATA);
  nid.hWnd = g_hWnd;
  nid.uID = 1;
  if ( !Shell_NotifyIcon(NIM_DELETE, &nid) )
  {
    MessageBox(g_hWnd, TEXT("Nelze odebrat ikonu"), NULL, MB_ICONERROR | MB_TASKMODAL);
    return FALSE;
  }
  return TRUE;
}
Jen připomenu, že v obou případech nesmíme zapomenou naplnit prvek cbSize této struktury. V našem ukázkovém programu přidáme ikonu hned po vytvoření okna a odebereme ji těsně před jeho zrušením. Opět upozornění - odebrání musíme udělat opravdu před zrušením okna, nikoli až třeba někde před koncem programu, abychom v tomto okamžiku měli ještě platný handle okna, který použijeme jako prvek hWnd struktury NOTIFYICONDATA. Nyní se podívejme na proceduru okna, kde provedeme další nutné kroky k realizaci toho nejjednoduššího "skrývání okna do oznamovací oblasti":
// Procedura okna 

LRESULT CALLBACK WndProc(HWND hWnd, UINT uMsg, WPARAM wParam, LPARAM lParam)
{
  switch ( uMsg ) 
  {
    case WM_CLOSE:
      DeleteNotifyIcon();
      break;
    case WM_DESTROY:
      PostQuitMessage(0);
      break;
    case WM_NOTIFY_ICON:
      switch (lParam)
      {
        case WM_LBUTTONDBLCLK:
          ShowWindow(hWnd, SW_RESTORE);
          SetForegroundWindow(hWnd);
          break;
      }
      break;
    case WM_SYSCOMMAND:
      switch ( wParam )
      {
        case SC_MINIMIZE:
          ShowWindow(hWnd, SW_HIDE);
          return 0;
      }
      break;
  }
  return DefWindowProc(hWnd, uMsg, wParam, lParam);
}
Je vidět, jak zachytíme požadavek na minimalizaci okna v handleru zprávy WM_SYSCOMMAND, kde při parametru wParam rovném hodnotě SC_MINIMIZE tuto zprávu zadržíme, tedy vrátíme 0 místo zpracování výchozí procedurou, a místo toho skryjeme okno funkcí ShowWindow s parametrem SW_HIDE. Naopak při zachycení dvojkliku na oznamovací ikonu okno opět zobrazíme v původní pozici před skrytím a ještě ho navíc přesuneme do popředí. Doprovodný projekt je ke stažení zde: win_api_24.zip

Copyright © 2019 - Radek Chalupa