Hej Lasse,
Ja, jeg skrev det kun ved at bruge Win32 API'et!
Jeg vil prøve at ridse det lidt op for dig!
Først lav en grund klasse der eksempelvis kunne hedde: CONTROL. Denne klasse skal aldrig benyttes alene, men er en klasse der kun indeholder de mest fundamentale Win32 kommandoer og funktioner. En rå skebelon af denne klasse kunne se således ud:
class CONTROL
{
public:
bool Create(HWND);
HWND Gethandle();
void AddControl(CONTROL *);
void Messages(MSG);
virtual void Repaint();
private:
HWND hwnd;
CONTROL *controls[X]; //X skal erstattes med et antal!
};
Create(HWND)
Denne funktion skulle egenligt havde været klassens constructor, men da det ikke vil kunne lade sig gøre på grund af dynamikken i mit vindue system, har jeg valgt at bruge denne funktion som en slags "constructor"... Denne funktion skaber selve vinduet og indeholder derfor Win32 funktionen CreateWindowEx(). Hvis den gældende vindue skal have en 'Parent /Child' forhold skal HWND'en fra dens 'Parent' passes som argument! Denne funktion skal forøvrigt ALDRIG kaldes direkte - men kan desværre heller IKKE tilføjes som en private member funktion, da andre klasser skal havde adgang til den (forklaring følg. senere)
HWND GetHandle()
Denne funktion returnere såmænd bare den private declared hwnd! Der findes ingen SetHandle() da hwnd'en bliver tilskrevet af Windows (i Create() funktionen)...
AddControl(CONTROL *)
Denne funktion kræver en pointer til en klasse af sin egen type. Det vil sige, at pointeren bliver betragtet som en 'Child' komponent og tilføjes til controls[] array'en i private sektionen! Denne funktion benytter Create() funktionen og derfor må Create() funktionen ikke benyttes udefra klassen (Kun på nær én enkelt undtagelse - men det kommer vi nærmere ind på)!
Messages(MSG)
Her er nok den mest interessante funktion. Denne funktion bliver kaldt af sin 'Parent' komponet, hvis den altså har sådan én! Den MSG der bliver krævet som argument, er ikke nøvendigvis sendt for at blive brugt af den gældende klasse - det kunne være en MSG der er blevet sendt til én af dens 'Childs' (Altså til en pointer der ligger i controls[] Array'en). Derfor skal HWND der ligger i MSG'en først sammenlignes med klassens egen hwnd, hvis sammenligningen mislykkedes, skal MSG'en sendes videre til alle komponenterne i controls[] array'en. I tilfælde af at sammenligningen lykkedes, bliver MSG'en behandlet som en normal Windows message. Eks. hvis MSG'en indeholder en WM_PAINT besked, skal den virtuelle funktion Repaint() kaldes.
Eks:
bool CONTROL::Messages(MSG msg)
{
if (msg.hwnd == hwnd)
{
switch(msg.message)
{
case WM_PAINT: Repaint();
break;
//case WM_ osv.
}
return true;
}
else
{
bool b = false;
for (int i = 0; i < X; i++) //X skal erstattes med et max antal!
if ( controls[ i ] -> Messages(msg) )
b = true;
return b;
}
}
Repaint()
Denne funktion er en virtuel funktion og forventes at blive overskrevet af en top-klasse (Se evt. den senere defineret klasse: WINDOW). Derfor indeholder denne funktion ingen kode overhovedet!
Fidusen er nu at alle komponenter, så som knapper osv. arver denne klasse. Eks:
class WINDOW: public CONTROL
{
public:
void Repaint(); //Overskriver Repaint() funktion fra bund klassen.
};
I Repaint() funktionen skal der selvfølig tegnes selve komponentens grafiske design. I dette tilfælde er det bare en normal vindue så denne komponent har nok ikke så meget brug for denne virtuelle funktion. Men hvis det var en knap eller noget andet, ville denne funktion jo være meget væsenligt!
Din WINDOW klasse skulle nu gerne havde arvet alle egenskaberne fra bund klassen CONTROL. Du kan selvfølgelig tilføje flere egenskaber som din WINDOW komponent skal kunne!
Lav nu en ny helt uafhængig klasse, der skal indeholde selve programmets main loop. Den kunne evt. blive kaldt for APPLICATION... DENNE KLASSE SKAL ALTSÅ IKKE ARVE ANDRE KLASSER! Klassen skal modtage alle beskeder, der bliver sendt fra Windows direkte. Samtidig skal den indeholde programmets hoved vindue, der evt. kunne blive kaldt for MainWindow:
class APPLICATION
{
public:
WINDOW MainWindow; //Tidligere defineret klasse!
bool Initialize(HINSTANCE);
void Run();
~APPLICATION; //Destructor
private:
WNDCLASSEX wcex;
};
Initialize(HINSTANCE)
Denne funktion skaber programmets hoved vindue ved hjælp af MainWindow.Create() funktion. Desuden skal Win32 funktionen RegisterClassEx() skrives her med den private wcex, som argument!
Run()
Her bliver selve programmets main loop sat igang!
Destructor
Her skal applikationen (programmet) afskrives ved hjælp af Win32 funktionen UnregisterClass() med wcex som argument.
Da Windows ikke kan sende beskeder direkte til en klasse kan vi i stedet gøre således.:
APPLICATION Application; //En global applikation bliver declaret
LRESULT CALLBACK WndProc(HWND hwnd, UINT msg, WPARAM wParam, LPARAM lParam)
{
if (msg == WM_DESTROY)
PostQuitMessage(0);
else
{
MSG message; //En lokal MSG bliver declaret og assignet!
message.hwnd = hwnd;
message.message = msg;
message.lParam = lParam;
message.wParam = wParam;
Application.MainWindow.Messages(message); //...og sendt til programmets hoved vindue
}
return DefWindowProc(hwnd,msg,wParam,lParam);
}
Læg mærke til at klassen APPLICATION kun må skabes én gang da den indeholder selve main-loopet!
Alle disse klasser og kode jeg har nævnt kan selvfølgeligt skrives i sin egen header for så at blive inkluderet i et projekt. I selve dit projekts main funktion kan der derfor stå noget lign.:
int APIENTRY WinMain(HINSTANCE hInstance,
HINSTANCE hPrevInstance,
LPSTR lpCmdLine,
int nCmdShow)
{
if (!Application.Initialize(hInstance))
return 0;
/*
WINDOW Vindue;
Application.MainWindow.AddControl(&Vindue); //Tilføj anden komponent til hoved vinduet!
*/
Application.Run();
return 0;
}
Og det er faktisk alt der skal til, hver gang du vil starte for Application'en og den's hoved vindue...
Hvis der er noget der står dig uklart eller hvis du gerne vil havde mere information så bare sig til!
Held og Lykke
Søren Klit Lambæk
[Redigeret d. 29/03-05 01:14:13 af Søren Klit Lambæk]