Czas poznać pierwszą, najpospolitszą kontolkę, którą będziemy najczęściej wykorzystywać, czyli przycisk(Button).

Inicjacja kontrolek w programie.

Jak już wspominałem w poprzedniej lekcji musimy dołączyć odpowiedni plik nagłówkowy i odpowiednią dla niego bibliotekę. Dołączamy plik o nazwie "commctrl.h", a w opcjach projektu w poleceniach dla linkera (jest pokazane w poprzedniej lekcji) piszemy:
-lcomctl32

Tworzenie kontrolki

Dopiero teraz możemy brać się za tworzenie przycisku. Schemat tworzenia kontrolki jest zawsze taki sam, w tym celu używamy funkcji CreateWindowEx. Być może pomyślisz teraz, że się pomyliłem, ale nie, do tworzenia kontrolek używamy tej samej funkcji, co do tworzenia okien, z tego prosty wniosek, wszystkie kontrolki to w rzeczywistości okna potomne. Tworzenie kontrolek może się odbyć w różnych miejscach kodu, możemy wywołać funkcję w głównej funkcji zaraz po utworzeniu okna, albo np. w komunikacie WM_CREATE, byle po stworzeniu okna rodzica(to na którym będą) dla kontrolek, ponieważ nie można zrobić kontrolki na oknie, którego jeszcze nie ma. Ja preferuję robienie kontrolek właśnie w WM_CREATE(komunikat wysyłany zaraz po utworzeniu okna), bo mamy pewność, że kontrolki zostaną stworzone zaraz po powstaniu okna. Teraz spójrzmy na przykład utworzenia przycisku na oknie:

#include <windows.h>
#include <commctrl.h>

HWND hButton,hWnd;
//globalne deklaracje uchytów na okna:
//hWnd - uchwyt na nasze okno rodzica
//hButton - uchwyt na przycisk

CHAR szClassName[]=
"OknoRodzica"
;
HINSTANCE* hInst;
//globalny wskaźnik na uchwyt naszego programu
//uchwytu programu często będziemy używać w różnych funkcjach, dlatego dobrze, mieć globalny wskaźnik na niego


LRESULT CALLBACK WndProc(HWND,UINT,WPARAM,LPARAM);

INT WINAPI WinMain(HINSTANCE hInstance,HINSTANCE,LPSTR lStart,INT nShow)
{
  hInst=&hInstance;
//pobieramy uchwyt programu do globalnego wskaźnika
  //tworzenie okna rodzica

  WNDCLASSEX wc;
  wc.hInstance=*hInst;
//używamy globalnego wskaźnika

  wc.lpszClassName=szClassName;
  wc.lpfnWndProc=WndProc;
  wc.style=
0
;
  wc.cbSize=sizeof(WNDCLASSEX);
  wc.hIcon=LoadIcon(
0
,IDI_APPLICATION);
  wc.hIconSm=LoadIcon(
0
,IDI_APPLICATION);
  wc.hCursor=LoadCursor(
0
,IDC_ARROW);
  wc.lpszMenuName=
0
;
  wc.cbClsExtra=
0
;
  wc.cbWndExtra=
0
;
  wc.hbrBackground=(HBRUSH)COLOR_BACKGROUND;
  if(!RegisterClassEx(&wc)) return
0
;
  hWnd=CreateWindowEx(
0
,szClassName,
"Tworzenie buttona"
,WS_OVERLAPPEDWINDOW,
20
,
20
,
600
,
300
,
0
,
0
,*hInst,
0
);
  ShowWindow(hWnd,nShow);
  MSG msgs;
  while(GetMessage(&msgs,
0
,
0
,
0
))
  {
    TranslateMessage(&msgs);
    DispatchMessage(&msgs);
  }
  return msgs.wParam;
}

LRESULT CALLBACK WndProc(HWND hwnd,UINT msg,WPARAM wPar,LPARAM lPar)
{
  switch(msg)
  {
    case WM_CREATE:
      
//funkcja tworząca kontrolkę

      hButton=CreateWindowEx(
0
,WC_BUTTON,
"Przycisk"
,WS_CHILD|WS_VISIBLE,
20
,
20
,
200
,
70
,hwnd,(HMENU)
1
,*hInst,
0
);
      break;
    case WM_DESTROY:
      DestroyWindow(hButton);
      PostQuitMessage(
0
);
      break;
    default:
      return DefWindowProc(hwnd,msg,wPar,lPar);
  }
  return
0
;
}

Uchwyty do okien i kontrolek lepiej jest deklarować globalnie, dzięki temu można się do nich odwoływać z każdego miejsca. Dodatkowo pożyteczną rzeczą jest możliwość globalnego użycia uchwytu naszej aplikacji, często w różnych miejscach będziemy jej używać. Ja w tym programie, stworzyłem globalny wskaźnik na HINSTANCE, któremu na początku programu zostaje przypisany adres naszej aplikcaji z parametru fucnkji main. Dzięki temu zabiegowi, w każdym miejscu programu mamy wartość uchwtu naszego programu.

Wszystko w funkcji WinMain powinniśmy już znać, nowością jest tutaj natomiast obsługa nowego komunikatu WM_CREATE. Jest on wysyłany podczas tworzenia okna, dzięki temu zaraz po utworzeniu okna rodzica, zostają tworzone kontrolki. Jak widać kontrolkę tworzymy znaną nam funkcją CreateWindowEx. Pierwszy parametr, czyli rozszerzony styl okna, z reguły powinniśmy zostawić, gdyż w kontrolkach się go raczej nie stosuje, jedynie czasem może się nam przydać WS_EX_CLIENTEDGE, ale nie wszystkie kontrolki ładnie wyglądają, gdy są zagłębione. Drugi parametr jak pamiętamy to nazwa klasy okna, używanie kontrolek polega właśnie na tym, że stosujemy gotowe klasy okien, czyli kontrolek, to właśnie podanie tutaj WC_BUTTON, powoduje, że okno wygląda jak przycisk. Trzecim parametrem jest nazwa okna, w przypadku przycisku, będzie to tekst na nim. Czwarty parametr to style okien(kontrolek). Tutaj jest jak w przydaku okien, podajemy WS_VISIBLE, żeby kontrolka była widoczna, albo możemy ją pokazać funckją ShowWindow, gdyż kontrolka to w rzeczywistości okno, więc możemy operować na niej jak na oknie. Drugą obowiązkową flagą w tym parametrze jest WS_CHILD, co poprostu oznacza, że kontrolka będzie potomna dla okna, tzn. będzie zawierała się w oknie rodzica. Kolejne 4 parametry to wymiary: dwa pierwsze to kolejno odległość x i y od lewego i górnego brzegu okna rodzica; kolejne dwa to szerokość i wysokość kontrolki. Dziewiąty parametr funkcji to okno rodzica, w przypadku okien, podawaliśmy tu 0, gdyż oknem potomnym był pulpit, jak się można domyślać, oknem rodzica dla kontrolki, będzie nasze stworzone wcześniej okno, dlatego podajemy tutaj uchwyt do naszego okna. Można użyć uchwytu przekazywanego z pierwszego parametru funkcji proceduralnej WndProc. Dziesiąty parametr w oknach to uchwyt do menu, w kontrolkach służy on do czegoś całkiem innego, a jest to identyfikator kontrolki w naszym programie, czyli poprostu numer naszej kontrolki, każda utworzona kontrolka na oknie powinna mieć swój numer, tutaj właśnie ustalamy ten numer, w przykładzie moja kontrolka na identyfikator 1. Dodatkowo, aby typy danych się zgadzały, rzutujemy numer kontrolki na typ HMENU. Przedostatni parametr to uchwyt do naszej aplikacji, jak już wcześniej wspomniałem, będzie on wymagany w wielu funkcjach WinAPI, dlatego dobrze mieć globalny wskaźnik na niego i w przykładzie właśnie używam obiektu na który wskazuje ten mój wskaźnik(jeżeli nie wiesz o czym mówię, przypomnij sobie lekcję C++ o wskaźnikach). Ostatni parametr funkcji CreateWindowEx to dodatkowe dane, tak jak w przypadku okien dajemy tutaj 0. Na koniec utworzone kontrolki podczas niszczenia okien także powinniśmy zniszczyć fucnkją DestroyWindow(jako parametr uchwyt do niszczonej kontrolki), funkcję powinniśmy umieścić w komunikacie WM_DESTROY, dzięki temu podczas niszczenia okna rodzica, zostaną zniszczone kontrolki.

Jak widzisz tworzenie i operowanie na kontrolkach jest podobne do tworzenia okien, gdyż znowu przypominam, kontrolki to tak jakby specjalne okna.

Naciśnięcie przycisku

Mamy już przycisk, ale co nam po nim, jak nie wiemy kiedy zostaje naciśnięty. Otóż już mówię jak się to sprawdza. Gdy naciśniemy przycisk do okna rodzica (czyli do naszego okna głównego) wysyłany jest komunikat WM_COMMAND, a w trzecim parametrze wPar, przekazywany jest numer(identyfikator, który wcześniej ustalilismy) naciśniętego przycisku. Dzięki temu można rozpoznać, który z przycisków został naciśnięty. Spójrzmy na przykład:

#include <windows.h>
#include <commctrl.h>

HWND hButton,hWnd;
CHAR szClassName[]=
"OknoRodzica"
;
HINSTANCE* hInst;

LRESULT CALLBACK WndProc(HWND,UINT,WPARAM,LPARAM);

INT WINAPI WinMain(HINSTANCE hInstance,HINSTANCE,LPSTR lStart,INT nShow)
{
  hInst=&hInstance;
  WNDCLASSEX wc;
  wc.hInstance=*hInst;
  wc.lpszClassName=szClassName;
  wc.lpfnWndProc=WndProc;
  wc.style=
0
;
  wc.cbSize=sizeof(WNDCLASSEX);
  wc.hIcon=LoadIcon(
0
,IDI_APPLICATION);
  wc.hIconSm=LoadIcon(
0
,IDI_APPLICATION);
  wc.hCursor=LoadCursor(
0
,IDC_ARROW);
  wc.lpszMenuName=
0
;
  wc.cbClsExtra=
0
;
  wc.cbWndExtra=
0
;
  wc.hbrBackground=(HBRUSH)COLOR_BACKGROUND;
  if(!RegisterClassEx(&wc)) return
0
;
  hWnd=CreateWindowEx(
0
,szClassName,
"Tworzenie buttona"
,WS_OVERLAPPEDWINDOW,
20
,
20
,
600
,
300
,
0
,
0
,*hInst,
0
);
  ShowWindow(hWnd,nShow);
  MSG msgs;
  while(GetMessage(&msgs,
0
,
0
,
0
))
  {
    TranslateMessage(&msgs);
    DispatchMessage(&msgs);
  }
  return msgs.wParam;
}

LRESULT CALLBACK WndProc(HWND hwnd,UINT msg,WPARAM wPar,LPARAM lPar)
{
  switch(msg)
  {
    case WM_CREATE:
      
//funkcja tworząca kontrolkę

      hButton=CreateWindowEx(
0
,WC_BUTTON,
"Przycisk"
,WS_CHILD|WS_VISIBLE,
20
,
20
,
200
,
70
,hwnd,(HMENU)
1
,*hInst,
0
);
      break;
    case WM_COMMAND:
      if(wPar==
1
) MessageBox(hwnd,
"Przycisk naciśnięty"
,
":>"
,MB_OK);
      
//jeżeli został naciśnięty przycisk o identyfikatorze numer 1 wyświetli się komunikat

      break;
    case WM_DESTROY:
      DestroyWindow(hButton);
      PostQuitMessage(
0
);
      break;
    default:
      return DefWindowProc(hwnd,msg,wPar,lPar);
  }
  return
0
;
}

No to by było na tyle, jeżeli chodzi o zwykły przycisk. Potrafimy już sobie taki zrobić i obsłużyć jego naciśnięcie.