Czasem zdarza się, że nasz program ma do wykonania jakąś czasochłonną operację. Z zewnątrz objawia się to jego chwilowym "zawieszeniem się". Zwykle w takich sytuacjach user zaczyna się irytować i czasem może nawet zrezygnować z używania takiej aplikacji, a tego drugiego byśmy nie chcieli. Dość oczywistym rozwiązaniem problemu jest poinformowanie użytkownika o postępie pracy programu. Właśnie do takiego zadania została stworzona kontrolka ProgressBar. Pasek postępu jest na tyle czytelny, że powinien spacyfikować nawet takiego usera, który usilnie stara się nie czytać żadnych napisów w oknie programu:
Tworzenie ProgressBara
Tworzymy go tak, jak każdą inną kontrolkę. Małym utrudnieniem jest fakt, że jak wszystkie ciekawsze kontrolki, ProgressBar też należy do grupy
Common Controls (Jeśli nie wiesz co to, zajrzyj tu:
Toolbar, cz. 1. Musimy ją zainicjalizować z pomocą funkcji
InitCommonControlsEx:
INITCOMMONCONTROLSEX icc;
icc.dwSize = sizeof( INITCOMMONCONTROLSEX );
icc.dwICC = ICC_BAR_CLASSES;
InitCommonControlsEx( & icc );
Teraz można już brać się za tworzenie ProgressBara. Jako nazwę klasy wpiszemy stałą
PROGRESS_CLASS:
HWND hProgressBar = CreateWindowEx( 0, PROGRESS_CLASS, NULL, WS_CHILD | WS_VISIBLE,
10, 10, 200, 15, hwnd,( HMENU ) 200, hInstance, NULL );
Jeśli chcemy słuchać zaleceń Microsoftu, długość paska powinna wynosić pomiędzy 160 a 355 pikseli.
Tak utworzony ProgressBar wygląda beznadziejnie - jest podzielony na prostokątne segmenty, zamiast przesuwać się płynnie. Aby temu zaradzić można użyć dodatkowych stylów:
Wersja 6.0 biblioteki
CommCtrl.lib jest obecna w systemie Windows XP i nowszych, więc właściwie na wszystkich używanych systemach. Jeśli jednak program ma działać na starszych komputerach, nie należy używać tych stylów.
Ustawianie zakresu wartości
Zanim zaczniemy używać ProgressBar, powinniśmy ustawić zakres liczb na jakich będzie operował. Ustawiamy zarówno wartość maksymalną, przy której pasek będzie cały zamalowany, jak również wartość minimalną, przy której będzie pusty. Wynika z tego, że może on pracować w dowolnym zakresie liczb, nie tylko od zera.
Aby ustawić zakres pozycji paska, wysyłamy komunikat
PBM_SETRANGE. Żeby nie było zbyt prosto, ani intuicyjnie, zarówno minimalną jak i maksymalną wartość przekazujemy za pomocą parametru
lParam. Pomoże nam w tym makro
MAKELONG, które scala dwie liczby 16-bitowe do jednej 32-bitowej. Wynika z tego, że możemy przekazać liczby od 0 do 65535. Nie wolno nam użyć
wParam, więc zostawimy go sam na sam z zerem:
SendMessage( hProgressBar, PBM_SETRANGE, 0,( LPARAM ) MAKELONG( 0, 123 ) );
Funkcja zwraca
lParam w przypadku powodzenia lub 0 jeśli się nie uda.
Warto jeszcze wiedzieć, że gdybyśmy przypadkiem chcieli ustawiać wartości procentowo, nie musimy zmieniać zakresu, który domyślnie jest ustawiony od 0 do 100.
Zmiana pozycji ProgressBara
Pasek postępu, który nie pokazuje postępu nie jest zbyt przydatny. Aby ustawić nową pozycję możemy użyć
aż dwóch różnych komunikatów. Pierwszy z nich -
PBM_SETPOS - ustawia kontrolce całkiem nową wartość, którą przekazujemy w parametrze
wParam:
SendMessage( hProgressBar, PBM_SETPOS,( WPARAM ) 17, 0 );
Wartość zwracana to poprzednia pozycja ProgressBara.
Drugi komunikat -
PBM_DELTAPOS - dodaje do obecnej pozycji liczbę przekazaną w parametrze
wParam:
SendMessage( hProgressBar, PBM_DELTAPOS,( WPARAM ) 5, 0 );
Ten komunikat również zwraca poprzednią pozycję.
Przy zmianie pozycji ProgressBara należy zawsze pilnować, żeby nie wykroczyć poza zakres, ustawiony komunikatem
PBM_SETRANGE.
Praca krokowa
Jeśli wiemy, że nasz ProgressBar będzie za każdym razem przesuwał się o tą samą wielkość, możemy jeszcze trochę zautomatyzować ten proces, by się za bardzo nie zmęczyć. Na początku przyda się wysłać komunikat
PBM_SETSTEP, który ustala wielkość tą, o jaki za każdym razem będzie przyrastała pozycja paska postępu. Wielkość kroku przekazujemy w parametrze
wParam, mniej więcej w ten sposób:
SendMessage( hProgressBar, PBM_SETSTEP,( WPARAM ) 13, 0 );
Jeśli wpiszemy 0, domyślnie krok ustawi się na 10.
Teraz, aby powiększyć PrograssBar o tę wielkość wystarczy wysłać komunikat
PBM_SETSTEP:
SendMessage( hProgressBar, PBM_STEPIT, 0, 0 );
ProgressBar ze stylem PBS_MARQUEE
Styl
PBS_MARQUEE jest przeznaczony do sytuacji, kiedy nie wiemy ile pracy zastało do wykonania programowi. Jak to możliwe, żeby na pasku postępu pokazać, że nie znamy czasu do końca? Właśnie tak:
Taki ProgressBar ma wyłącznie informować, że program pracuje, a nie zawiesił się. Po uruchomieniu paska samoczynnie przesuwa się on jednostajnie w prawą stronę. My możemy jedynie zmienić prędkość tego ruchu.
Wszystko, co musimy zrobić, żeby nasz pasek postępu działał tak, jakbyśmy sobie życzyli, to zapoznanie się z komunikatem
PBM_SETMARQUEE. Parametr
lParam mówi kontrolce, czy ma być aktywna. Wpisujemy do niego
TRUE lub
FALSE. Jeśli wybraliśmy
true możemy w parametrze
lParam podać prędkość przesuwania się zielonego obszaru w milisekundach na klatkę. Najlepiej jednak nic nie ruszać, bo domyślna wartość (30) jest optymalna:
SendMessage( hProgressBar, PBM_SETMARQUEE, TRUE, 0 );
SendMessage( hProgressBar, PBM_SETMARQUEE, FALSE, 0 );
ProgressBar na pasku zadań
Skoro nauczyliśmy się tworzyć pasek postępu w oknie programu, możemy pójść jeszcze krok dalej i umiescić taki również na pasku zadań. Rozwiązanie takie ma wiele zalet. Po pierwsze widać go, kiedy okno programu jest zminimalizowane, po drugie można na nim pokazać też pauzę lub błąd programu, a po trzecie i najważniejsze pokazujemy w ten sposób jak bardzo jesteśmy
pro ;-)
Zanim jednak zdecydujemy, że warto umieszczać w programie taki bajer, powinniśmy wiedzieć, że działa on tylko w systemie Windows 7 lub nowszym, więc przed jego utworzeniem będziemy musieli programowo sprawdzić wersję systemu. Kolejnym utrudnieniem jest całkiem łatwy do przewidzenie fakt, że panowie z Micro$oftu zrobili wszystko, aby żadna z potrzebnych funkcji nawet nie przypominała tych, których używamy do tworzenia zwykłego ProgressBara. Są też inne "smaczki", ale dla chcącego nic trudnego, więc zamiast narzekać bierzmy się do roboty.
Sprawdzanie wersji systemu
Żeby sprawdzić wersję wersję Łindołsa posłużymy się strukturą OSVERSIONINFO. Jej składniki są następujące:
Windows 7 wcale nie ma numeru 7 tylko 6.1, więc będziemy musieli sprawdzić główny jak i poboczny numer wersji. Oczywiście musimy przed całą operacją standardowo wyzerować strukturę i określić jej rozmiar.
Dane pobierzemy za pomocą funkcji GetVersionEx, podając jej wskaźnik do naszej struktury:
OSVERSIONINFO osvi;
ZeroMemory( & osvi, sizeof( OSVERSIONINFO ) );
osvi.dwOSVersionInfoSize = sizeof( OSVERSIONINFO );
GetVersionEx( & osvi );
bool b7LubNowszy =(( osvi.dwMajorVersion[ h1 ] 6 ) &&( osvi.dwMinorVersion >= 1 ) ) ||( osvi.dwMajorVersion >= 7 );
Teraz pozostaje przed każdą operacją na pasku zadań sprawdzać, czy zmienna
b7LubNowszy ma wartość
true.
Oczekiwanie na utworzenie przycisku na pasku zadań
Nie jest tajemnicą, że programiści, którzy próbują modyfikować nieistniejące obiekty, nigdy nie żyją długo i szczęśliwie. Dlatego przed jakimikolwiek machinacjami z paskiem zadań trzeba się upewnić, że program utworzył już na nim przycisk.
Żeby tego dokonać należy odebrać odpowiedni komunikat. Doświadczenie z produktami Microsoftu podpowiada jednak, że to nie może być takie proste. I rzeczywiście, musimy bowiem sami zarejestrować ten komunikat. Używamy do tego funkcji
RegisterWindowMessage z parametrem "TaskbarButtonCreated". Wywołujemy ją podczas obsługi
WM_CREATE. Funkcja zwróci nam numer wiadomości, który musimy odebrać. Dopiero wtedy możemy wziąć się do roboty.
LRESULT CALLBACK WindowProcedure( HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam )
{
static DWORD g_wmTBC =( DWORD ) - 1;
if( message[ / h1 ] g_wmTBC )
{
} else
{
switch( message )
{
case WM_CREATE:
g_wmTBC = RegisterWindowMessage( "TaskbarButtonCreated" );
break;
}
}
Musieliśmy uciec się do użycia intrukcji
if, ponieważ
switch przyjmuje tylko stałe wartości.
Interfejs ITaskbarList3
Interfejs ten służy do najróżniejszych modyfikacji paska zadań. Pobieramy wskaźnik do niego za pomocą funkcji
CoCreateInstance. Jej wywołanie jest dosyć skomplikowane, więc przejdźmy od razu do przykładu, który najlepiej po prostu sobie skopiować:
#include <Shobjidl.h>
ITaskbarList3 * ptl;
if( CoCreateInstance( CLSID_TaskbarList, NULL, CLSCTX_ALL, IID_ITaskbarList3,( LPVOID * ) & ptl ) != S_OK )
MessageBox( 0, "I nici z bajerów.", 0, 0 );
Sprawdziliśmy od razu wartość zwracaną, która równa się
S_OK jeśli wszystko jest OK ;-)
Obsługa ProgressBara na pasku zadań
Po tym wszystkim powinniśmy się spodziewać czegoś strasznie skomplikowanego na deser. Czasem jednak nawet wrednych Panów z Microsoftu stać na odrobinę litości, dlatego musimy zapoznać się tylko z dwiema prostymi funkcjami.
Na początku powinniśmy ustawić styl wyświetlania naszego paska postępu, a mamy ich do wyboru całkiem sporo:
Styl ustawimy, wywołując funkcję składową
SetProgressState dla pobranego wcześniej wskaźnika:
ptl->SetProgressState( hOknoProgramu, TBPF_ERROR );
Ustawienie wartości paska jest równie łatwe i sprowadza się do użycia funkcji
SetProgressValue. Podajemy w niej zarówno aktualne położenie paska, jak i maksymalną wartość. Nie możemy za to ustawić minimalnej wartości, która zawsze wynosi 0. Ustawmy sobie pasek na 2/3 długości:
ptl->SetProgressValue( hOknoProgramu, 2, 3 );