Trzecią kontrolką jaką poznamy będzie ComboBox, bądź jak to niektóry śmiesznie ją nazywają "pole Kombi". Chodzi tu o pole, które po naciśnięciu rozwija listę wyboru(opcji).

Już chyba oczywiste, że na początku dołączamy pliki nagłówkowe. Po zrobieniu projektu okna rodzica, itd. przyszedł czas na kontrolkę Combo Box. Oczywiste jest chyba, że tworzymy ją przy pomocy CreateWindowEx. Spójrzmy na przykład:

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

HWND hCombo,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,
"Lekcja-ComboBox"
,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ę ComboBox

      hCombo=CreateWindowEx(
0
,WC_COMBOBOX,
0
,WS_CHILD|WS_VISIBLE,
10
,
10
,
570
,
100
,hwnd,
0
,*hInst,
0
);
      break;
    case WM_DESTROY:
      DestroyWindow(hCombo);
//zwalniamy kontrolke

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

Jeżeli chodzi o funkcje CreateWindowEx(), to praktycznie nie zmienia się znaczenie parametrów. Jedynie, w drugim parametrze(jako nazwę klasy), należy wpisać WC_COMBOBOX. Można dodać, że tytuł okna(trzeci parametr) w przypadku Combo Box'a jest ignorowany.

Stworzony w ten sposób wygląd kontrolki nie jest taki, do jakiego jesteśmy przyzwyczajeni, bo będzie się składała z dwóch części. Górnej w której wpisuje się tekst, oraz dolnej w której mamy możliwe opcje do wybrania. Mowa o czymś takim:

Jak już wspomniałem, nie jest to typowy wygląd Combo Box'a jak się powszechnie stosuje. Aby Combo Box wyglądał tak jak zwykle wygląda(rozwijalna lista), musimy dodać dodatkowy styl CBS_DROPDOWN:

...
hCombo=CreateWindowEx(
0
,WC_COMBOBOX,
0
,WS_CHILD|WS_VISIBLE|CBS_DROPDOWN,
10
,
10
,
570
,
100
,hwnd,
0
,*hInst,
0
);
...

Teraz ComboBox, będzie miał listę, która się będzie rozwijać. Do kontrolki nadal będzie można bezpośrednio wpisywać tekst.

Jest jeszcze jeden styl, który blokuje, możliwość wpisywania własnego tekstu, można jedynie wybierać odpowiednie opcje. Styl ten to CBS_DROPDOWNLIST:

...
hCombo=CreateWindowEx(
0
,WC_COMBOBOX,
0
,WS_CHILD|WS_VISIBLE|CBS_DROPDOWNLIST,
10
,
10
,
570
,
100
,hwnd,
0
,*hInst,
0
);
...

Umieszczanie danych

Mamy już gotową kontrolkę, teraz przydało by sie dodać do niej jakieś możliwości wyboru. Robimy to wysyłając komunikat CB_ADDSTRING do Combo Box'a. Spójrzmy na przykład:

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

HWND hCombo,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,
"Lekcja-ComboBox"
,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:
      hCombo=CreateWindowEx(
0
,WC_COMBOBOX,
0
,WS_CHILD|WS_VISIBLE|CBS_DROPDOWNLIST,
10
,
10
,
570
,
100
,hwnd,
0
,*hInst,
0
);
      
//dodawanie opcji wyboru

      SendMessage(hCombo,CB_ADDSTRING,
0
,(LPARAM)
"Opcja 1"
);
      SendMessage(hCombo,CB_ADDSTRING,
0
,(LPARAM)
"Opcja druga"
);
      SendMessage(hCombo,CB_ADDSTRING,
0
,(LPARAM)
"Opcja III"
);
      break;
    case WM_DESTROY:
      DestroyWindow(hCombo);
      PostQuitMessage(
0
);
      break;
    default:
      return DefWindowProc(hwnd,msg,wPar,lPar);
  }
  return
0
;
}

Teraz nasze okno będzie wyglądać tak:

Funkcji SendMessage() chyba nie muszę przedstawiać, jedynie powiem, że trzeci parametr ignorujemy, natomiast czwarty to wskaźnik na tablicę znaków, oczywście rzutowany na LPARAM.

Obsługa kontrolki

Aby ustawić daną opcję jako aktywną(aktualnie wybraną), należy do kontrolki wysłać komunikat CB_SETCURSEL:

...
SendMessage(hCombo,CB_SETCURSEL,(WPARAM)
0
,
0
);
...

Tutaj jako trzeci parametr podajemy numer opcji wyboru, liczony od 0 w górę, który stanie się aktywny(wybrany).

Aby sprawdzić jaka opcja jest aktualnie wybrana, należy wysłać do kontrolki komunikat CB_GETCURSEL:

...
INT index=SendMessage(hCombo,CB_GETCURSEL,
0
,
0
);
...

Funkcja zwróci numer aktualnie wybranej opcji, przypominam, że numery liczone są od 0!

Czcionka

Tutaj tak samo jak z Editem, wysyłamy komunikat WM_SETFONT.