Du bist nicht angemeldet.

Stilllegung des Forums
Das Forum wurde am 05.06.2023 nach über 20 Jahren stillgelegt (weitere Informationen und ein kleiner Rückblick).
Registrierungen, Anmeldungen und Postings sind nicht mehr möglich. Öffentliche Inhalte sind weiterhin zugänglich.
Das Team von spieleprogrammierer.de bedankt sich bei der Community für die vielen schönen Jahre.
Wenn du eine deutschsprachige Spieleentwickler-Community suchst, schau doch mal im Discord und auf ZFX vorbei!

Werbeanzeige

1

21.09.2006, 18:37

Kapselung der Windowsoberfläche – Teil1

Hi, damit sich die Tutorial – Sektion mal füllt hab ich mir vorgenommen auch mal ein Tutorial zu schreiben. Da ich mir vor kurzem die Windowsoberfläche gekapselt hab, hab ich mir gedacht, dass ich darüber ein Tutorial schreibe.

Vorüberlegung:

So zuerst wäre es meiner Meinung nach am besten, wenn wir uns überlegen, was unsere Kapselung unterstützen soll und wie man dann später damit Arbeitet. Zuerst sollte man damit ja logischerweise ein Fenster erstellen können und für ein bisschen Interaktion einige Elemente wie z.B. Buttons oder Editfelder. Für die Ordentliche Verwaltung von den Elementen und Fenstern werden wir Listen der STL verwenden. Als nächstes sollte man auf die Fenster und Elemente von überall zugreifen können, damit man z.B. in der main eine Editbox erstellen kann und in der Callback – funktion auf das Element zugreifen kann und den eingegeben Text abfragen kann. Was fällt einem dazu ein? – Natürlich ein Singleton.

Makros und Definitionen:

Bevor wir mit dem Programmieren der eigentlichen Klassen beginnen schreiben wir uns zuerst zwei kleine hilfreiches Makros:

C-/C++-Quelltext

1
2
3
4
5
6
7
8
template <typename T> inline void SAFE_DELETE (T *pPointer)
{
    if (pPointer)
    {
        delete pPointer;
        pPointer = NULL;
    }
}


Hier wird zuerst geprüft ob der Pointer gültig ist, wird dann gelöscht und auf 0 gesetzt.

Und dann noch das zweite:

C-/C++-Quelltext

1
2
3
4
5
6
7
inline unsigned long FAIL (unsigned long Value)
{
    if (!Value)
        return 0;
    else 
        return 1;
}


Jeder der schon mal was mit DirectX gemacht hat kennt dieses Makro und ich finde es so schöner:

C-/C++-Quelltext

1
if (FAIL(Foo())) {}


als so

C-/C++-Quelltext

1
if ((Foo()) != OK) {}


Als nächtes kommen 2 Definitionen für die Error – Codes die wir später brauchen

C-/C++-Quelltext

1
2
const unsigned long OK = 0x00000000L;
const unsigned long ERROR_INVALID_API_CALL = 0x00000001L;


und diese zur Bestimmung der Elenente:

C-/C++-Quelltext

1
2
3
const unsigned long WIN_BUTTON = 0x00000001L;
const unsigned long WIN_STATIC = 0x00000002L;
const unsigned long WIN_EDIT = 0x00000003L;


Die Klasse Element:

Als erstes schreiben wir uns eine nette kleine Klasse, die auf den Namen Element hört.
Später werden wir dann alle Elemente von dieser ableiten.

So hier ist sie:

C-/C++-Quelltext

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
class Element
{
    friend class Button;
    friend class Edit;
    friend class Static; 

public:

    // Deaktiviert oder Aktiviert das Element

    unsigned long enable (bool Enable);

    // Setzt den Font des Elements

    unsigned long set_font (unsigned long Size, bool Bold, bool Italic, bool Underlined, bool Crossed, wchar_t *pFont);

    // Inline Methoden

    inline unsigned long get_id ()                    {return m_ID;}      // Liefert die ID

    inline HWND get_handle      ()                    {return m_Element;} // Liefert das Handle des Elements

    inline HWND get_parent      ()                    {return m_Parent;}  // Liefert das Handle des Parent Fensters zurück

    inline unsigned long get_type ()                  {return m_Type;}    // Liefert den Typ des Elements 

                           
private:
    
    HWND          m_Element;     // Handle des Elements

    HWND          m_Parent;      // Handle des Parent - Fensters

    unsigned long m_ID;          // ID des Elements

    unsigned long m_Type;        // Typ des Elements

    HINSTANCE     m_Instance;    
};


Zuerst geben wir der Klasse ein paar Freunde, die dann später auf die Membervariablen der Element Klasse zugreifen können. Als nächstes werden zwei Methoden implementiert, die für alle Elemente gleich sind: Die Methode enable und die Methode set_font.

Zuerst zu der einfacheren Methode enable:

C-/C++-Quelltext

1
2
3
4
5
6
unsigned long Element::enable (bool Enable)
{
    EnableWindow (m_Element, Enable);

    return OK;
}


Wie man sieht passiert hier nichts schwieriges. Hier wird nur die EnableWindow Funktion der Win32 API aufgerufen, der wir das Handle des Elements angeben und einen bool wert, der angibt ob es aktiviert oder deaktiviert werden soll. Auch in der nächsten passiert nichts großartiges:

C-/C++-Quelltext

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
unsigned long Element::set_font (unsigned long Size, bool Bold, bool Italic, bool Underlined, bool Crossed, wchar_t *pFont)
{
    HFONT Font = NULL;
    unsigned long Weight = 0;

    // Falls Bold == true 

    if (Bold)
        Weight = 1500;

    // Font erstellen

    if ((Font = CreateFontW (Size, 0, 0, 0, 
                             Weight, Italic, 
                             Underlined, Crossed, 
                             ANSI_CHARSET, 
                             OUT_TT_PRECIS, 
                             CLIP_DEFAULT_PRECIS, 
                             ANTIALIASED_QUALITY, 
                             FF_DONTCARE | DEFAULT_PITCH, 
                             pFont)) == NULL)
    {
        return ERROR_INVALID_API_CALL;
    }

    SendMessage (m_Element, WM_SETFONT, reinterpret_cast<WPARAM>(Font), 0); 

    return OK;
}


Zuerst wird hier ein Font erstellt. Die Parameter der Funktion CreateFont kann man hier nachschlagen: CreateFont. Nachdem dieser erstellt wurde schicken wir die Nachricht WM_SETFONT mit der SendMessage Funktion an das Element. Die SendMessage Funktion erwartet das Handle des Elements die zu sendende Nachricht, den WPARAM und den LPARAM Wert. Den zwei letzten Parametern übergibt verschiedene Werte, je nach Nachricht.
Und siehe da, schon ist diese Funktion auch fertig. Gehen wir mal zu den Elementen über.

Die Klassen Button und Static:

Hier erst mal die Klasse, die einen Button präsentiert.

C-/C++-Quelltext

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
class Button : public Element
{
public:

    // Initialsiert den Button

    unsigned long create (wchar_t *pText,
                          unsigned long x, unsigned long y, 
                          unsigned long Width, unsigned long Height, 
                          HWND ParentWindow, 
                          unsigned long ID, 
                          HINSTANCE Instance);

    // Setzt den Text des Buttons

    unsigned long set_text (wchar_t *pText);

    // Liefert den Text des Buttons

    unsigned long get_text (wchar_t *pText);

    // Zerstört das Element 

    unsigned long destroy ();
};


Nicht sonderlich groß, was? Schauen wir uns doch mal die create Funktion an:

C-/C++-Quelltext

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
unsigned long Button::create (wchar_t *pText,
                              unsigned long x, unsigned long y, 
                              unsigned long Width, unsigned long Height, 
                              HWND ParentWindow, 
                              unsigned long ID, 
                              HINSTANCE Instance)
{
    // Membervariablen initialisieren

    m_ID = ID;
    m_Parent = ParentWindow;
    m_Instance = Instance;
    m_Type = WIN_BUTTON;

    // Button erstellen

    if ((m_Element = CreateWindowW (L"BUTTON", pText, WS_CHILD | WS_VISIBLE | BS_PUSHBUTTON,
                                    x, y, 
                                    Width, Height, 
                                    m_Parent,
                                    reinterpret_cast<HMENU>(m_ID), 
                                    m_Instance, 
                                    NULL)) == NULL)
    {
        return ERROR_INVALID_API_CALL;
    }

    return OK;
}


Hier werden zuerst den Membervariablen Werte zugewiesen. Als nächstes wird mit CreateWindow – Funktion der Button erstellt.
Als ersten Parameter übergeben wir hier eine von Windows bereits vorgefertigte Klasse die angibt, dass das Fenster wie ein Standard Button aussehen soll.
Als nächstes übergeben wir den Text der später auf dem Button zu sehen sein soll.
Im dritten Parameter kann man Flags angeben, die angeben wie sich das Fenster verhalten soll. In unserem Fall geben wir hier an, dass es ein einem Parentwindow untergeordnetes Fenster sein soll und dass es sichtbar sein soll. Mit dem Flag BS_PUSHBUTTON geben wir an, dass es ein Button wird und keine Check –oder Radiobox.
Die folgenden 2 Parameter geben an wo sich die linke obere Ecke des Buttons befinden soll. Parameter 6 und 7 beschreiben wie Hoch und breit wir das Fenster wünschen und im darauf folgenden Parameter geben wir an welchem Fenster unser Button untergeordnet sein soll, also in welchen es sichtbar sein soll.
Darauf folgen die ID und eine Variable vom Typ HINSTANCE die man von der WinMain – Funktion her kennt.

Die Methoden set_text, get_text und destroy sind dann wieder ganz einfach und selbsterklärend.

C-/C++-Quelltext

1
2
3
4
5
6
7
unsigned long Button::set_text (wchar_t *pText)
{
    if (!SetWindowTextW (m_Element, pText))
        return ERROR_INVALID_API_CALL;

    return OK;
}


Der SetWindowText Funktion übergibt man wie man sieht nur das Handle des Elements und den Text der gesetzt werden soll.

C-/C++-Quelltext

1
2
3
4
5
6
7
unsigned long Button::get_text (wchar_t *pText)
{
    if (!GetWindowTextW (m_Element, pText, static_cast<int>(wcslen (pText))))
        return ERROR_INVALID_API_CALL;

    return OK;
}


Und der GetWindowText Funktion übergibt man das Handle des Elements und im zweiten ein Array, dass Text speichert. Im dritten Parameter geben wir die länge des Arrays an.

C-/C++-Quelltext

1
2
3
4
5
6
7
unsigned long Button::destroy ()
{
    // das Element zerstören

    DestroyWindow (m_Element);

    return OK;
}


Hier wird einfach nur die Funktion DestroyWindow aufgerufen, der nur das Handle des Elements übergeben wird.

So jetzt wären wir schon am Ende mit der Button Klasse und eigentlich auch schon mit der Klasse Static, denn bei der ist alles gleich bis auf das, dass bei der CreateWindow – Funktion statt L“BUTTON“ L“STATIC“ steht.

Die Klasse Edit:

Bei dieser Klasse ist eigentlich auch alles gleich, bis auf die create Methode. Dort gibt es eine kleine änderung.
Aber zuerst die Klasse:

C-/C++-Quelltext

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
class Edit : public Element
{
public:

    // Initialisiert die Editbox

    unsigned long create (wchar_t *pText,
                          unsigned int x, unsigned int y,
                          unsigned int Width, unsigned int Height,
                          HWND ParentWindow,
                          unsigned long ID,
                          HINSTANCE Instance,
                          bool Password = false,
                          bool ThirdDimension = true);

    // Setzt den Text

    unsigned long set_text (wchar_t *pText);

    // Liefert den Text

    unsigned long get_text (wchar_t *pText);

    // Zerstört das Element 

    unsigned long destroy ();
};


Wie gesagt werden wir nur die create – Methode besprechen. Denn wie man an den Parametern schon sieht gibt es hier Änderungen:

C-/C++-Quelltext

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
unsigned long Edit::create(wchar_t *pText, 
                           unsigned int x, unsigned int y, 
                           unsigned int Width, unsigned int Height, 
                           HWND ParentWindow, unsigned long ID, 
                           HINSTANCE Instance, bool Password, bool ThirdDimension)
{
    // Membervariablen initialisieren

    m_ID = ID;
    m_Parent = ParentWindow;
    m_Instance = Instance;
    m_Type = WIN_EDIT;

    unsigned long Style = WS_VISIBLE | WS_CHILD | WS_BORDER | ES_AUTOHSCROLL;
    unsigned long ExStyle = NULL;

    if (Password)
        Style = Style | ES_PASSWORD;

    if (ThirdDimension)
        ExStyle = WS_EX_CLIENTEDGE;

    // Editbox erstellen

    if ((m_Element = CreateWindowExW (ExStyle, 
                                     L"EDIT",
                                     pText,
                                     Style,
                                     x, y,
                                     Width, Height,
                                     m_Parent,
                                     reinterpret_cast<HMENU>(m_ID),
                                     m_Instance,
                                     NULL)) == NULL)
    {
        return ERROR_INVALID_API_CALL;
    }

    return OK;
}


Hier hat man jetzt die Möglichkeit anzugeben ob das Editfeld 2D oder 3D sein soll. Weiterhin hat man die Möglichkeit geheime Eingaben zu machen, indem jedes Zeichen durch ein * erstetzt wird. Zuerst werden die Membervariablen definiert. Danach wird ein Standard Style erstellt für ein 2D Editfeld. Und eine Extended Style Variable die vorerst null ist. Falls Password true ist fügen wir der Style Variable ES_PASSWORD hinzu und falls ThirdDimension true ist Setzten wir ExStyle auf WS_EX_CLIENTEDGE. Nach dem ganzen initialisieren erstellen wir das Element. Hierzu verwenden wir diesmal CreateWindowEx. Diese Funktion hat nur einen Parameter mehr: nämlich den Extended Style.

Die Klasse Window:

Jetzt sind wir schon bei der Vorletzten Klasse unserer Kapselung. Die Klasse Window wird jetzt nochmal ein wenig größer als die letzten paar Klassen. Sie repräsentiert ein Fenster und verwaltet für dieses Fenster die Elemente.

C-/C++-Quelltext

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
class Window
{
public:
    // Erstellt das Fenster

    unsigned long create (wchar_t *pTitle, 
                          LRESULT CALLBACK WndProc(HWND, UINT, WPARAM, LPARAM), 
                          unsigned long Width, unsigned long Height,
                          HINSTANCE Instance, unsigned long ID);

    // Fügt einen Button hinzu 

    unsigned long add_button (wchar_t *pText, 
                              unsigned long x, unsigned long y,
                              unsigned long Width, unsigned long Height,
                              unsigned long ID);

    // Fügt einen Text hinzu 

    unsigned long add_static (wchar_t *pText,
                              unsigned long x, unsigned long y,
                              unsigned long Width, unsigned long Height,
                              unsigned long ID);

    // Fügt eine Editbox hinzu

    unsigned long add_edit (wchar_t *pText,
                            unsigned long x, unsigned long y,
                            unsigned long Width, unsigned long Height,
                            unsigned long ID, bool Password = false, 
                            bool ThirdDimension = true);

    // Liefert ein Element zurück

    Element *get_element (unsigned long ID);

    unsigned long destroy ();

    // Inline methoden

    inline HWND get_handle () {return m_Window;}   // Liefert das Handle des Fensters

    inline unsigned long get_id () {return m_ID;}  // Liefert die ID des Fensters


private:

    std::list<Element*> m_Elements;    // Liste der Elemente

    std::list<Element*>::iterator m_i; // Iterator

    unsigned long m_ID;                // ID des Fensters

    HWND m_Window;               // Handle des Fensters

    HINSTANCE m_Instance;        // Instanz

};


Hier haben wir 8 Methoden und und 2 inline – Methoden, die jedoch selbsterklärend sind.
Im privaten Teil der Klasse sehen wir schon die Liste der STL die Instanzen vom Typ Element speichert. Danach kommt der Iterator und die üblichen Variablen.

Fangen wir mal wieder mit der create – Methode an.

C-/C++-Quelltext

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
unsigned long Window::create (wchar_t *pTitle, 
                              LRESULT CALLBACK WndProc (HWND, UINT, WPARAM, LPARAM), 
                              unsigned long Width, unsigned long Height,
                              HINSTANCE Instance, unsigned long ID)
{
    WNDCLASSEX windowclass; // Fensterklasse

    wchar_t ClassName[23];

    // Aus der ID einen String machen

    swprintf_s (ClassName, 23, L"Fenster%d", ID);

    m_Instance = Instance;
    m_ID = ID;

    // Größe der Struktur zwischenspeichern

    windowclass.cbSize = sizeof (WNDCLASSEX);

    // Fenster soll beim Verschieben neu gezeichnet werden

    windowclass.style = CS_HREDRAW | CS_VREDRAW;

    // Zeiger auf Callback-Funktion

    windowclass.lpfnWndProc = reinterpret_cast<WNDPROC>(WndProc);

    // Keine erweiterten Einstellungen

    windowclass.cbClsExtra = 0;
    windowclass.cbWndExtra = 0;

    // Instanz speichern

    windowclass.hInstance = Instance;

    // Icons und Cursor festlegen

    windowclass.hIcon   = LoadIcon (NULL, IDI_APPLICATION);
    windowclass.hIconSm = LoadIcon (NULL, IDI_APPLICATION);
    windowclass.hCursor = LoadCursor (NULL, IDC_ARROW);

    // Hintergrundfarbe festlegen

    windowclass.hbrBackground = reinterpret_cast<HBRUSH>(16);

    // Ein Menü brauchen wir nicht

    windowclass.lpszMenuName = NULL;

    // Klassenname angeben

    windowclass.lpszClassName = ClassName;

    // Fensterklasse registrieren

    if (!RegisterClassEx (&windowclass))
        return ERROR_INVALID_API_CALL;

    // Das Fenster erzeugen

    m_Window = CreateWindowExW(NULL,
                               ClassName,
                               pTitle,
                               WS_OVERLAPPEDWINDOW | WS_VISIBLE,
                               CW_USEDEFAULT, CW_USEDEFAULT,
                               Width, Height,
                               NULL,
                               NULL,
                               Instance,
                               NULL);

    if (!m_Window)
        return ERROR_INVALID_API_CALL;

    return OK;
}


Hier wird ein Fenster erstellt in dem wir die WNDCLASSEX – Struktur füllen. Danach wird die Struktur durch den Aufruf von RegisterClassEx Registriert und mit CreateWindowEx wird das Fenster dann erstellt. Also garnichts besonderes.

Dann noch der Code der add_button Methode:

C-/C++-Quelltext

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
unsigned long Window::add_button (wchar_t *pText, 
                                  unsigned int x, unsigned int y,
                                  unsigned int Width, unsigned int Height,
                                  unsigned long ID)
{
    Button *pButton = NULL;
    pButton = new Button;
    Element *pElement = NULL;

    unsigned long Result = OK;

    if (FAIL(Result = pButton->create (pText,
                                       x, y, 
                                       Width, Height, 
                                       m_Window, 
                                       ID, 
                                       m_Instance)))
    {
        return Result;
    }

    pElement = reinterpret_cast<Element*>(pButton);

    m_Elements.push_back (pElement);

    return OK;
}


Hier wird eine Instanz der Klasse Button erstellt und eine Instanz der Klasse Element. Danach wird der Button erstellt in dem wir seine Methode create aufrufen, die wir vorher implementiert haben. Danach wird der Button gecasted, damit er in unsere Liste passt. Später muss man dann dieses Element nur noch zurückcasten, wenn man damit arbeiten will. Damit wären wir schon bei der get_element Methode, die dann z.B. dieses Element zurückliefert, wenn man die Richtige ID angibt.

C-/C++-Quelltext

1
2
3
4
5
6
7
8
9
10
11
12
13
Element *Window::get_element (unsigned long ID)
{
    // Alle Elemente durchgehen

    for (m_i = m_Elements.begin(); m_i != m_Elements.end(); ++m_i)
    {
        if (((*m_i)->get_id ()) == ID)
        {
            return (*m_i);
        }
    }

    return reinterpret_cast<Element*>(NULL);
}


Hier wird jedes Element durchgegegangen und überprüft ob die ID des Elements mit der als Parameter übergebenen ID übereinstimmt. Falls dies der Fall ist Liefern wir das Element zurück. Falls kein Element übereinstimmt Liefern wir null zurück.

Dann kommt die Letzte Methode. Na wer weiß es? Na klar die destroy Methode.

C-/C++-Quelltext

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
unsigned long Window::destroy()
{
    // Jedes Element durchgehen

    for (m_i=m_Elements.begin(); m_i != m_Elements.end(); ++m_i)
    {
        switch ((*m_i)->get_type())
        {
        case WIN_BUTTON: reinterpret_cast<Button*>((*m_i))->destroy(); break;
        case WIN_STATIC: reinterpret_cast<Static*>((*m_i))->destroy (); break;
        case WIN_EDIT: reinterpret_cast<Edit*>((*m_i))->destroy(); break;
    }

        SAFE_DELETE (*m_i);
    }

    m_Elements.clear ();

    return OK;
}


Hier wird jedes Element der Liste durchgegangen und je nach typ gecastet und die jeweilige destroy Methode aufgerufen. Danach wird die Liste geleert.

Hier wären wir mit dieser Klasse schon fertig, weil wir aus Platzgründen die add_static und add_list Methoden nicht besprechen werden. Es wäre ja auch unsinnig, denn die sind sowiso fast das Gleiche wie die add_button Methode.

Die Klasse Gui:

Diese Klasse bringt Ordnung in die vielen Klassen. Diese Klasse verwaltet die Fenster und außerdem ist sie eine Singleton Klasse, damit man überall darauf zugreifen kann.

C-/C++-Quelltext

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
class WinGui
{
public:

    // Destruktor 

    ~WinGui () {}

    // Fügt ein Fenster zur GUI hinzu

    unsigned long add_window (wchar_t *pTitle, 
                              LRESULT CALLBACK WndProc(HWND, UINT, WPARAM, LPARAM), 
                              unsigned int Width, unsigned int Height,
                              HINSTANCE Instance, unsigned long ID);

    // Liefert das Objekt eines Fensters

    Window *get_window (unsigned long ID);

    // räumt auf bei den ganzen Fenstern

    unsigned long destroy ();

    // Liefert die Instanz auf die Gui - Klasse

    static WinGui &instance ();

private:

    // Konstruktor

    WinGui () {}

    std::list<Window*> m_Windows;  // Liste der Fenster

    std::list<Window*>::iterator m_i; // Iterator

};


Wie bei der Window Klasse haben wir hier eine Liste. Dieses mal speichert sie aber keine Elemente wie z.B. ein Button sondern Fenster.

Schauen wir uns doch zuerst einmal an wie der Singleton funktioniert:
Ein Singleton benutzt man, damit man nur eine Instanz von der Klasse erzeugen kann. Damit man also keine Instanz von außen erzeugen kann ist der Konstruktor privat. Die Instanz bekommt man nur durch den aufruf der Methode instance. Die instance - Methode ist eine statische Methode, damit wir die Methode statisch aufrufen können. Damit jeder versteht was ich meine hier ein Beispiel:

C-/C++-Quelltext

1
WinGui::instance().get_window (0);


C-/C++-Quelltext

1
2
3
4
5
6
WinGui &WinGui::instance()
{
    static WinGui TheOneAndOnly;

    return TheOneAndOnly;
}


Hier wird eine statische Instanz der Klasse erzeugt, damit die Instanz nicht gelöscht wird nachdem die Funktion beendet ist. Danach wird die Adresse dieser Instanz zurückgeliefert. Würde man hier die Instanz direkt zurückliefern würde nur eine Kopie dieser erstellt werden und dies würde zu Fehlern führen, weil die Instanz direkt nicht verändert wird, sondern nur die Kopie, wenn wir mit der Klasse arbeiten.

Somit können wir eigentlich gleich mit der add_window Methode weitermachen:

C-/C++-Quelltext

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
unsigned long WinGui::add_window(wchar_t *pTitle, 
                              LRESULT CALLBACK WndProc(HWND, UINT, WPARAM, LPARAM), 
                              unsigned int Width, unsigned int Height, 
                              HINSTANCE Instance, unsigned long ID)
{
    Window *pWindow = NULL;
    pWindow = new Window;

    unsigned long Result = OK;

    // Fenster erstellen

    if (FAIL(Result = pWindow->create(pTitle, WndProc, Width, Height, Instance, ID)))
        return Result;

    // fenster in die Liste schicken

    m_Windows.push_back (pWindow);

    return OK;
}


Das kennen wir eigentlich schon alles aus der add_button Methode. Hier wird die Instanz auf das Fenster erzeugt, dann wird das Fenster erstellt und die Instanz in die Liste geschoben.

Also weiter mit einer Methode die wir eigentlich auch schon kennen:

C-/C++-Quelltext

1
2
3
4
5
6
7
8
9
10
11
12
Window *WinGui::get_window(unsigned long ID)
{
    for (m_i=m_Windows.begin(); m_i != m_Windows.end(); ++m_i)
    {
        if (((*m_i)->get_id ()) == ID)
        {
            return (*m_i);
        }
    }

    return reinterpret_cast<Window*>(NULL);
}


Hier wird jedes Element mit der angegebenen ID verglichen und falls gefunden zurückliefern und sonst NULL;

Als nächstes destroy:

C-/C++-Quelltext

1
2
3
4
5
6
7
8
9
10
11
12
13
14
unsigned long WinGui::destroy()
{
    // Jedes Element durchgehen

    for (m_i=m_Windows.begin(); m_i != m_Windows.end(); ++m_i)
    {
        // Element zerstören

        (*m_i)->destroy ();
    }

    // Die Liste leeren

    m_Windows.clear ();

    return OK;
}


Auch nix neues: Jedes Element durchgehen, zerstören und liste leeren.


Geschafft:

So, jetzt sind wir fertig.

War eigentlich nicht so schwer und für jeden machbar.

Da dies mein erstes Tutorial ist würde ich mich über Verbesserungsvorschläge, Kritik und viellcht auch Lob sehr freuen.

Ach fast hätte ich es vergessen. Hier werden ja nur drei Elemente erklärt. Falls ihr wollt schreibe ich auch noch einen zweiten Teil über Radio – Checkboxes, Rahmen, Listen und Comboboxen.


Anfänger

Anhang:

Beispielprogramm mit Projektmappe im zip – Fotmat(16,04 KB)
Ich gebe bei der Arbeit immer 100%

6% Montags
30% Dienstags
35% Mittwochs
25% Donnerstag
4% Freitag

T-VIRUS

Alter Hase

Beiträge: 548

Wohnort: Göttingen(West)/Nordhausen(Ost)

Beruf: Schüler

  • Private Nachricht senden

2

21.09.2006, 19:03

Langes und nettes Tutorial :)
Hatte auch mal ein Framework aus der C Winapi geschrieben ^^
Aber bei Editboxen hab ich aufgehört :p
Meine Blog:)

Wer Bugs im Text findet kann sie melden, fix erscheint irgendwann :D

MFG T-VIRUS

Anonymous

unregistriert

3

21.09.2006, 19:09

Mh, ich beschwer mich jetzt mal nur über das zip Archiv. Wie wäre es,
wenn du die Intellisense Database und den Debug Ordner raus lässt?
Die Intellisense Database wird automatisch erzeugt, wenn der Benutzer
das Projekt öffnet und builden sollte auch jeder selbst können. Gut ich
habe eine Flatrate, die schnell genug ist, aber unnötiger Kram sollte
vermieden werden.

4

21.09.2006, 19:13

Habs verändert. Sind jetzt nur noch 16 kb
Ich gebe bei der Arbeit immer 100%

6% Montags
30% Dienstags
35% Mittwochs
25% Donnerstag
4% Freitag

Paul_C.

Frischling

Beiträge: 81

Wohnort: Duisburg

  • Private Nachricht senden

5

21.09.2006, 19:57

Habe das Tutorial fast komplett gelesen. Macht einen sehr guten Eindruck. Und meiner Meinung nach auch ein toller Schreibstil.
Schreib ruhig auch das nächste Tutorial. :)

Ich finde es jedenfalls sehr gut.

DarkFitzi

Alter Hase

Beiträge: 608

Wohnort: Eisenberg, Thüringen

Beruf: Schüler, 10te Klasse

  • Private Nachricht senden

6

23.09.2006, 21:57

Gelungenes Tutorial ;-)
würde mich ebenfalls über weitere Teile freuen 8)
Ich würde die Welt gern verbessern, doch Gott gibt mir den Sourcecode nicht! :-(

David Scherfgen

Administrator

Beiträge: 10 382

Wohnort: Hildesheim

Beruf: Wissenschaftlicher Mitarbeiter

  • Private Nachricht senden

7

23.09.2006, 22:13

SAFE_DELETE ist unnötig.
delete 0 ist in C++ erlaubt.

(wusste ich früher auch nicht)

8

24.09.2006, 08:05

@ David Scherfgen

Danke für den Tipp

@ all
Danke für die Kritik und für das Lob
Ich gebe bei der Arbeit immer 100%

6% Montags
30% Dienstags
35% Mittwochs
25% Donnerstag
4% Freitag

9

24.09.2006, 12:20

auch wenn delete 0 erlaubt ist, ist es doch praktisch, wenn der Pointer direkt auf 0 gesetzt wird. Weil, sonst gehts beim zweimaligen Aufrufen daneben.
Lieber dumm fragen, als dumm bleiben!

Anonymous

unregistriert

10

24.09.2006, 12:24

Jonathan_Klein
Das ist so nicht ganz richtig. Der Standard sagt nicht, das es knallen soll wenn man einen Pointer 2x frei gibt, oder wenn er 0 ist. Das ist nicht spezifiziert. Das liegt am Betriebssystem.

Werbeanzeige