Wstęp
W niniejszym dokumencie pokażę Wam jak oprogramować pasek zadań na platformie Windows. Jaki jest tego sens – zapytałbyś zapewne. W aplikacjach użytkowych jest to bardzo pomocna rzecz. Dzięki niej możemy powiadomić użytkownika o aktualnym stanie aplikacji i umożliwić sterowanie aplikacją bez konieczności wyciągania okna na wierzch. Dokument zawiera opis jak oprogramować:
Wyjaśnienie:
* chodzi o kartę; składa się ona z ikonki i tekstowej etykiety. Użyłem wyrazu ikona, ponieważ lepiej brzmi :)
|
Miganie aplikacji na pasku zadań
Zapewne nieraz widziałeś jak po ściągnięciu jakiegoś pliku z Internetu ikona przeglądarki się podświetla, a potem wraca do poprzedniego stanu i tak w kółko. Nazwałem to sobie „miganiem”. Wygląda to następująco:
Zatem, jak to osiągnąć? Służą do tego dwie funkcje
FlashWindow oraz
FlashWindowEx. Ta pierwsza jest prostsza, jednak my zajmiemy się tą drugą ;). Jej deklaracja wygląda następująco:
BOOL WINAPI FlashWindowEx(
PFLASHWINFO pfwi
);
Jak zapewne zauważyłeś – przyjmuje ona tylko jeden argument. Jest to wskaźnik na strukturę
FLASHWINFO. Przyjrzyjmy się niej.
typedef struct
{
UINT cbSize;
HWND hwnd;
DWORD dwFlags;
UINT uCount;
DWORD dwTimeout;
} FLASHWINFO, * PFLASHWINFO;
Jak widać nie jest duża :). Oto zestawienie składników i ich znaczenia:
Możliwe flagi określające status migania:
Część teoretyczną mamy za sobą. „Nareszcie!” – odrzekłbyś ze szczęściem :). W praktyce jest to dużo łatwiejsze, sam się przekonasz :P. Zbierzmy więc całą dotychczasową wiedzę i napiszmy krótki podsumowujący kod:
FLASHWINFO obj;
ZeroMemory( & obj, sizeof( FLASHWINFO ) );
obj.cbSize = sizeof( FLASHWINFO );
obj.dwFlags = FLASHW_TRAY;
obj.hwnd = g_hwnd;
obj.uCount = 3;
obj.dwTimeout = 300;
FlashWindowEx( & obj );
Ten kod spowoduje nam trzykrotne mignięcie okna (każde trwające 300ms), a następnie nasza ikonka pozostanie w stanie podświetlonym. Krótki kod, wspaniały (:P) efekt… To nie takie trudne, nieprawdaż? :)
Uwagi
Nie wiem czemu, ale ta funkcja nie działa na oknach dialogowych. (W każdym razie mi :P.)
Link do dokumentacji MSDN:
http://msdn.microsoft.com/en-us/library/windows/desktop/ms679347(v=vs.85).aspx
Dodawanie i odejmowanie ikon na pasku zadań
Spotkałeś się zapewne z aplikacjami, które mimo, że „są włączone tylko jeden raz” (mają aktualnie jedną instancję) to na pasku zadań mają wiele ikon bądź nie mają żadnej. Jest to ciekawa i użyteczna funkcjonalność, którą nieraz byłoby warto „wpleść” w naszą aplikację. „Jak to zrobić?” – niecierpliwiła by się część czytelników. Jest to łatwe do zrobienia, lecz jest jedno „ale”. Rzekoma funkcjonalność jest dostępna dopiero od Windows 2000. Ponadto musimy zarejestrować w systemie komunikat. „Wiedziałem, że to zbyt piękne by było prawdziwe” – pomyślałeś zapewne. Nie przejmuj się, damy radę :)
Sprawdzenie wersji systemu
Wiem, że wiesz jakiego masz Windowsa, ale Twój program nie wie :P. Co z niego za idiota, nie? W końcu to nie ja go stworzyłem :D. Musimy więc napisać kod, który sprawdzi wersję używanego systemu. Skorzystamy ze struktury
OSVERSIONINFO. Ważniejsze jej pola to:
My pobierzemy wersję i porównamy ją z Windowsem 7, z tego powodu, iż będzie to nam potrzebne do dalszych części artykułu :). Co ciekawe, Windows 7 nie jest w wersji 7 tylko 6.1! :P. Będziemy więc zmuszeni do sprawdzenia wersji głównej i pomniejszej.
Dane pobierzemy funkcją
GetVersionEx:
OSVERSIONINFO osv;
ZeroMemory( & osv, sizeof( OSVERSIONINFO ) );
osv.dwOSVersionInfoSize = sizeof( OSVERSIONINFO );
GetVersionEx( & osv );
bool Win7lubNowszy =(( osv.dwMajorVersion == 6 ) &&( osv.dwMinorVersion >= 1 ) ) ||( osv.dwMajorVersion >= 7 );
Interfejs ITaskbarList
To jest sedno całego artykułu –
ITaskbarList. Co ciekawsze, jest to prawdziwa klasa! Taka z metodami! Co jest rzadkością w WinAPI :P. (WinAPI było napisane w C, ten interfejs najwyraźniej w C++ :).) Spowodowanie aby ten interfejs działał jest nieco męczące...
Wskaźnik do interfejsu uzyskujemy w procedurze komunikatu, który wcześniej sami musimy zarejestrować :/. Oto przykład, który najlepiej sobie skopiować w całości:
#include <Shobjidl.h>
ITaskbarList3 * ptl;
LRESULT CALLBACK WindowProcedure( HWND hwnd, UINT msg, WPARAM wParam, LPARAM lParam )
{
static DWORD g_wmTBC =( DWORD ) - 1;
if( msg == g_wmTBC )
{
if( CoCreateInstance( CLSID_TaskbarList, NULL, CLSCTX_ALL, IID_ITaskbarList3,( LPVOID * ) & ptl ) != S_OK )
Win7lubNowszy = false;
}
else
switch( msg )
{
case WM_CREATE:
g_wmTBC = RegisterWindowMessage( "TaskbarButtonCreated" );
break;
}
}
Jestem winny „troszeczkę” wyjaśnień :P. Użyliśmy
ITaskbarList3, jest to potomek (klasa pochodna) klasy ITaskbarList. W wersji 3 doszedł pasek postępu i przyciski na miniaturce, dlatego od razu sobie go bierzemy :). Niestety jest on dostępny dopiero w Windows 7, więc jeśli chcesz możesz użyć
ITaskbarList, bo już pierwsza wersja umożliwia nam zabawę z kartami na pasku zadań :). Jest już dostępna czwarta wersja interfejsu, ale nic ciekawego dla nas nie wnosi, więc po co z niej korzystać :)?
Zmienna przechowująca wartość komunikatu jest zmienną statyczną, ponieważ nie będziemy z niej korzystać po za tą funkcją.
„No dobra, ale czemu użyłeś if-a, a nie skorzystałeś ze switch-a do obsługi komunikatu?!”. Odpowiedź jest prosta – etykiety we wnętrzu switcha muszą być wartościami stałymi, a zmienna (w której przechowujemy kod komunikatu), jak sama nazwa wskazuje – zmienia się :P.
Dzięki
CoCreateInstance uzyskujemy wskaźnik do interfejsu
ITaskbarList. Gdy nam się uda – funkcja zwraca
S_OK (ale nie taki do picia :D szkoda, co :P?). Gdyby jednak nam się nie udało – zmienną z wartością o systemie ustawiamy na
false. Ta zmienna oznacza czy wolno nam użyć tego interfejsu... gdyby się okazało, że nie udało się pozyskać interfejsu
ITaskbarList, np. przez starą wersję systemu to próba użycia tego interfejsu może skooczyd się tragicznie… :P. Wiem, że nazwa tej zmiennej nie jest trafna – była dla wcześniejszego przykładu :), zmień ją sobie jak chcesz, pozwalam Ci :P
No to to by było tyle na wstępie :P. W reszcie możemy się zająć za to co jest tematem tego artykułu.
Dodawanie i usuwanie kart
Doceniam Cię, że chciało Ci się czytać aż tyle. W końcu cierpliwość to cnota, nie :P? Teraz już bez dalszych przedłużeń poznajmy nasze funkcje (a konkretnie metody):
HRESULT AddTab( HWND hwnd );
HRESULT DeleteTab( HWND hwnd );
HRESULT ActivateTab( HWND hwnd );
Widzisz jakie proste? To nie przywidzenia, opłacało się czekać :D.
Funkcja zwraca
S_OK jeśli się powiedzie. Zapewne nie za bardzo wiesz co znaczy to „aktywuje kartę” – nie przejmuj się – ja też :D. Na MSDN jest napisane tylko tyle, że to wybrane okno (tylko jedno może być w danym czasie!) jest wyświetlane jako aktywne. Najprawdopodobniej chodzi o podgląd okna na żywo na miniaturce.
Użyjmy więc czym prędzej tego cudeńka na jakimś oknie dialogowym :)
if( OSVersion )
{
if( ptl->AddTab( hwnd ) == S_OK )
{
ptl->ActivateTab( hwnd );
ptl->DeleteTab( g_hwnd );
}
}
Trzeba pamiętać, aby za każdym razem sprawdzać czy korzystamy z odpowiedniego systemu, by przypadkiem nie spowodować jakiegoś błędu, który by zakończył naszą aplikację. Zobaczmy efekt naszej pracy:
Niestety – nie podoba nam się ikonka okna (domyślna windowsowa), a skoro jesteśmy rządnymi wiedzy i jakości, więc postanawiamy to poprawić. Sprawa jest prosta – wystarczy wysład do okna odpowiedni komunikat.
SendMessage( hwnd, WM_SETICON, ICON_SMALL,( LPARAM ) LoadIcon( GetModuleHandle( NULL ), MAKEINTRESOURCE( IDI_DIALOG_ICON ) ) );
SendMessage( hwnd, WM_SETICON, ICON_BIG,( LPARAM ) LoadIcon( GetModuleHandle( NULL ), MAKEINTRESOURCE( IDI_APP_ICON ) ) );
Argumentów się zapewne domyślisz :). Pierwszy komunikat ustawia ikonkę okna, drugi – ikonę aplikacji, czyli tą na pasku zadań. Gdybyśmy wysłali tylko pierwszy komunikat, a drugi już nie, to na pasku zadań ujrzelibyśmy ikonkę okna. Nasze okno po poprawie wygląda następująco:
Link do dokumentacji:
http://msdn.microsoft.com/en-us/library/windows/desktop/bb774652(v=vs.85).aspx
Pasek postępu (progress bar)
Tu sprawa jest stosunkowo prosta :). Ten pasek wygląda mniej więcej tak:
Tym razem mamy dwie, przyjemne metody klasy
ITaskbarList3. Obie zwracają S_OK w razie powodzenia. Pierwsza z nich to:
HRESULT SetProgressState( HWND hOkno, TBPFLAG stan );
hOkno to uchwyt okna, którego karta znajduje się na pasku zadao.
Oto możliwe stany paska:
Kolejna funkcja to:
HRESULT SetProgressValue( HWND hOkno, ULONGLONG aktualny_postep, ULONGLONG max_postep );
Jak widać kolejna funkcja z jasnymi argumentami :). Przykładowe wywołania:
if( OSVersion )
{
ptl->SetProgressState( hwnd, TBPF_NORMAL );
ptl->SetProgressValue( hwnd, 4, 7 );
}
Spowoduje ustawienie paska na kolor zielony (normalny) i 4/7 długości.
Uwagi
Jeśli pasek jest w stanie
TBPF_INDETERMINATE i wywołamy funkcję
SetProgressValue to pasek przejdzie do stanu
TBPF_NORMAL. Więcej uwag na MSDN.
Link do dokumentacji:
http://msdn.microsoft.com/en-us/library/windows/desktop/dd391692(v=vs.85).aspx
Przyciski na miniaturce okna
W Winindows 7 gdy najedziemy na ikonkę danej aplikacji znajdującej się na pasku zadań ukazuje nam się miniatura okna (podgląd). Winindows 7 wprowadza ciekawą funkcjonalność – przyciski na tej miniaturze. Możemy sterować aplikacją, która jest „zwinięta” na pasek! Jest to na pewno bardzo wygodne :). Oto jak coś takiego wygląda:
Do oprogramowania tego służą następujące funkcje:
HRESULT ThumbBarAddButtons( HWND hwnd, UINT cButtons, LPTHUMBBUTTON pButton );
HRESULT ThumbBarUpdateButtons( HWND hwnd, UINT cButtons, LPTHUMBBUTTON pButton );
Tu twórcy tego interfejsu trochę namieszali... zamiast zrobić jedną funkcję - są dwie i trzeba wiedzieć, którą kiedy wywołać. W przeciwnym razie nie uzyskamy przycisków :(. Za pierwszym razem wywołuje się
ThumbBarAddButtons, a za każdym następnym –
ThumbBarUpdateButtons. Ta pierwsza funkcja jest jakby inicjalizująca. Tutaj drobna uwaga – za każdym razem, gdy okno zostanie usunięte, a następnie dodane przy pomocy
AddTab oraz
DeleteTab trzeba od nowa je inicjalizować, tzn. wywołać
ThumbBarAddButtons. W przypadku powodzenia funkcje zwracają
S_OK. Znaczenie argumentów:
Oto struktura
THUMBBUTTON:
typedef struct THUMBBUTTON {
THUMBBUTTONMASK dwMask;
UINT iId;
UINT iBitmap;
HICON hIcon;
WCHAR szTip[ 260 ];
THUMBBUTTONFLAGS dwFlags;
} THUMBBUTTON, * LPTHUMBBUTTON;
Wartości, jakie może przyjąć maska:
Można oczywiście je łączyć operatorem alternatywy bitowej |. Określają one, które pola struktury wypełniliśmy. Znaczenia tych wartości chyba nie muszę objaśniać :).
Kod podsumowujący ten dział:
void przepisz( LPWSTR dokad, LPWSTR skad )
{
for(; * skad; skad++, dokad++ ) * dokad = * skad;
}
if( OSVersion )
{
THUMBBUTTON data;
ZeroMemory( & data, sizeof( THUMBBUTTON ) );
data.dwMask = THB_ICON | THB_TOOLTIP | THB_FLAGS;
data.dwFlags = THBF_ENABLED;
data.iId = ID_TBB;
przepisz( data.szTip, L"Nasz tooltip :D" );
data.hIcon = LoadIcon( GetModuleHandle( NULL ), MAKEINTRESOURCE( IDI_ICON ) );
ptl->ThumbBarAddButtons( hwnd, 1, & data );
}
Ten kod spowoduje utworzenie jednego przycisku na miniaturce. Nie ładujemy tu bitmapy z tego powodu, że wystarczy sama ikona. Nawet gdybyśmy załadowali bitmapę i ikonę, to zostanie użyta ikona :P. Co do tooltipa... musieliśmy użyć funkcji przepisującej z tego względu, że pole
szTip jest tablicą, a nie wskaźnikiem na rzekomą tablicę. Efekt naszej pracy:
Link do dokumentacji:
http://msdn.microsoft.com/en-us/library/windows/desktop/dd391692(v=vs.85).aspx
Tooltip i obszar podglądu okna na miniaturce
Na koniec dwie, prościutkie funkcje :). Czasem są nawet przydatne. Jedną określa się obszar okna, który ma być wyświetlany w podglądzie na miniaturce. Druga służy do ustawienia tekstu tooltipa (podpowiedzi). Jest on wyświetlany, gdy kursor myszy znajdzie się nad miniaturką okna. Wygląda to mniej więcej tak:
Jak można zauważyć tooltip jest wyświetlany nad miniaturką. Obszar okna wyświetlany na miniaturce został przycięty. Ot co możemy osiągnąć :). Deklaracja pierwszej metody interfejsu:
HRESULT SetThumbnailTooltip( HWND hwnd, LPCWSTR pszTip );
Nie ma tu nic trudnego :).
hwnd to uchwyt do okna, któremu ustawiamy tooltip.
pszTip to wskaźnik na łańcuch znaków rozszerzonych
(wchar_t*). Przyjrzyjmy się drugiej funkcji:
HRESULT SetThumbnailClip( HWND hwnd, RECT * prcClip );
Pierwszy argument jak zapewne wiesz – jest to uchwyt okna, nad którego miniaturką pracujemy :).
prcClip jest to natomiast wskaźnik na strukturę
RECT, która zawiera informacje (współrzędne) o obszarze, który ma być wyświetlany na miniaturce okna. Obie funkcje w przypadku powodzenia zwracają
S_OK. Przykładowe wywołania:
if( OSVersion )
{
ptl->SetThumbnailTooltip( hwnd, L"Podgląd" );
RECT rect;
GetClientRect( hwnd, & rect );
rect.top = 10;
rect.left += 5;
rect.right -= 5;
rect.bottom = 115;
ptl->SetThumbnailClip( hwnd, & rect );
}
Link do dokumentacji:
http://msdn.microsoft.com/en-us/library/windows/desktop/dd391692(v=vs.85).aspx