Esempio: app_one

Ho pensato che un esempio di creazione di controlli a runtime (in fase di esecuzione), cosa molto utile, risulta poco pratico se l’applicazione non fa niente di particolare. Per questo inizieranno ora una serie di lezioni che porteranno gradualmente alla creazione di un programma che supporta l’apertura, la modifica e il salvataggio di documenti di testo. Il primo passo, quello che faremo in questa lezione, consisterà nel creare la finestra e l’EDIT control posizionato al centro del nostro programma. Riprendendo il codice della Semplice Finestra precedentemente creata, aggiungiamo un #define per l’ID del nostro controllo ed i seguenti handlers di messaggi all’interno della Window Procedure.

#define IDC_MAIN_EDIT   101
    case WM_CREATE:
    {
        HFONT hfDefault;
        HWND hEdit;

        hEdit = CreateWindowEx(WS_EX_CLIENTEDGE, "EDIT", "",
            WS_CHILD | WS_VISIBLE | WS_VSCROLL | WS_HSCROLL | ES_MULTILINE |
            ES_AUTOVSCROLL | ES_AUTOHSCROLL,
            0, 0, 100, 100, hwnd, (HMENU)IDC_MAIN_EDIT,
            GetModuleHandle(NULL), NULL);
        if(hEdit == NULL)
            MessageBox(hwnd, "Impossibile creare l'edit box",
                         "Errore", MB_OK | MB_ICONERROR);

        hfDefault = GetStockObject(DEFAULT_GUI_FONT);
        SendMessage(hEdit, WM_SETFONT, (WPARAM)hfDefault,
                    MAKELPARAM(FALSE, 0));
    }
    break;
    case WM_SIZE:
    {
        HWND hEdit;
        RECT rcClient;

        GetClientRect(hwnd, &rcClient);

        hEdit = GetDlgItem(hwnd, IDC_MAIN_EDIT);
        SetWindowPos(hEdit, NULL, 0, 0, rcClient.right, rcClient.bottom,
                  SWP_NOZORDER);
    }
    break;

Creare i controlli

La creazione dei controlli controlli, come per gli altri tipi di finestra si ottiene con l’API CreateWindowEx(). La differenza è che passiamo delle classi pre-registrate, in questo caso la classe “EDIT” del controllo in questione, in questo modo otterremo la finestra del controllo. Quando creiamo i controlli li usiamo all’interno delle finestre di dialogo, non facciamo altro che scrivere una lista di controlli che al momento della chiamata a DialogBox() oppure CreateDialog() il sistema andrà ad interpretare nello stesso modo di quando essi vengono definiti all’interno di un file di risorse.

    hEdit = CreateWindowEx(WS_EX_CLIENTEDGE, "EDIT", "",
        WS_CHILD | WS_VISIBLE | WS_VSCROLL | WS_HSCROLL | ES_MULTILINE |
        ES_AUTOVSCROLL | ES_AUTOHSCROLL,
        0, 0, 100, 100, hwnd, (HMENU)IDC_MAIN_EDIT,
        GetModuleHandle(NULL), NULL);
    if(hEdit == NULL)
        MessageBox(hwnd, "Impossibile creare l'edit box.",
                 "Errore", MB_OK | MB_ICONERROR);

Come potete notare questa chiamata a CreateWindowEx() specifica stili diversi, è una pratica abbastanza comune, specialmente per i common controls, che generalmente hanno una lista più fornita di stili. I primi 4 stili WS_ sono abbastanza ovvi, stiamo creando il controllo come figlio della nostra finestra, vogliamo che sia visibile e che abbia una scroll bar verticale ed una orizzontale.

I 3 stili specifici per gli EDIT control (ES_MULTILINE | ES_AUTOVSCROLL | ES_AUTOHSCROLL) specificano che l’edit control deve supportare linee multiple di testo ed effettuare lo scroll automatico sia in verticale che in orizzontale rispettivamente.

Gli stili regolari (WS_*) sono elencati qui. e gli stili estesi (WS_EX_*) sono spiegati sotto la referenza a CreateWindowEx() nella MSDN, qui potrete inoltre trovare collegamenti a stili specifici per ogni controllo (ES_* nel caso del nostro edit control).

Abbiamo usato l’handle della nostra finestra come padre del controllo, e gli abbiamo assegnato un id di IDC_MAIN_EDIT che useremo in seguito per far riferimento al controllo. La posizione, la grandezza e i parametri non rappresentano molto in questo momento dato che andremo ad effettuare il ridimensionamento in seguito dinamicamente all’interno del messaggio WM_SIZE in modo che vada a riempire sempre la nostra finestra.

Ridimensionamento di controlli creati dinamicamente

In generale se la finestra viene lasciata ridimensionabile avrete sempre bisogno di aggiungere del codice per ridimensionare e riposizionare i controlli che avete creato, in modo da rispettare una visuale decente.

    GetClientRect(hwnd, &rcClient);

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

Siccome abbiamo solamente un controllo per ora, l’operazione è relativamente semplice. Utilizziamo GetClientRect() per ottenere le dimensioni della Client Area della finestra, ovvero l’area bianca della finestra che non include i bordi, i menu e il titolo. I valori di questa dimensione saranno inseriti all’interno della nostra struttura RECT, avremo quindi i valori left(sinistra), top(alto), width(larghezza) height(altezza) che ci definiscono esattamente l’area su cui possiamo lavorare.

Dopodichè semplicemente ci ricaviamo l’handle del nostro edit control usando GetDlgItem() che funziona esattamente come sulle finestre e sulle dialog, e poi chiamiamo SetWindowPos() per muovere e ridimensionare il controllo per riempire l’intera client area. Potete ovviamente cambiare i valori da passare all’interno di SetWindowPos() in qualcosa che riempie solo metà dell’altezza della finestra, lasciando la parte bassa libera per piazzare altri controlli.

Creare altri controlli a runtime

Non andrò oltre con gli esempi su come creare dinamicamente altri controlli tipo LISTBOX, BOTTONI, etc… perchè sostanzialmente è la stessa cosa e diventa noioso dopo un pò :) Se seguite i links alla MSDN che vi ho dato sopra, oppure guardate all’interno della vostra referenza locale alle API di Windows, sarete in grado di trovare tutte le informazioni necessarie per creare gli altri controlli standard.

Comunque non preoccupatevi perchè nelle prossime sezioni ritorneremo sui common controls e sicuramente diventerete più pratici.