Panel użytkownika
Nazwa użytkownika:
Hasło:
Nie masz jeszcze konta?
Autor: 'Złośliwiec'
Biblioteki C++

Toolbar, cz. 2

[lekcja]

Płaskie toolbary


Pewnie nie jesteś zachwycony dotychczasowymi efektami? Toolbary w "profesjonalnych" aplikacjach wyglądały jakoś inaczej, no nie? Zgadza się, w dzisiejszych czasach modne są płaskie przyciski na toolbarach, trójwymiarowe nie są już trendi ;-P. Zrobienie "płaskiego" toolbaru jest wrecz banalne, wystarczy dodać do stylów toolbaru stałą TBSTYLE_FLAT. Wypuszcza ona powietrze z tych paskudnych, nadętych przycisków i już wyglądają one całkiem przyzwoicie:

Płaski jak decha - i tak ma być (Windows 98)
Płaski jak decha - i tak ma być (Windows 98)

Niestety, w Dev-ie jest drobna przeszkoda, która może nieco utrudnić ustawienie wspomnianego stylu. Mianowicie plik nagłówkowy commctrl.h z tego pakietu jest kompletnie skopany i praktycznie nie da się go używać bez wprowadzenia do niego kilku własnoręcznych modyfikacji. Nie ma się co łamać, można dodać na poczatku programu jakieś zaklęcie w rodzaju:

C/C++
#ifndef TBSTYLE_FLAT
#define TBSTYLE_FLAT 2048
#endif

Ponieważ jednak nie jest to jedyna stała, której brakuje lub która jest źle zdefiniowana, musimy przerobić cały nagłówek. Nie martw się, nie będziesz musiał się tym na razie męczyć - w dziale Download znajdziesz spakowany nagłówek w wersji poprawionej przeze mnie. Nadal nie jest on do końca sprawny, ale przynajmniej wszystkie rzeczy opisane w tym kursie będziesz mógł skompilować bez problemów.
 
Wracając do toolbaru... Istnieje możliwość utworzenia przezroczystego toolbaru, tj. samych przycisków, bez tła. Tym razem ilustracji nie będzie, gdyż na szarym okienku taki toolbar wygląda dokładnie tak samo, jak zwykły, ale gdybyś kiedyś robił program z pięknym bitmapowym tłem, może ci się ten przezroczysty toolbarek przydać ;-). Wystarczy zamiast TBSTYLE_FLAT dać TBSTYLE_TRANSPARENT.

Ramka


Toolbar dość paskudnie wygląda, jeśli pozbawiony jest ramki. Możesz ją dodać, ustawiając mu styl WS_BORDER. Powyższy screen przedstawia właśnie toolbar z ramką.

Ikony + tekst


Przedstawiony powyżej toolbar wyświetla same ikony, ale w większości współczesnych aplikacji (w tym Dev-C++) możesz się spotkać również z przyciskami składającymi się i z tekstu, i z ikony. Zmajstrowanie takiego cuda nie jest szczególnie trudne. Wystarczy dodać odpowiednie napisy do wewnętrznej listy stringów toolbaru, wysyłając komunikat TB_ADDSTRING:

C/C++
SendMessage( hToolbar, TB_ADDSTRING, 0,( LPARAM ) "Nowy\0Otwórz\0Zapisz\0-\0Koniec\0" );

Jak widać, poszczególne napisy muszą być rozdzielone znakami zerowymi, a na ich końcu powinien znajdować się podwójny znak zerowy (tutaj widać tylko jeden, zgadnij dlaczego ;-)). Napisy te przekazujemy w parametrze lParam. Można również wykorzystać napisy zawarte w pliku zasobów, wtedy parametr wParam określa uchwyt programu, a lParam - identyfikator napisu w pliku. Teraz musimy jeszcze oczywiście poprzydzielać indeksy napisów na świeżo utworzonej liście odpowiednim przyciskom (czyli wypełnić pole iString), co jednak w naszym przykładzie już zapobiegawczo zrobiliśmy na samym początku ;-).

Ikonki z tekstem (Windows 98)
Ikonki z tekstem (Windows 98)

Istnieje możliwość przypisywania pojedynczych stringów do przycisku bez konieczności dodawania ich do wewnętrznej listy. Wystarczy zrobić takie małe "oszustwo" i podać adres bufora ze stringiem, zamiast indeksu do iString, np:

C/C++
LPSTR buf =( LPSTR ) GlobalAlloc( GMEM_FIXED, 100 );
LoadString( hThisInstance, IDS_NAPIS, buf, 100 );
tbb[ 0 ].iString =( int ) buf;

Ta metoda oczywiście pozwala tylko na dodawanie nowych przycisków. Co zrobić, jeśli chcemy zmienić napis już istniejącego przycisku? Otóż nowsze wersje biblioteki dają nam w prezencie przydatny komunikat TB_SETBUTTONINFO. Pozwala on zmienić atrybuty przycisku w dowolnym momencie. Niestety, korzysta on z nieco innej struktury, niż dotychczas omawiana TBBUTTON. Zmiana etykiety przycisku przy pomocy tego komunikatu będzie wyglądała tak:

C/C++
TBBUTTONINFO tbbi;
ZeroMemory( & tbbi, sizeof( tbbi ) );

tbbi.cbSize = sizeof( tbbi );
tbbi.dwMask = TBIF_TEXT;
tbbi.pszText = "To już jest koniec";

SendMessage( hToolbar, TB_SETBUTTONINFO, TOOL_KONIEC,( LPARAM ) & tbbi );

W sumie nie ma tutaj co omawiać; przerabialiśmy podobne rzeczy już tysiące razy. Pole dwMask określa, których z pozostałych pól struktury używamy. Potrzebujemy tylko ustawić pole pszText, więc do dwMask wpisujemy stałą TBIF_TEXT. W samym komunikacie TB_SETBUTTONINFO przekazujemy wskaźnik do struktury jako lParam, oraz identyfikator zmienianego przycisku jako wParam. My zmieniamy przycisk TOOL_KONIEC. Oczywiście tak długi tekst, jak tutaj podaliśmy, nie zmieści się na przycisku, więc będziemy musieli go jeszcze rozszerzyć (ustawiając dodatkowo pole cx struktury TBBUTTONINFO), ale to już twoja praca domowa  (albo zajrzyj na koniec tej strony ;-) ).
 
Możesz również sprawić, żeby tekst wyświetlany był nie pod spodem, ale obok ikony. Odpowiada za to styl TBSTYLE_LIST.

Tekst obok obrazka (Windows 98)
Tekst obok obrazka (Windows 98)

Przycisk + rozwijalna lista


Wiele programów (np. Internet Explorer) ma takie fajne przyciski ze strzałeczką obok. Jeśli ją nacisnąć, pojawia się dodatkowe menu. My też możemy sobie taki przycisk zrobić, a co. Niech będzie to przycisk Importuj (jeśli nie wiesz o co chodzi - zajrzyj do odcinka o menu). Będzie on rozwijał to samo podmenu, które pojawia się po najechaniu na pozycję menu "Importuj".
 
Przede wszystkim potrzebny nam przycisk ze stylem TBSTYLE_DROPDOWN (lub BTNS_DROPDOWN, dla wyższych wersji biblioteki). Tak więc wypełnianie odpowiedniego pola struktury TBBUTTON (całości nie podaję, bo już to przerabialiśmy ;-)) powinno wyglądać mniej więcej w ten sposób:

C/C++
tbb[ 1 ].fsStyle = TBSTYLE_BUTTON | TBSTYLE_DROPDOWN;

Samo to szczęścia nam nie da, bo trzeba jeszcze wyprodukować strzałkę. W tym celu musimy ustawić rozszerzony styl TBSTYLE_EX_DRAWDDARROWS. Niestety, nie jest to takie proste jak w przypadku "zwykłych" stylów - robi się to przez oddzielny komunikat, TB_SETEXTENDEDSTYLE:

C/C++
SendMessage( hToolbar, TB_SETEXTENDEDSTYLE, 0, TBSTYLE_EX_DRAWDDARROWS );

Dzięki temu eksperymentowi powinniśmy już otrzymać gotowy przycisk ze strzałką. Teraz trzeba tak pokombinować, żeby po kliknięciu na strzałkę pojawiało się menu. Tutaj czeka nas dłuższa gimnastyka. Zakładamy, że główne menu programu już mamy gotowe (ze wspomnianego odcinka kursu...), więc możemy skorzystać z funkcji GetSubMenu, aby pobrać uchwyt do tego fragmentu menu, który nas interesuje, a następnie wyświetlić je za pomocą TrackPopupMenu.
 
Jak wykryć, że naciśnięto strzałkę obok przycisku, a nie sam przycisk? Otóż wysyłane jest wówczas powiadomienie TBN_DROPDOWN. '''Powiadomienie''', a nie komunikat! I tutaj przydałaby się mała dygresja na temat powiadomień. Każde powiadomienie wysyłane jest jako komunikat WM_NOTIFY. Żeby rozróżnić, jakie konkretnie jest to powiadomienie (jest ich wiele rodzajów, podobnie jak ze "zwykłymi" komunikatami) i od jakiej kontrolki pochodzi, musimy się bliżej zainteresować parametrem lParam komunikatu WM_NOTIFY. Jest w nim przekazywany wskaźnik do struktury, która zawiera wszystkie potrzebne nam informacje. Typ struktury zależy od rodzaju powiadomienia, np. w przypadku toolbaru będzie to struktura NMTOOLBAR. Generalnie obsługa powiadomień może wyglądać mniej więcej tak:

C/C++
case WM_NOTIFY:
{
   
LPNMHDR lpn =( LPNMHDR ) lParam;
   
LPNMTOOLBAR lpnTB =( LPNMTOOLBAR ) lParam;
   
   
switch( lpn->code )
   
{
   
case TBN_DROPDOWN:
       
{
           
return FALSE;
       
}
       
// obsługa innych powiadomień
        // ...
       
default: break;
   
}
}
break;

Typ LPNMTOOLBAR to wskaźnik na strukturę typu NMTOOLBAR. Jedno z pól tej struktury (a konkretnie jej pierwsze pole) jest typu NMHDR (skrót od ''Notification Message Header''). Pełni ona rolę nagłówka powiadomienia. Pole code tej struktury zawiera kod powiadomienia i w naszym przypadku powinno być równe TBN_DROPDOWN. Na razie nie będziemy obsługiwali innych powiadomień. Moglibyśmy jeszcze wykorzystać pola hwndFrom i idFrom struktury NMHDR, żeby sprawdzić, czy powiadomienie faktycznie pochodzi od toolbaru, ale odpuśćmy sobie takie detale ;-).
 
Pewnie się zastanawiasz, dlaczego jeden parametr lParam przekonwertowaliśmy na dwa rodzaje struktur i czy to przypadkiem nie pomyłka. Bynajmniej. Wskaźnik zawarty w  lParam wskazuje na strukturę typu NMTOOLBAR, ale na samym poczatku tej struktury znajduje się (jak już wspomnieliśmy) nagłówek typu NMHDR, więc możemy wskaźnik ten przerobić na LPNMHDR. Robimy to dla czystej wygody, dzięki temu piszemy po prostu lpn->code zamiast lpnTB->hdr.code, co przy pisaniu bardziej skomplikowanej obsługi powiadomieć może się okazać błogosławionym rozwiązaniem ;-).
 
Zajmijmy się teraz wyświetlaniem naszego menu. Najpierw pobieramy i zapamiętujemy w jakiejś zmiennej uchwyt do podmenu Importuj (pamiętając o tym, żeby sobie takie menu utworzyć wcześniej w pliku zasobów):

C/C++
HMENU hPopupMenu;
hPopupMenu = GetSubMenu( hMenu, 0 );

Mamy już odpowiedni uchwyt, pozostaje się tylko zastanowić, w którym miejscu wyświetlić. Najlepiej byłoby tuż pod przyciskiem. Tak więc musimy pobrać współrzędne tegoż przycisku:

C/C++
RECT rc;
SendMessage( lpn->hwndFrom, TB_GETRECT,( WPARAM ) lpnTB->iItem,( LPARAM ) & rc );

Otrzymane współrzędne są względne - punkt (0,0) to lewy górny róg obszaru klienta. Musimy je zatem przekonwertować na współrzędne ekranowe. Robiliśmy to już we wcześniejszych odcinkach kursu z punktami, ale teraz mamy cały prostokąt, więc najprościej będzie użyć funkcji MapWindowPoints (która potrafi konwertować całą tablicę punktów na raz, a przecież prostokąt to właśnie tablica 2 punktów). Nie będziemy jej dokładniej omawiać, zobaczymy tylko gotowy przykład:

C/C++
MapWindowPoints( lpn->hwndFrom, HWND_DESKTOP,( LPPOINT ) & rc, 2 );

Teraz tylko wyświetlić menu. Możemy to zrobić funkcją TrackPopupMenu albo TrackPopupMenuEx. Ponieważ zaś lubimy wyzwania... No dobra, nie bij, ta druga funkcja ma pewną zaletę - możemy jej podać obszar ekranu, którego menu nie powinno przysłonić. Jak się pewnie domyślasz, obszarem tym będzie nasz przycisk. W dodatku TrackPopupMenuEx przyjmuje mniej argumentów (!).

C/C++
TPMPARAMS tpm;

tpm.cbSize = sizeof( TPMPARAMS );
tpm.rcExclude = rc;

TrackPopupMenuEx( hPopupMenu, TPM_LEFTALIGN | TPM_LEFTBUTTON | TPM_VERTICAL,
rc.left, rc.bottom, hwnd, & tpm );

Jak widzimy, funkcja korzysta z nowej struktury (hurrra :-]) - TPMPARAMS, określającej rozszerzone parametry. Właśnie do tej struktury wpisujemy współrzędne naszego prostokąta. Oprócz tego wyświetlamy menu w ten sposób, że jego lewy górny róg będzie się znajdował tuż pod przyciskiem. W porównaniu do TrackPopupMenu, w "wersji rozszerzonej" możemy jeszcze użyć (i używamy ;-) ) dodatkowej flagi - TBM_VERTICAL, która podpowiada systemowi, co zrobić w sytuacji, gdyby zabrakło miejsca na ekranie do wyświetlenia menu.

Przycisk ze strzałką (Windows 98)
Przycisk ze strzałką (Windows 98)

Hot-tracking


Cóż to znowu za czort? Ano, ''hot-tracking'' oznacza podświetlanie (lub oznaczanie w innych sposób) kontrolek, na które najedziemy myszą. Co prawda w naszym toolbarze mamy to już zaimplementowane automatycznie (odkąd go spłaszczyliśmy ;-) ), ale teraz dowiemy się jeszcze, co zrobić by dodatkowo po najechaniu zmieniała się bitmapa przycisku.
 
Toolbar może miec przypisane trzy listy obrazków. Jedna z nich odpowiada za domyślny wygląd ikon, druga - za wygląd ikon, na które najechano strzałką, trzecia - za wygląd nieaktywnych (wyłączonych) przycisków. Do przypisywania toolbarowi tych list służą specjalne komunikaty, odpowiednio: TB_SETIMAGELIST, TB_SETHOTIMAGELIST, TB_SETDISABLEDIMAGELIST. W parametrze lParam tych komunikatów powinniśmy przekazać uchwyt do listy obrazków.
 
Zanim zastanowimy się, jak uzyskać ten uchwyt, musimy dokonać drobnego spostrzeżenia: toolbar może mieć aż trzy listy obrazków na raz, ale każdy przycisk może mieć przypisany tylko jeden indeks bitmapy w danym momencie. Oznacza to, że nie możemy robić hot-trackingu wybiórczo - albo wszystkie przyciski na toolbarze go obsługują, albo żaden. Jeśli zdecydujemy się na to pierwsze, to musimy zrobić po dwie (lub trzy) bitmapy dla każdego przycisku, tak aby ikony odpowiednich przycisków znajdowały się w obu (lub trzech) bitmapach w TEJ SAMEJ KOLEJNOŚCI, a następnie dodać te bitmapy do dwóch (trzech) list i przypisać listy do toolbaru.
 
Jak tworzymy listę obrazków? Mamy do tego funkcję ImageList_Create. Bliższego omawiania nie będzie; zbyt leniwy jestem na to ;-). Pierwsze dwa argumenty to wymiary pojedynczej ikony, a trzeci oznacza głębokość koloru (wspólna dla całej listy), np. ILC_COLOR8, ILC_COLOR24. Połączony z flagą ILC_MASK oznacza, że dla każdego obrazka zostanie stworzona mapa bitowa przeźroczystości obrazka. Pozostałe argumenty nie mają dla nas większego znaczenia. Zwracana wartość jest typu HIMAGELIST, więc powinniśmy sobie wcześniej utworzyć zmienną tego typu:

C/C++
HIMAGELIST himlDef, himlHot;
himlDef = ImageList_Create( 16, 16, ILC_COLOR24 | ILC_MASK, 0, 1 );
himlHot = ImageList_Create( 16, 16, ILC_COLOR24 | ILC_MASK, 0, 1 );

Wczytywać bitmapy już dawno umiemy (prawda?), więc nie będziemy się powtarzać; zakładamy, że mamy już gotowe uchwyty do bitmap hbmDef i hbmHot, które możemy teraz dodać do odpowiednich list. Bitmapa o uchwycie hbmDef powinna zawierać czarno-białe obrazki, hbmHot - w kolorze. Do dodawania służy funkcja ImageList_Add, ale jeszcze lepiej jest użyć ImageList_AddMasked, która przy okazji wykona za nas "usuwanie" niepotrzebnego tła z bitmapy:

C/C++
ImageList_AddMasked( himlDef, hbmDef, RGB( 192, 192, 192 ) );
ImageList_AddMasked( himlHot, hbmHot, RGB( 192, 192, 192 ) );

Teraz możemy za pomocą wspomnianych już wyżej komunikatów przypisać stworzone i wypełnione przed chwilą listy obrazków do odpowiednich toolbarów:

C/C++
SendMessage( hToolbar, TB_SETIMAGELIST, 0,( LPARAM ) himlDef );
SendMessage( hToolbar, TB_SETHOTIMAGELIST, 0,( LPARAM ) himlHot );

Zakładając, że już wcześniej ponadawaliśmy przyciskom odpowiednie indeksy, otrzymujemy toolbar z czarno-białymi przyciskami, które dopiero po "podświetleniu" stają się kolorowe:

Więc chodź, pomaluj mi świaaat... (Windows 98)
Więc chodź, pomaluj mi świaaat... (Windows 98)

Nie zapomnij o zwolnieniu zasobów. Listy obrazków niszczymy za pomocą ImageList_Destroy, podając jako argument uchwyt do niszczonej listy. Bitmapy, przypominam, usuwamy za pomocą DeleteObject.

Tooltipy

Podpowiedzi, wskazówki, z angielska ''tooltips'', to te śmieszne etykietki, zwykle na żółtym tle, które pojawiają się po najechaniu kursorem myszy na jakiś istotny element interfejsu i zatrzymaniu go przez jakiś czas (zwykle około sekundy). Są one przydatne w przypadku każdej kontrolki, ale największe chyba znaczenie mają właśnie przy toolbarach, których przyciski często nie posiadają widocznych podpisów, a nie zawsze przecież ikonka potrafi nam zasugerować dokładne znaczenie danego przycisku ;-).
Zanim zaczniesz się biedzić nad tooltipami, ponadawaj przyciskom unikalne identyfikatory (idCommand), w przeciwnym razie tooltipy będą błędnie wyświetlane.
 
Najprostszym sposobem na ustawienie tooltipów jest... ustawienie tekstu na przycisku, co już zrobiliśmy. Jeśli ustawimy tekst, ale jednocześnie go ukryjemy, to będzie on wyświetlany jako tooltip. Tylko jak to zrobić? Jedna z metod (chyba najprostsza) to ustawienie toolbarowi stylów  TBSTYLE_TOOLTIP i TBSTYLE_LIST (koniecznie obydwa!). Ponadto ustawiamy jeszcze rozszerzony styl TBSTYLE_EX_MIXEDBUTTONS (ten ostatni oczywiście przez wysłanie odpowiedniego komunikatu - patrz wyżej).
 
Jeśli toolbar ma ustawiony styl TBSTYLE_EX_MIXEDBUTTONS, to tekst na przyciskach nie jest wyświetlany jako etykieta (chyba, że dany przycisk ma ustawiony styl BTNS_SHOWTEXT), a najwyżej jako tooltip:

To jest właśnie tooltip (Windows 98)
To jest właśnie tooltip (Windows 98)

A co zrobić, żeby wyświetlić w tooltipie inny tekst, niż etykieta przycisku? To już wymaga nieco więcej zachodu. Trzeba odpowiedzieć na powiadomienie TTN_GETDISPINFO, które wysyłane jest przez toolbar do okna rodzicielskiego. W parametrze lParam dostajemy wtedy wskaźnik do struktury typu TOOLTIPTEXT, która zawiera nagłówek (typu NMHDR, oczywiście), pozwalający nam zorientować się, którego dokładnie przycisku dotyczy dane powiadomienie. W strukturze tej występuje również pole lpszText, do którego musimy wpisać tekst tooltipa. Oto przykład odpowiedzi na to powiadomienie, w której to odpowiedzi ustawiamy tekst dwóch z naszych przycisków:

C/C++
case TTN_GETDISPINFO:
{
   
LPTOOLTIPTEXT lpttt =( LPTOOLTIPTEXT ) lParam;
   
switch( lpttt->hdr.idFrom )
   
{
   
case TOOL_NOWY:
       
lpttt->lpszText = "Tworzy nowy plik";
       
break;
       
   
case TOOL_OTWORZ:
       
lpttt->lpszText = "Otwiera istniejący plik";
       
break;
   
}
}
break;

Dzięki tej pisaninie możemy wyświetlać w tooltipach co chcemy, a etykiety zostawić w spokoju. Wtedy możliwe jest ustawienie krótkich etykiet typu Nowy, Zapisz itp., oraz nieco dłuższych tooltipów, dokładniej objaśniających funkcję danego przycisku:

Dłuuugi tooltip (Windows 98)
Dłuuugi tooltip (Windows 98)

Kontrolki na toolbarze


Na koniec dowiemy się, jak wstawić do toolbaru inną kontrolkę. Mogłeś się z tym spotkać np. w edytorach tekstu, gdzie na toolbarach jest zwykle ComboBox z wyborem czcionek; zaraz zajmiemy się wstawieniem takiego właśnie ComboBoxa (ale bez czcionek ;-)).
 
Najpierw trzeba na toolbarze zrobić trochę miejsca. Możemy wstawić do niego nowy separator i rozszerzyć go do tylu pikseli, ile ma mieć nasz ComboBox. Separator ten nie będzie i tak widoczny zza ComboBoxa. Zdefiniujemy też sobie identyfikator dla tego separatora, a także dla ComboBoxa. No to do dzieła:

C/C++
#define TOOL_PLACEHOLDER 7
#define IDC_COMBOBOX     501

HWND g_hCombo;

int szerokosc = 100;
TBBUTTON tbb3[ 1 ];

ZeroMemory( tbb3, sizeof( tbb3 ) );
tbb3[ 0 ].idCommand = TOOL_PLACEHOLDER;
tbb3[ 0 ].fsState = TBSTATE_ENABLED;
tbb3[ 0 ].fsStyle = TBSTYLE_SEP;

SendMessage( hToolbar, TB_ADDBUTTONS, 1,( LPARAM ) & tbb3 );

"Separator" już jest, ale ma za małe wymiary - kontrolka nam się nie zmieści. Trzeba rozszerzyć. Wykorzystamy omówioną niedawno strukturę TBBUTTONINFO, by dzieła tego dokonać:

C/C++
TBBUTTONINFO tbbi;
ZeroMemory( & tbbi, sizeof( tbbi ) );

tbbi.cbSize = sizeof( tbbi );
tbbi.dwMask = TBIF_SIZE;
tbbi.cx = szerokosc;

SendMessage( hToolbar, TB_SETBUTTONINFO, TOOL_PLACEHOLDER,( LPARAM ) & tbbi );

Potrzebujemy teraz wymiarów rozciągniętego właśnie "separatora". Pobierzemy je znanym już komunikatem TB_GETITEMRECT i wykorzystamy do stworzenia ComboBoxa. Aby ComboBox został utworzony na toolbarze, toolbar musi być rodzicem ComboBoxa. Jednak na razie jako uchwyt rodzica podamy hwnd, czyli nasze główne okno - dlaczego, zaraz się wyjaśni.

C/C++
RECT rc;
SendMessage( hToolbar, TB_GETITEMRECT, TOOL_PLACEHOLDER,( LPARAM ) & rc );

g_hCombo = CreateWindowEx( 0L, "COMBOBOX", NULL,
WS_CHILD | WS_BORDER | WS_VISIBLE | CBS_DROPDOWN,
rc.left, rc.top, rc.right - rc.left, rc.bottom - rc.top,
hwnd,( HMENU ) IDC_COMBOBOX, hThisInstance, 0 );

ComboBox już gotowy, ale aby pojawił się na toolbarze, musimy mu jeszcze zmienić rodzica. Proces adopcyjny jest bardzo prosty i nie będziemy musieli nawet biegać po sądach, wystarczy wywołać SetParent:

C/C++
SetParent( g_hCombo, hToolbar );

Dlaczego stworzyliśmy bachora, a potem przenieśliśmy go do rodziny zastępczej? Nie wygodniej by było od razu utworzyć go z rodzicem hToolbar? Otóż na pewno byłoby mniej roboty, ale wtedy drań wysyłałby wszystkie komunikaty do toolbaru, a nie do okna głównego, czyli bez zastosowania subclassingu byłyby one dla nas nieprzydatne (nie moglibyśmy obsługiwać zdarzeń związanych z ComboBoxem). Tymczasem dzięki tej sztuczce wszystko gra jak w zegarku:

ComboBox wpakowany na toolbar (Windows 98)
ComboBox wpakowany na toolbar (Windows 98)
Poprzedni dokument Następny dokument
Toolbar, cz. 1 Toolbar, cz. 3