Controlli di sottoclasse

Se un controllo esegue quasi tutte le operazioni desiderate, ma sono necessarie altre funzionalità, è possibile modificare o aggiungere funzionalità al controllo originale sottoclassandolo. Una sottoclasse può avere tutte le funzionalità di una classe esistente e tutte le funzionalità aggiuntive che si desidera assegnare.

Questo documento illustra come vengono create le sottoclassi e include gli argomenti seguenti.

Sottoclasse dei controlli prima di ComCtl32.dll versione 6

È possibile inserire un controllo in una sottoclasse e archiviare i dati utente all'interno di un controllo . Questa operazione viene eseguita quando si usano versioni di ComCtl32.dll precedenti alla versione 6. Esistono alcuni svantaggi nella creazione di sottoclassi con versioni precedenti di ComCtl32.dll.

Per creare un nuovo controllo, è consigliabile iniziare con uno dei controlli comuni di Windows ed estenderlo in base a una particolare esigenza. Per estendere un controllo, creare un controllo e sostituire la relativa routine finestra esistente con una nuova. La nuova procedura intercetta i messaggi del controllo e li agisce o li passa alla routine originale per l'elaborazione predefinita. Utilizzare la funzione SetWindowLong o SetWindowLongPtr per sostituire il WNDPROC del controllo. Nell'esempio di codice seguente viene illustrato come sostituire un WNDPROC.

OldWndProc = (WNDPROC)SetWindowLongPtr (hButton,
GWLP_WNDPROC, (LONG_PTR)NewWndProc);

Archiviazione dei dati utente

È possibile archiviare i dati utente con una singola finestra. Questi dati possono essere utilizzati dalla nuova routine finestra per determinare come disegnare il controllo o dove inviare determinati messaggi. Ad esempio, è possibile usare i dati per archiviare un puntatore di classe C++ alla classe che rappresenta il controllo. L'esempio di codice seguente illustra come usare SetProp per archiviare i dati con una finestra.

SetProp (hwnd, TEXT("MyData"), (HANDLE)pMyData);

Svantaggi dell'approccio alla sottoclasse precedente

Nell'elenco seguente vengono evidenziati alcuni degli svantaggi dell'uso dell'approccio descritto in precedenza per la sottoclassazione di un controllo.

  • La procedura della finestra può essere sostituita una sola volta.
  • È difficile rimuovere una sottoclasse dopo la creazione.
  • L'associazione di dati privati a una finestra non è efficiente.
  • Per chiamare la routine successiva in una catena di sottoclassi, non è possibile eseguire il cast della routine finestra precedente e chiamarla, è necessario chiamarla usando la funzione CallWindowProc.

Creazione di sottoclassi di controlli tramite ComCtl32.dll versione 6

Nota

ComCtl32.dll versione 6 è solo Unicode. I controlli comuni supportati da ComCtl32.dll versione 6 non devono essere sottoclassati (o superclassati) con le procedure della finestra ANSI.

 

ComCtl32.dll versione 6 contiene quattro funzioni che semplificano la creazione di sottoclassi ed eliminano gli svantaggi descritti in precedenza. Le nuove funzioni incapsulano la gestione interessata da più set di dati di riferimento, pertanto lo sviluppatore può concentrarsi sulle funzionalità di programmazione e non sulla gestione delle sottoclassi. Le funzioni di sottoclasse sono:

SetWindowSubclass

Questa funzione viene usata per sottoclassare inizialmente una finestra. Ogni sottoclasse viene identificata in modo univoco dall'indirizzo della pfnSubclass e dalla relativa uIdSubclass. Entrambi sono parametri della funzione SetWindowSubclass. Diverse sottoclassi possono condividere la stessa routine sottoclasse e l'ID può identificare ogni chiamata. Per modificare i dati di riferimento, è possibile effettuare chiamate successive a SetWindowSubclass. Il vantaggio importante è che ogni istanza della sottoclasse ha i propri dati di riferimento.

La dichiarazione di una routine di sottoclasse è leggermente diversa da una routine di finestra normale perché contiene due parti di dati aggiuntive: l'ID della sottoclasse e i dati di riferimento. Gli ultimi due parametri della dichiarazione di funzione seguente mostrano questo.

LRESULT CALLBACK MyWndProc (HWND hWnd, UINT msg,
WPARAM wParam, LPARAM lParam, UINT_PTR uIdSubclass,
DWORD_PTR dwRefData);

Ogni volta che un messaggio viene ricevuto dalla nuova routine finestra, vengono inclusi un ID sottoclasse e i dati di riferimento.

Nota

Tutte le stringhe passate alla routine sono stringhe Unicode anche se Unicode non viene specificato come definizione del preprocessore.

 

Nell'esempio seguente viene illustrata un'implementazione strutturata di una routine finestra per un controllo sottoclassato.

LRESULT CALLBACK OwnerDrawButtonProc(HWND hWnd, UINT uMsg, WPARAM wParam,
                               LPARAM lParam, UINT_PTR uIdSubclass, DWORD_PTR dwRefData)
{
    switch (uMsg)
    {
    case WM_PAINT:
        .
        .
        .
        return TRUE;
    // Other cases...
    } 
    return DefSubclassProc(hWnd, uMsg, wParam, lParam);
}

La routine della finestra può essere collegata al controllo nel gestore WM_INITDIALOG della procedura di dialogo, come illustrato nell'esempio seguente.

case WM_INITDIALOG:
{
    HWND button = GetDlgItem(hDlg, IDC_OWNERDRAWBUTTON);
    SetWindowSubclass(button, OwnerDrawButtonProc, 0, 0);
    return TRUE;
}

GetWindowSubclass

Questa funzione recupera informazioni su una sottoclasse. Ad esempio, è possibile usare GetWindowSubclass per accedere ai dati di riferimento.

RemoveWindowSubclass

Questa funzione rimuove le sottoclassi. RemoveWindowSubclass in combinazione con SetWindowSubclass consente di aggiungere e rimuovere dinamicamente sottoclassi.

DefSubclassProc

La funzione DefSubclassProc chiama il gestore successivo nella catena di sottoclassi. La funzione recupera anche l'ID e i dati di riferimento appropriati e passa le informazioni alla routine finestra successiva.