Ucząc się programowania, zaczynamy tworzyć coraz to bardziej złożone programy, które posiadają coraz to więcej przycisków i innych dobrodziejstw interfejsu użytkownika. Jednak prędzej czy później okaże się, że nasze okno programu jest zbyt małe by pomieścić to wszystko. Oczywiście możemy powiększyć okno, ale nasz monitor też prędzej czy później się cały zapełni, i co wtedy? Na szczęście z pomocą przychodzą nam paski przesuwu, czyli ScrollBary:
ScrollBar jest kontrolką umożliwiającą przesuwanie w poziomie i w pionie. Oto jak jest zbudowany ScrollBar:
Tworzenie ScrollBara
Samo tworzenie wygląda tak samo, jak w przypadku innych kontrolek. Nazwa klasy to 
SCROLLBAR. Domyślnie tworzony jest pasek poziomy, natomiast do stworzenia paska pionowego trzeba użyć stylu 
SBS_VERT. Zaczniemy od stworzenia globalnego uchwytu:
HWND g_hScrollBar = NULL;
I teraz możemy już stworzyć ScrollBar:
g_hScrollBar = CreateWindowEx( 0, "SCROLLBAR", NULL, WS_CHILD | WS_VISIBLE, 50, 20, 220, 21, hwnd, NULL, hInstance, NULL );
Powyższy kod utworzy nam samodzielny pasek przesuwu. Możemy też dodać wbudowany ScrollBar do głównego okna naszego programu. Wystarczy przy tworzeniu okna dodać style 
WS_HSCROLL lub 
WS_VSCROLL w zależności od tego jaki pasek chcemy otrzymać.
Oczywiście jak to w WinAPI nie obędzie się bez problemów. Mianowicie jeśli skorzystamy z pierwszego sposobu, będziemy musieli potem ręcznie ustawić pasek przesuwu we właściwe miejsce, i z każdą zmianą rozmiaru okna na nowo go ustawiać. Oto jak to zrobić:
RECT okno;
GetClientRect( hwnd, & okno );
int iVThumb = GetSystemMetrics( SM_CYVTHUMB );
g_hScrollBar = CreateWindowEx( 0, "SCROLLBAR", NULL, WS_CHILD | WS_VISIBLE | SBS_VERT,
okno.right - 16, okno.top, 16, okno.bottom - iVThumb,
hwnd, NULL, hInstance, NULL );
Jeśli w naszym programie zezwalamy na zmianę rozmiaru okna, to musimy wrzucić powyższy kod do obsługi komunikatu 
WM_SIZE.
Inicjalizacja ScrollBara
Zanim zaczniemy korzystać ze ScrollBara, musimy w nim ustawić parę rzeczy, między innymi pozycję maxymalną i minimalną, itd. W tym celu posłużymy się funkcją 
SetScrollInfo, która jako jeden z argumentów przyjmuje strukturę 
SCROLLINFO z wieloma mniej lub bardziej przydatnymi polami. Oto jak mniej więcej jest ona zadeklarowana:
typedef struct tagSCROLLINFO {
    UINT cbSize;
    UINT fMask;
    int nMin;
    int nMax;
    UINT nPage;
    int nPos;
    int nTrackPos;
} SCROLLINFO, * LPCSCROLLINFO;
Oczywiście 
cbSize to rozmiar struktury, który jak zawsze w WinAPI trzeba podawać. Pole 
fMask oznacza które pola struktury zawierają poprawne informacje, czyli te które chcemy odczytać/zapisać. Teraz 
nMin, 
nMax i 
nPage - cóż to takiego? 
nMin to odległość początku przewijanego obszaru od góry (w przypadku całego okna 
0), 
nMax to wysokość całego okna (włącznie z niewidoczną częścią, do której trzeba będzie przewijać), natomiast 
nPage to wysokość obszaru, który jest widoczny (w przypadku całego okna to wysokość obszaru klienta). Oczywiście to są informacje dotyczące pionowego paska, dla poziomego będzie to długość, nie wysokość. Pole 
nPos to pozycja ScrollBara, natomiast pole 
nTrackPos jest tylko do odczytu, więc go nie ruszamy.
Skoro już wiemy (prawie) wszystko o tej strukturze, to możemy ją wypełnić:
RECT okno;
GetClientRect( hwnd, & okno );
SCROLLINFO si;
ZeroMemory( & si, sizeof( si ) );
si.cbSize = sizeof( SCROLLINFO );
si.fMask = SIF_RANGE | SIF_PAGE | SIF_POS;
si.nMin = 0;
si.nMax = 1000;
si.nPage = okno.bottom;
si.nPos = 0;
Na początku oczywiście zerujemy strukturę i wypełniamy pole 
cbSize rozmiarem struktury. Potem ustawiamy pola 
nMin, 
nMax i 
nPos. W naszym przykładzie zakładamy, że ScrollBar będzie przewijał cały obszar klienta w pionie. Pozycję początkową ustawiamy na 
0 czyli na górę ;-). Teraz przejdźmy do funkcji 
SetScrollInfo:
Składnia: SetScrollInfo (hwnd, fnBar, lpsi, fRedraw)
Tak więc dla ScrollBara jako samodzielnej kontrolki wywołamy funckję 
SetScrollInfo następująco:
SetScrollInfo( g_hScrollBar, SB_CTL, & si, TRUE );
A dla ScrollBara będącego częścią okna (styl 
WS_VSCROLL):
SetScrollInfo( hwnd, SB_VERT, & si, TRUE );
Przesuwanie
No właśnie, tyle się natrudziliśmy, a nasz ScrollBar wciąż nie działa! Nie dość, że nie przesuwa kontrolek w oknie, to w dodatku gdy go przesuniemy, sam wraca na stare miejsce! Dlaczego tak się dzieje? Otoż nie obsłużyliśmy odpowiednio komunikatów odpowiedzialnych za przesuwanie. Są tą 
WM_VSCROLL i 
WM_HSCROLL. W niższym słowie parametru 
wParam znajdziemy stałą oznaczającą to, co użytkownik zrobił ze ScrollBarem. Stałe te zostały zebrane w tabelce (Uwaga: dla 
ScrollBara pionowego):
Ponieważ nie wszyscy mogą kojarzyć o które części ScrollBara dokładnie chodzi, zamieszczam rysunek:
Na powyższym rysunku zaznaczone są też nazwy stałych dla ScrollBara poziomego, dlatego oddzielnej tabelki nie będzie. Dodam tylko że 
SB_LEFT i 
SB_RIGHT których nie ma na rysunku to odpowiednie końce poziomego paska przesuwu. Teraz twardszy orzech do zgryzienia - jak odczytać aktualną pozycję ScrollBara? Można ją znaleźć w wyższym słowie 
wParam, ale:
Łatwo można dojść do wniosku, że ten sposób się nie nadaje i to prawda ;-) Instnieje funkcja 
GetScrollPos do odczytywania pozycji, ale jako rzecze dokumentacja, nie wolno jej używać bo jest przestarzała. W zamian trzeba używać 
GetScrollInfo! Funkcja ta odczytuje dane do struktury 
SCROLLINFO, tej samej, którą używaliśmy przy inicjalizacji ScrollBara. Jest zbudowana tak samo jak 
SetScrollInfo, tylko że nie ma 4 argumentu. A oto jak odczytać pozycję suwaka:
SCROLLINFO si;
ZeroMemory( & si, sizeof( si ) );
si.cbSize = sizeof( SCROLLINFO );
si.fMask = SIF_POS;
SetScrollInfo( hwnd, SB_VERT, & si );
int pozycja = si.nPos;
Dobra, mamy już pozycję, teraz jak sprawić, by okno i suwak w rzeczywistości się przesunęły? Zaczniemy od suwaka, bo to prostsze ;-) Możemy się w tym celu posłużyć funkcją 
SetScrollPos która ustawi nam nową pozycję suwaka. Ale znowu MSDN surowo zabrania jej używania, a w zamian daje 
SetScrollInfo. Tą funkcję już znamy, korzystaliśmy już z niej wcześniej. Tym razem zmienimy tylko pozycję. No właśnie, na co zmienimy? To dobre pytanie. Wszystko zależy od tego co użytkownik zrobił ze ScrollBarem. Oto przykładowa obsługa przesunięcia ScrollBara:
case WM_VSCROLL: {
    
    SCROLLINFO si;
    ZeroMemory( & si, sizeof( si ) );
    si.cbSize = sizeof( SCROLLINFO );
    si.fMask = SIF_POS | SIF_PAGE | SIF_TRACKPOS;
    GetScrollInfo( hwnd, SB_VERT, & si );
    
    int pozycja = si.nPos;
    
    switch( LOWORD( wParam ) ) {
    case SB_TOP:
        pozycja = 0;
        break;
    case SB_BOTTOM:
        pozycja = 1000;
        break;
    case SB_LINEUP:
        if( pozycja > 0 ) {
            pozycja--;
        }
        break;
    case SB_LINEDOWN:
        if( pozycja < 1000 ) {
            pozycja++;
        }
        break;
    case SB_PAGEUP:
        pozycja -= si.nPage;
        if( pozycja < 0 ) {
            pozycja = 0;
        }
        break;
    case SB_PAGEDOWN:
        pozycja += si.nPage;
        if( pozycja > 1000 ) {
            pozycja = 1000;
        }
        break;
    case SB_THUMBPOSITION:
        pozycja = si.nTrackPos;
        break;
    case SB_THUMBTRACK:
        pozycja = si.nTrackPos;
        break;
    }
    
    ZeroMemory( & si, sizeof( si ) );
    si.cbSize = sizeof( SCROLLINFO );
    si.fMask = SIF_POS;
    si.nPos = pozycja;
    
    SetScrollInfo( hwnd, SB_VERT, & si, TRUE );
}
break;
Jak widać poza pozycją odczytaliśmy także wielkość strony i parametr 
nTrackPos którego na początku mieliśmy nie ruszać. Teraz jest on bardzo przydatny, gdyż właśnie w nim znajdziemy nowe wartości ScrollBara dla komunikatów 
SB_THUMBPOSITION i 
SB_THUMBTRACK. Obsługa pozostałych komunikatów jest chyba jasna.
Teraz zabierzmy się za przesuwania zawartości okna. Domyślasz się pewnie, że jest do tego jakaś funkcja np. 
ScrollWindow. Owszem, taka funkcja istnieje, lecz jako rzecze MSDN ta funkcja jest zła i trzeba używać 
ScrollWindowEx, która ma o wiele więcej parametrów ;-(. No cóż, takie jest życie. Wystarczy narzekania, przyjrzyjmy się bliżej tej funkcji:
Składnia: ScrollWindowEx(hwnd, dx, dy, prcScroll, prcClip, hrgnUpdate, prcUpdate, flags)
Teraz nadszedł czas zastanowić się co podać jako argumenty 
dx i 
dy by się przewijało tak jak Pan Bóg przykazał? Można to zrobić np. w ten sposób:
int dy = -( pozycja - si.nPos );
Jak pamiętamy ze wcześniejszych fragmentów kodu 
pozycja to nowa pozycja, natomiast 
si.nPos wciąż przechowuje starą pozycję. Dodatkowo musimy zamienić znaki w wyniku (operator 
-), inaczej ScrollBar będzie przewijał na odwrót ;-) Ten kod musimy umieścić przez kodem ustawiającym nową pozycję ScrollBara (a konkretniej, przed 
ZeroMemory) bo inaczej wartość 
si.nPos zostanie skasowana.
Teraz przejdźmy do 
ScrollWindowEx. Jako argument 
dx dajemy 
0, gdyż w naszym przykładzie mamy tylko pionowy pasek przesuwu. A oto kod:
ScrollWindowEx( hwnd, 0, dy,( CONST RECT * ) NULL,( CONST RECT * ) NULL,( HRGN ) NULL,( LPRECT ) NULL, SW_SCROLLCHILDREN );
UpdateWindow( hwnd );
Hura! Nasze kontrolki w oknie się przesuwają i zostawiają za sobą piękny ślad... No właśnie, coś tu jest nie tak, nie? W profesjonalnych aplikacjach nie ma takiego śladu! Na szczęście jego usunięcie nie jest zbyt kłopotliwe, wystarczy dorzucić flagi 
SW_INVALIDATE i 
SW_ERASE do ostatniego argumentu funkcji przewijającej. A oto kompletny kod obsługi komunikatu 
WM_VSCROLL:
case WM_VSCROLL: {
    
    SCROLLINFO si;
    ZeroMemory( & si, sizeof( si ) );
    si.cbSize = sizeof( SCROLLINFO );
    si.fMask = SIF_POS | SIF_PAGE | SIF_TRACKPOS;
    GetScrollInfo( hwnd, SB_VERT, & si );
    
    int pozycja = si.nPos;
    
    switch( LOWORD( wParam ) ) {
    case SB_TOP:
        pozycja = 0;
        break;
    case SB_BOTTOM:
        pozycja = 1000;
        break;
    case SB_LINEUP:
        if( pozycja > 0 ) {
            pozycja--;
        }
        break;
    case SB_LINEDOWN:
        if( pozycja < 1000 ) {
            pozycja++;
        }
        break;
    case SB_PAGEUP:
        pozycja -= si.nPage;
        if( pozycja < 0 ) {
            pozycja = 0;
        }
        break;
    case SB_PAGEDOWN:
        pozycja += si.nPage;
        if( pozycja > 1000 ) {
            pozycja = 1000;
        }
        break;
    case SB_THUMBPOSITION:
        pozycja = si.nTrackPos;
        break;
    case SB_THUMBTRACK:
        pozycja = si.nTrackPos;
        break;
    }
    
    int dy = -( pozycja - si.nPos );
    ScrollWindowEx( hwnd, 0, dy,( CONST RECT * ) NULL,( CONST RECT * ) NULL,( HRGN ) NULL,( LPRECT ) NULL, SW_SCROLLCHILDREN | SW_INVALIDATE | SW_ERASE );
    UpdateWindow( hwnd );
    
    ZeroMemory( & si, sizeof( si ) );
    si.cbSize = sizeof( SCROLLINFO );
    si.fMask = SIF_POS;
    si.nPos = pozycja;
    
    SetScrollInfo( hwnd, SB_VERT, & si, TRUE );
}
break;
W powyższym przykładzie został pokazany ScrollBar pionowy. Obsługa poziomego odbywa się analogicznie, z tym że w funkcji 
ScrollWindowEx uzupełniamy drugi argument, a nie trzeci jak w przykładzie powyżej. Komunikat do obsłużenia to 
WM_HSCROLL.
Podsumowanie
Na koniec tego artykułu podsumujemy nasze osiągnięcia ;-) Nauczyliśmy się jak utworzyć ScrollBar i ustawić jego parametry. Dowiedzieliśmy się także co zrobić, żeby naprawdę przewijał. Jak to w WinAPI, nie było to zbyt proste zadanie, ale daliśmy sobie z nim radę bez problemu. Tak więc jakieś podstawy ScrollBarów już znamy, choć jest jeszcze wiele rzeczy, które można by omówić, np. stworzenie poziomego ScrollBara, jednak informacje zawarte w tym artykule powinny ci wystarczyć, by stworzyć taki pasek samemu.