Esempio: app_three

Una nota IMPORTANTE sui Common Controls

Per tutti i common controls, è indispensabile chiamare InitCommonControls() PRIMA di usarli. Bisognerà includere #include <commctrl.h> per usare questa funzione e ottenere le dichiarazioni necessarie da usare nei Common Controls. Bisognerà aggiungere anche comctl32.lib nei setaggi del linker (se non è già inclusa). È da notare che InitCommonControls() è una API molto vecchia e per un maggiore controllo è possibile usare InitCommonControlsEx() (aka InitCommonControlSex()) che oltretutto è necessaria per i common controls più recenti. Comunque finchè non si utilizzano le funzionalità avanzate, InitCommonControls() è adeguata è più semplice.

Toolbars

Potete creare toolbars usando CreateToolbarEx() ma in questo tutorial non lo useremo. La prima cosa da fare è creare la toolbar…

hTool = CreateWindowEx(0, TOOLBARCLASSNAME, NULL, WS_CHILD | WS_VISIBLE, 0, 0,
        0, 0, hwnd, (HMENU)IDC_MAIN_TOOL, GetModuleHandle(NULL), NULL);

È abbastanza semplice, TOOLBARCLASSNAME è una costante definita negli headers dei common control. hwnd è la finestra padre, dove si vuole inserire la toolbar. IDC_MAIN_TOOL è un identificatore che è possibile usare in seguito per ottenre l’ HWND della toolbar usando GetDlgItem(), se lo si desidera.

// Invia il messaggio TB_BUTTONSTRUCTSIZE, che è richiesto
// per compatibilità.
SendMessage(hTool, TB_BUTTONSTRUCTSIZE, (WPARAM)sizeof(TBBUTTON), 0);

Questo messaggio è richiesto per far "capire" al sistema quale versione della libreria common controls si sta usando. Dato che le nuove versioni aggiungono nuove funzionalità alla struttura, in questo modo il sistema può "immaginare" esattamente cosa si sta richiedendo.

Bottoni sulle Toolbar

Le bitmap dei bottoni sulle toolbar semplici sono di due tipi, i bottoni standard, forniti con comctl32, e i bottoni definiti dall’utente. NOTA: Bottoni e bitmap sono aggiunti alle toolbar separatamente… prima si aggiunge una lista di immagini da usare, e POI si aggiunge la lista di bottoni dicendo quale bottone usa l’immagine.

Aggiungere bottoni Standard

Ora che abbiamo creato una toolbar, abbiamo bisogno di aggiungere dei bottoni. Le bitmap più comuni sono disponibili nella libreria common control stessa, quindi non c’è bisogno di ricrearli oppure aggiungerli agli exe che li usano.

Prima dichiariamo TBBUTTON e TBADDBITMAP

TBBUTTON tbb[3];
TBADDBITMAP tbab;

Dopodiché aggiungiamo le bitmap standard alla toolbar, usando l’imagelist predefinita nella libreria common control…

tbab.hInst = HINST_COMMCTRL;
tbab.nID = IDB_STD_SMALL_COLOR;
SendMessage(hTool, TB_ADDBITMAP, 0, (LPARAM)&tbab);

Ora che abbiamo caricato le immagini, possiamo aggiungere alcuni bottoni che le usano…

ZeroMemory(tbb, sizeof(tbb));
tbb[0].iBitmap = STD_FILENEW;
tbb[0].fsState = TBSTATE_ENABLED;
tbb[0].fsStyle = TBSTYLE_BUTTON;
tbb[0].idCommand = ID_FILE_NEW;

tbb[1].iBitmap = STD_FILEOPEN;
tbb[1].fsState = TBSTATE_ENABLED;
tbb[1].fsStyle = TBSTYLE_BUTTON;
tbb[1].idCommand = ID_FILE_OPEN;

tbb[2].iBitmap = STD_FILESAVE;
tbb[2].fsState = TBSTATE_ENABLED;
tbb[2].fsStyle = TBSTYLE_BUTTON;
tbb[2].idCommand = ID_FILE_SAVEAS;

SendMessage(hTool, TB_ADDBUTTONS, sizeof(tbb)/sizeof(TBBUTTON), (LPARAM)&tbb);

Qui abbiamo aggiunto i bottoni Nuovo, Apri, e Salva con nome usando le immagini standard, che è sempre una buona idea dato che l’utente è abituato a vederli e conosce bene cosa rappresentano.

Gli indici di ogni immagine dell’imagelist sono definiti negli headers dei common control e sono elencati sulla MSDN

Abbiamo assegnato un ID ad ogni bottone (ID_FILE_NEW etc…) allo stesso modo degli ID assegnati ai menu. Questi bottoni genereranno un messaggio WM_COMMAND identico a quello del menu, Quindi non è non è non bisogna ulteriormente processare i messaggi generati dalle toolbar se il messaggio viene giè processato nei menu. Allo stesso modo se avessimo aggiunto un bottone che non assolveva a nessuna funzione presente nei menu, sarebbe bastato aggiungerne l’ID e processarlo all’interno di WM_COMMAND.

Se vi state chiedendo la funzione svolta dal parametro wParam passato a TB_ADDBUTTONS, esso semplicemente svolge il calcolo del numero di bottoni nell’array tbb in modo da non costringerci a inserire un valore fisso nel codice. Se mettiamo 3, infatti, è ancora corretto, solo che nel momento in cui andremo ad inserire un nuovo bottone, dovremo andare a modificare a 4 il valore passato da SendMessage e in programmazione tutto questo è cattivo… Si tende infatti a ottimizzare il tutto in modo che una singola modifica su una parte del codice debba apportare il minor numero possibile di modifiche su tutto il resto. Se per esempio sizeof(TBBUTTON) è 16 bytes, finché avremo 3 bottoni sizeof(tbb) sarà 16 * 3 ovvero 48, quindi 48 / 16 ci darà il numero di bottoni, cioè 3.

Status bars

Spesso nelle applicazioni insieme alle ToolBar troviamo le status bars, quelle piccole barre ai piedi della finestra che visualizzano le informazioni. Sono semplici da usare, semplicemente bisogna crearle…

hStatus = CreateWindowEx(0, STATUSCLASSNAME, NULL,
    WS_CHILD | WS_VISIBLE | SBARS_SIZEGRIP, 0, 0, 0, 0,
    hwnd, (HMENU)IDC_MAIN_STATUS, GetModuleHandle(NULL), NULL);

E dopodiché (opzionalmente) impostare il numero di sezioni desiderate. Se non ne viene settata nessuna, semplicemente ci sarà una singola sezione che usa l’intera larghezza della barra, ed è possible impostare oppure ricavare il testo visualizzato con le già conosciute funzioni SetWindowText() e GetWindowText() così come per molti altri controlli. Quando invece si impostano le sezioni è buon uso specificarne la larghezza con SB_SETPARTS, e poi impostare il testo con SB_SETTEXT per ogni sezione.

Per definire le larghezze, dichiariamo un array di interi, dove ogni valore è la larghezza in pixels di una sezione. Se vogliamo che una sezione usi tutto lo spazio rimanente specifichiamo la larghezza -1.

int statwidths[] = {100, -1};

SendMessage(hStatus, SB_SETPARTS, sizeof(statwidths)/sizeof(int), (LPARAM)statwidths);
SendMessage(hStatus, SB_SETTEXT, 0, (LPARAM)\"Hi there :)\");

Il wParam è dinuovo il calcolo di quanti elementi stanno nell’array. Quando abbiamo finito di aggiungere le sezioni, settiamo il primo (indice 0) per vederlo in azione.

Grandezza adeguata

Contrariamente ai menu, tool e status bars sono controlli separati che giaciono nella client area della finestra padre. Per questo se lasciamo invariato il codice di WM_SIZE il controllo andrà sovrapposto con la textbox aggiunta negli esempi precedenti. E’ un problema molto semplice da correggere… in WM_SIZE, muoviamo la tool e la status bar in posizione e sottraiamo le loro altezze e posizioni dalla client area in modo che possiamo muovere il nostro controllo textbox per fargli riempire lo spazio rimanente…

  HWND hTool;
  RECT rcTool;
  int iToolHeight;

  HWND hStatus;
  RECT rcStatus;
  int iStatusHeight;

  HWND hEdit;
  int iEditHeight;
  RECT rcClient;

  // Dimensiona la toolbar e ne ricava l'altezza

  hTool = GetDlgItem(hwnd, IDC_MAIN_TOOL);
  SendMessage(hTool, TB_AUTOSIZE, 0, 0);

  GetWindowRect(hTool, &rcTool);
  iToolHeight = rcTool.bottom - rcTool.top;

  // Dimensiona la status bar e ne ricava l'altezza

  hStatus = GetDlgItem(hwnd, IDC_MAIN_STATUS);
  SendMessage(hStatus, WM_SIZE, 0, 0);

  GetWindowRect(hStatus, &rcStatus);
  iStatusHeight = rcStatus.bottom - rcStatus.top;

  // Calcola l'altezza rimanente e dimensiona l'edit

  GetClientRect(hwnd, &rcClient);

  iEditHeight = rcClient.bottom - iToolHeight - iStatusHeight;

  hEdit = GetDlgItem(hwnd, IDC_MAIN_EDIT);
  SetWindowPos(hEdit, NULL, 0, iToolHeight, rcClient.right, iEditHeight, SWP_NOZORDER);

Sfortunatamente è uno spezzone di codice un pò lungo ma è abbastanza semplice… le toolbars si autoposizionano quando viene inviato il messaggio TB_AUTOSIZE e le status bars fanno lo stesso quando viene inviato il messaggio WM_SIZE (le librerie common control non sono conosciute per la loro consistenza )