13.7.2017
Pokud vytváříte v Microsoft Visual Studiu aplikaci typu WPF (Windows Presentation Foundation), zjistíte ve vizuálním návrháři se nenabízí komponenta NotifyIcon, která je k disposici v aplikaci typu Windows Forms.
V tomto článku a ukázkové aplikaci si ukážeme jednoduchý způsob jak vytvořit NotifyIcon včetně fukcionality skrývání okna do této ikony.
Vytvoření takovéto ikony je standardní součástí Windows API a v .NET aplikaci využijeme proto volání příslušných funkcí pomocí DllImport
Po založení projektu (Visual C# - WPF aplikace pro klasický desktop) si nejprve připravíme potřebné deklarace hodnot, struktur a funkcí z Windows API, které budeme využívat. V ukázkovém projektu jsem tento soubor nazval WinApi.cs a zde je jeho kompletní výpis:
using System; using System.Runtime.InteropServices; namespace WinApi { internal static class UnsafeNativeMethods { [System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Globalization", "CA2101:SpecifyMarshalingForPInvokeStringArguments", MessageId = "NOTIFYICONDATA.szTip")] [System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Globalization", "CA2101:SpecifyMarshalingForPInvokeStringArguments", MessageId = "NOTIFYICONDATA.szInfoTitle")] [System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Globalization", "CA2101:SpecifyMarshalingForPInvokeStringArguments", MessageId = "NOTIFYICONDATA.szInfo")] [DllImport("shell32.dll", CharSet = CharSet.Unicode)] public static extern int Shell_NotifyIcon(int zprava, NativeMethods.NOTIFYICONDATA pNID); } internal static class NativeMethods { public const int WM_LBUTTONUP = 0x0202; public const int WM_APP = 0x8000; public const int wm_notify_ikona = WM_APP + 2001; public const int NIF_ICON = 0x00000002, NIF_MESSAGE = 0x00000001, NIF_TIP = 0x00000004, NIF_INFO = 0x00000010, NIF_STATE = 0x00000008, NIF_SHOWTIP = 0x00000080; public const int NIM_ADD = 0x00000000, NIM_MODIFY = 0x00000001, NIM_DELETE = 0x00000002, NIM_SETFOCUS = 0x00000003, NIM_SETVERSION = 0x00000004; public const int NOTIFYICON_VERSION_4 = 4; [StructLayout(LayoutKind.Sequential, CharSet = CharSet.Unicode)] public class NOTIFYICONDATA { public int cbSize = Marshal.SizeOf(typeof(NOTIFYICONDATA)); [System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Security", "CA2111:PointersShouldNotBeVisible")] public IntPtr hWnd = IntPtr.Zero; public int uID = 0; public int uFlags = 0; public int uCallbackMessage = 0; [System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Security", "CA2111:PointersShouldNotBeVisible")] public IntPtr hIcon = IntPtr.Zero; [MarshalAs(UnmanagedType.ByValTStr, SizeConst = 128)] public string szTip; public int dwState = 0; public int dwStateMask = 0; [MarshalAs(UnmanagedType.ByValTStr, SizeConst = 256)] public string szInfo; public int uTimeoutOrVersion = 0; [MarshalAs(UnmanagedType.ByValTStr, SizeConst = 64)] public string szInfoTitle; public int dwInfoFlags = 0; public Guid guidItem; [System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Security", "CA2111:PointersShouldNotBeVisible")] public IntPtr hBalloonicon = IntPtr.Zero; } } }
Nyní trochu teorie: Ikona v oznamovací oblasti panelu nástrojů se vytváří, ruší a modifikuje funkcí Shell_NotifyIcon, v jejímž parametru (je to adresa struktury NOTIFYICONDATA) se nastaví požadovaná funkčnost. Jedním z nutných parametrů je handle okna kterému ikona patří. Toto získáme s využitím třídy System.Windows.Interop.HwndSource. Do třídy hlavního okna si přidáme následující proměnnou:
private System.Windows.Interop.HwndSource hwndSource;
V obsluze události Loaded ji pak přiřadíme k okno a přidáme si vlastní funkci která bude jakýmsi bypassem procedury okna, tzn. budeme v ním moci zachytávat (a nějak na ně reagovat) všechny zprávy Windows které přijdou tomuto oknu:
this.hwndSource = (HwndSource)PresentationSource.FromVisual(this); this.hwndSource.AddHook(WindowProc);
Dalším nutným parametrem při vytvoření NotifyIcon je číslo vlastní zprávy, přes kterou bude tato ikona komunikovat se svým oknem. Tuto zprávu máme deklarovanou ve výše zmíněném souboru WinApi.cs takto:
public const int WM_APP = 0x8000; public const int wm_notify_ikona = WM_APP + 1;
Hodnota WM_APP je číslo od kterého začínají možné uživatelské zprávy tak aby nekolidovaly se standardními zprávami Windows.
Nyní se již můžeme podívat na funkce které vytvoří a zruší ikonu v oznamovací oblasti:
private void VytvoritNotifyIcon() { WinApi.NativeMethods.NOTIFYICONDATA nid = new WinApi.NativeMethods.NOTIFYICONDATA(); nid.hIcon = ikona.Handle; nid.hWnd = hwndSource.Handle; nid.uID = 1; nid.szTip = "Vývoj WMP"; nid.uFlags = WinApi.NativeMethods.NIF_ICON | WinApi.NativeMethods.NIF_MESSAGE | WinApi.NativeMethods.NIF_TIP; nid.uCallbackMessage = WinApi.NativeMethods.wm_notify_ikona; nid.uFlags |= WinApi.NativeMethods.NIF_SHOWTIP; if (WinApi.UnsafeNativeMethods.Shell_NotifyIcon(WinApi.NativeMethods.NIM_ADD, nid) == 0) throw new System.ComponentModel.Win32Exception(); nid.uTimeoutOrVersion = WinApi.NativeMethods.NOTIFYICON_VERSION_4; if (WinApi.UnsafeNativeMethods.Shell_NotifyIcon(WinApi.NativeMethods.NIM_SETVERSION, nid) == 0) throw new System.ComponentModel.Win32Exception(); } private void ZrusitNotifyIcon() { WinApi.NativeMethods.NOTIFYICONDATA nid = new WinApi.NativeMethods.NOTIFYICONDATA(); nid.hWnd = hwndSource.Handle; nid.uID = 1; if (WinApi.UnsafeNativeMethods.Shell_NotifyIcon(WinApi.NativeMethods.NIM_DELETE, nid) == 0) throw new System.ComponentModel.Win32Exception(); }
Funkci VytvoritNotifyIcon() zavoláme nejlépe na konci kódu obsluhy události Loaded a naopak funkci ZrusitNotifyIcon() při zrušení okna, tj. v obsluze události Closing.
Nyní zbývá podívat se jak vypadá zachycení a zpracování zprávy od ikony v proceduře okna:
private IntPtr WindowProc(IntPtr hwnd, int msg, IntPtr wParam, IntPtr lParam, ref bool handled) { switch (msg) { case WinApi.NativeMethods.wm_notify_ikona: int llp = lParam.ToInt32() & 0x0000FFFF; switch (llp) { case WinApi.NativeMethods.WM_LBUTTONUP: if (IsVisible) Hide(); else Show(); break; } break; } return IntPtr.Zero; }
Jak je zřejmé z kódu, je realizováno pouze to základní zachycení kliknutí myši, přesně řečeno reakce na puštění levého tlačítka. V reakci na toto otestujeme zda je okno právě viditelné a podle toho ho skryjeme nebo naopak znovu zobrazíme.
Ukázkový projekt (Microsoft Visual Studio Community 2017) si můžete stáhnout zde.
Školení
Kontakt
739 219 991
live:radekchalupa_1
Nové články