c ++ - Linking Button og Window Classes

Indlæg af Hanne Mølgaard Plasc

Problem



Jeg arbejder på min egen personlige winapi wrapper. Min ønskede syntaks er sådan:


// #define wndproc(name) void name (Window & hwnd, UINT msg, WPARAM wParam, LPARAM lParam)
// #define buttonproc(name) void name (Button & hwnd, UINT msg, WPARAM wParam, LPARAM lParam)

wndproc (rightClick) { //evaluates to function to handle window message
    ::msg ("You right clicked the window. Closing window...");
    hwnd.close(); //close() is implemented in my Window class
}

buttonproc (buttonClick) { //same thing basically
    ::msg ("You clicked this button. I'm going to hide the other one...");

    //if text on this button is "One button", find the one belonging to parent
    //with the text "Other button" and hide it, or vice-versa
    hwnd.text == "One button"
    ? hwnd.parent().button ("Other button").hide();
    : hwnd.parent().button ("One button").hide();
}

int main() {
    Window win; //create default window
    win.addmsg (WM\_LBUTTONDOWN, rightClick); //look for l-click message and call that

    Button b1 (win, "One button", 100, 100, 50, 20, buttonClick); //parent, coords, size, clicked
    Button b2 (win, "Other button", 200, 100, 50, 20, buttonClick);  

    return messageLoop(); //should be self-explanatory
}  


Sagen er, i wndproc, hwnd er en Window & og i buttonproc, hwnd er en Button &. Jeg kan måske komme væk med at sige:


msgproc (Window, rightClick){...} 
msgproc (Button, buttonClick){...}


Problemet er, at jeg skal kalde disse procedurer og give dem den rigtige hwnd. Min hovedvindue procedure er implementeret i min Window klasse. Det får de fire normale argumenter. Hvis jeg skal viderebringe en WM\_COMMAND besked til den højre knapprocedure, vil jeg gerne give den det tilsvarende Button objekt.


Den måde, som det er på nuværende tidspunkt, passerer jeg en peger til superklassen for både Window og Button. Det skaber selvfølgelig fortryllet kode som:


((Window *)hwnd)->operator()() //get HWND of the Window


Det ser ud til, at det virkelig ikke virker så godt i hvert fald. Desværre er den eneste måde, jeg kan tænke på i øjeblikket at gøre det, at holde en liste over alle Button oprettet og trække den rigtige ud. udvide dette til alle mulige modtagere.


Fordelen ved at gøre det på denne måde er, at min Button klasse har en statisk vindueprocedure, der kaldes når som helst en WM\_COMMAND besked er fundet. Jeg har ikke tilføjet andre kontroller, men det er designet til at fungere ved at tjekke id med eksisterende og kalde den procedure, du angiver, når du opretter knappen, hvis den er en kamp. Sagen er, når dette er gjort, vil andre ting (som en afkrydsningsfelt), der tilføjer WM\_COMMAND -handleren, også blive kaldt.


Jeg tænkte på at holde en liste i Window af hvert HWND barn og dets tilsvarende objekt. På denne måde kan jeg bare udnytte de ekstra procedurer i hver klasse som Button, hvilket vil medføre meget ekstra behandling og erstatte proc [i] ((BaseWindow *)hwnd, msg, wParam, lParam) med noget som proc [i] (control [loword(wParam)], msg, wParam, lParam) for WM\_COMMAND ved hjælp af lParam for at se om det er en kontrol.


Det ser ud til, at jeg mangler noget stort, men. Chancerne er, at jeg begynder at gennemføre dette og derefter løbe ind i et stort problem. Er der en bedre måde at gøre alt dette på?


Mens jeg er i den, er der en måde at lave en control() -funktion, der returnerer den korrekte objekttype (Button, Checkbox ...), afhængigt af hvilken en den finder id'et til at svare til i stedet for blot en matrix af forskellige genstande (som jeg er temmelig sikker på, at jeg har set en måde at gøre)?

Bedste reference


Spørgsmålet var, hvordan man håndterer WM\_COMMAND-meddelelser, som bliver leveret til moderen snarere end knappen klasse (eller hvad som helst).


Den enkleste løsning er at tilføje en WM\_COMMAND-handler til vinduesklasseklassen for at videresende meddelelsen til den kontrol, der genererede den. Meddelelsen vil så blive håndteret i kontrolens klasse.


Koden i WM\_COMMAND-behandleren kan se sådan ud:


if (lParam != 0)
{
    // lParam non zero so this is a control notification.
    if ((HWND)lParam == hWnd)
    {
        // The message has arrived at its destination
        return OnNotify(HIWORD(wParam), LOWORD(wParam));
    }
    else
    {
        // Reflect the message back to the control.
        return SendMessage((HWND)lParam, WM\_COMMAND, wParam, lParam);
    }
}


Jeg misforstod oprindeligt spørgsmålet. Nedenstående beskriver et par måder at routere meddelelser til objekter på:


MFC-vejen


Hvert vindue bruger samme vindue procedure. Du har et globalt kort fra HWND til vinduesobjekt. (Kortet er faktisk per tråd, men i de fleste apps er det unødvendigt.) Når en besked ankommer, kigger du op på objektet og sender meddelelsen til den. Hvis Button stammer fra Window, så er det meget nemt at gøre pr. Klasse behandling.


En lille komplikation er, at hvis du vil fange meddelelser, der genereres mens vinduet oprettes, skal du føje vinduet til dit kort i den globale vindueprocedure.


ATL-vejen


Hvert vindue har sin egen vindueprocedure (muligvis også sin egen klasse for at gøre det nemt at indstille vinduet proceduren oprindeligt; jeg glemmer). Vinduesproceduren er en genereret stub, der lægger en peger på objektet * og hopper til basisklassens vinduesprocedure (som er en ikke-statisk medlemsfunktion). (For at holde det enkelt, skal stubben hoppe til et ikke -virtual wndproc i basisklassen, der kalder den 'rigtige' virtuelle wndproc.) Udover at ændre den måde, hvorpå HWNDs kort til objekter er, er det i andre henseender stort set det samme som MFC-modellen.


* På x86 sætter stubben objektpekeren i ECX, før man hopper til wndproc. Dette ville også fungere på x64 (selvom jeg ikke ved, om det virker som dette), men objektspekeren overskriver HWND (så ikke-virtuelle WNDproc-basen vil ikke have en HWND-parameter).