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

24.09.2006, 10:21

Kapselung der Windowsoberfläche - Teil 2

Hallo,

Wie versprochen gibt’s ein zweites Tutorial von mir, in dem ich weitere Elemente erkläre und wir sie in die Klassen einbauen werden.

Definierungen:

Als erstes gibt es neue Error – Codes:

C-/C++-Quelltext

1
2
const unsigned long ERROR_INVALID_PARAM = 0x00000002;
const unsigned long ERROR_NULL_POINTER = 0x00000003;


und neue Defines zur erkennung der Elemente:

C-/C++-Quelltext

1
2
3
4
5
const unsigned long WIN_CHECK = 0x00000004L;
const unsigned long WIN_COMBO = 0x00000005L;
const unsigned long WIN_FRAME = 0x00000006L;
const unsigned long WIN_RADIO = 0x00000007L;
const unsigned long WIN_LIST = 0x00000008L;


Das wars vorerst mal mit den Defines.

Weitere Veränderungen:

Die Klasse Element hat in der Zwischenzeit noch mehr Freunde gefunden mit der sie ihr privates teilen will:

C-/C++-Quelltext

1
2
3
4
5
friend class Combo;
friend class List;
friend class Frame;
friend class Check;
friend class Radio;


Machen wir also mit einem der neuen Freunde von Element weiter.

Eine Combobox:

Eines muss ich vorweg sagen. Die Elemente in diesem Tutorial werden nicht so einfach wie die im letzten. Aber trotzdem machbar.

Also fangen wir wieder mit der Klasse an und nehmen sie später auseinander.

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
class Combo : public Element
{
public:

    // erstellt eine combobox

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

    // fügt einen Listeneintrag hinzu

    unsigned int add_entry (wchar_t *pText);

    // Liefert den Index des gerade angewählten eintrags

    unsigned int get_selected_entry_index ();

    // Liefert die anzahl der einträge

    unsigned int get_num_entry ();

    // Wählt einen Eintrag aus

    unsigned long select_entry (unsigned int Index);

    // Leert die Liste

    unsigned long empty ();

    // Verknüpft einen Eintrag mit Daten

    template <typename T>
    unsigned long set_data (unsigned int Index, T Data);

    // Liefert die Daten eines Eintrags

    template <typename T>
    unsigned long get_data(unsigned int Index, T *pData);

    // Zerstört das Element 

    unsigned long destroy ();
};


Werfen wir mal einen Blick auf die create 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
unsigned long Combo::create(unsigned int x, unsigned int y, 
                            unsigned int Width, unsigned int Height, 
                            HWND ParentWindow, unsigned long ID,
                            HINSTANCE Instance)
{
    // Membervariablen initialisieren

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

    // Button erstellen

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

    return OK;
}


Nichts großes eigentlich das gleiche wie sonst auch. Der erste Parameter der CreateWindow ist klar, das ist die vorgefertigte Struktur für eine Combobox. Text geben wir keinen an, da dies ja sowieso keinen sinn hätte. Als Styles geben wir an, dass das Fenster sichtbar sein soll, dass es ein child fenster sein soll und dass es eine Dropdownliste haben soll. Darauf folgen die Koordinaten der linken oberen Ecke, die Fenstergröße und das Parentwindow. Dann brauchen wir noch die ID und die Instanz.

Natürlich sollten wir unserer Combobox auch Einträge hinzufügen können. Dies funktioniert mit der add_entry Methode.

C-/C++-Quelltext

1
2
3
4
unsigned int Combo::add_entry(wchar_t *pText)
{
    return static_cast<unsigned int>(SendMessageW (m_Element, CB_ADDSTRING, 0, reinterpret_cast<LPARAM>(pText)));
}


Wir senden in dieser Methode nur die Nachricht CB_ADDSTRING und übergeben ihr im lParam den Text der angezeigt werden soll. Im gegenzug liefert uns die SendMessage Funktion den Index des Eintrags, den wir dann auch gleich zurückliefern.
Später könnte es doch auch Hilfreich sein Daten mit so einem Eintrag zu verknüpfen. Damit man hier auch jeden Datentyp übergeben kann benutzen wir hier ein Template.

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
template <typename T>
unsigned long Combo::set_data(unsigned int Index, T Data)
{
    T *pData = NULL;

    // parameter prüfen

    if (Index > get_num_entry ()-1)
        return ERROR_INVALID_PARAM;

    // Speicher reservieren

    pData = new T;
    *pData = Data;

    if (!pData)
        return ERROR_NULL_POINTER;

    // Daten mit listeneintrag verknüpfen

    if ((SendMessageW (m_Element, CB_SETITEMDATA, Index, reinterpret_cast<LPARAM>(pData))) == CB_ERR)
        return ERROR_INVALID_API_CALL;

    return OK;
}


Diese Funktion erwartet zuerst den Index des Eintrags mit dem die Daten verknüpft werden sollen. Und als nächstes kommen die Daten an sich.
Als erstes wird hier überprüft ob der Eintrag, mit dem die Daten verknüpft werden sollen auch wircklich existiert. Nach diesem Vorgang wird Speicher für den angegebenen Wert reserviert, da man der SendMessage Funktion eben einen Zeiger übergeben muss. Danach wird die Message CB_SETITEMDATA gesendet.
Im wParam geben wir den Index an und in lParam den Zeiger.

Natürlich darf auch eine Funktion zum abfragen der Daten nicht Fehlen:

C-/C++-Quelltext

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
template <typename T>
unsigned long Combo::get_data(unsigned int Index, T *pData)
{
    T *pTemp = NULL;

    // Parameter prüfen

    if (Index > get_num_entry ()-1)
        return ERROR_INVALID_PARAM;

    // Daten abfragen

    if ((pTemp = reinterpret_cast<T*>(SendMessageW (m_Element, CB_GETITEMDATA, Index, 0))) == reinterpret_cast<T*>(CB_ERR))
        return ERROR_INVALID_API_CALL;

    *pData = *pTemp;

    return OK;
}


Hier wird wiederum der Index überprüft. Danach wird die Message CB_GETITEMDATA an das Element gesendet. Sie erwartet als wParam den Index und Liefert die Daten zurück.
Und jeder weiß es, wo new im spiel ist darf auch delete nicht weit sein. Darum gibt es die empty Methode:

C-/C++-Quelltext

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
unsigned long Combo::empty()
{
    void *pData = NULL;

    for (unsigned int i=0; i < get_num_entry (); ++i)
    {
        pData = reinterpret_cast<void*>(SendMessageW (m_Element, CB_GETITEMDATA, i, 0));

        if (pData != reinterpret_cast<void*>(CB_ERR))
        {
            SAFE_DELETE (pData);
        }
    }

    SendMessageW (m_Element, CB_RESETCONTENT, 0, 0);

    return OK;
}


Diese Funktion macht nichts weiter als jeden Eintrag durchzugehen und den (Falls vorhandenen)Speicher zu löschen. Danach werden noch mit der CB_RESETCONTENT Message alle Einträge gelöscht.
Die Methode destroy macht dann auch nichts weiter als diese empty Methode aufzurufen und die Combobox durch DestroyWindow zu zerstören.
Wichtig ist auch eine Methode zum auswählen eines Eintrags:

C-/C++-Quelltext

1
2
3
4
5
6
7
8
9
unsigned long Combo::select_entry (unsigned int Index)
{
    if (Index > get_num_entry () - 1)
        return ERROR_INVALID_PARAM;

    SendMessageW (m_Element, CB_SETCURSEL, Index, 0);

    return OK;
}


Hier wird der Parameter überprüft und die Message CB_SETCURSEL an das Element gesendet. Der wParam Paremeter ist hier mal wieder der Index.

Wer bis jetzt genau aufgepasst hat hat festgestellt, dass wir immer diese get_num_entry Methode benutzen. Hier ist sie:

C-/C++-Quelltext

1
2
3
4
unsigned int Combo::get_num_entry()
{
    return static_cast<unsigned int>(SendMessageW (m_Element, CB_GETCOUNT, 0, 0));
}


Um an die anzahl der Einträge zu kommen reicht ein SendMessage aufruf mit der CB_GETCOUNT Message und die Funktion liefert die Anzahl.

Als letztes kommt jetzt noch die sehr wichtige Methode get_selected_entry_index, mit der wir den Index des gerade angewählten Eintrags in Erfahrung bringen können.

C-/C++-Quelltext

1
2
3
4
unsigned int Combo::get_selected_entry_index()
{
    return static_cast<unsigned int>(SendMessageW (m_Element, CB_GETCURSEL, 0, 0));
}


Für diesen Zweck verwendet man die CB_GETCURSEL Message, und man hat schon sein Ergebnis.

Listen:

Listen sind im prinzip genau das gleiche wie Comboboxen. Darum werden wir auch nur die create Methode besprechen, da die anderen Methoden genau gleich sind.

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
class List : public Element
{
public:

    // erstellt eine Liste 

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

    // fügt einen Listeneintrag hinzu

    unsigned int add_entry (wchar_t *pText);

    // Liefert den Index des gerade angewählten eintrags

    unsigned int get_selected_entry_index ();

    // Liefert die anzahl der einträge

    unsigned int get_num_entry ();

    // Leert die Liste

    unsigned long empty ();

    // Verknüpft einen Eintrag mit Daten

    template <typename T>
    unsigned long set_data (unsigned int Index, T Data);

    // Liefert die Daten eines Eintrags

    template <typename T>
    unsigned long get_data(unsigned int Index, T *pData);

    // Zerstört das Element 

    unsigned long destroy ();
};


Wie versprochen die create:

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
unsigned long List::create(unsigned int x, unsigned int y, 
                           unsigned int Width, unsigned int Height, 
                           HWND ParentWindow, unsigned long ID, 
                           HINSTANCE Instance, 
                           bool ThirdDimension, bool Sort)
{
    unsigned long ExStyle = NULL;
    unsigned long Style = WS_VISIBLE | WS_CHILD | LBS_NOTIFY | WS_VSCROLL | WS_BORDER;

    // Membervariablen initialisieren

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

    if (ThirdDimension)
    {
        ExStyle = WS_EX_CLIENTEDGE;
        Style = WS_VISIBLE | WS_CHILD | LBS_NOTIFY | WS_VSCROLL;
    }

    if (Sort)
        Style = Style | LBS_SORT;

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

    return OK;
}


Diese Prozedur mit den Styles kommt uns doch noch von der Edit Klasse bekann vor, oder?
Also der Standard Wert für die Style Variable ist hier eine 2D Liste mit Rahmen. Falls ThirdDimension jedoch true ist nehmen wir den Rahmen wieder raus, denn der sieht bei 3D Schlecht aus. Weiterhin setzten wir in diesem Fall auch noch den Exteded Style auf WS_EX_CLIENTEDGE. Falls Sort true ist wird die Liste Alphabetisch geordnet und der LBS_SORT Style wird angehängt.
Danach wird dann nur noch das Element mit CreateWindowEx erstellt und gut ist.

Das Problem mit den Radiobuttons:

Stellt euch vor, wir würden jetzt eine Gruppe Radiobuttons erstellen, die angibt welche Farbe ein Rand eines Quadrats hat und noch eine Gruppe Radiobuttons die angibt welche Farbe das innere des Quadrats hat und jeden Button dem Hauptfenster unterordnen.
Was soll passieren? Natürlich! Man sollte von jeder Gruppe einen Radiobutton auswählen können. Aber nix da! Man wird feststellen, dass man nur einen Button von beiden gruppen zusammen auswählen kann. Nun woran liegt das?
Nach längerem überlegen kommt man doch zu dem Schluss, dass das nur daran liegen kann, dass wir alle Buttons dem gleichen Fenster untergeordnet haben. Jetzt stellt sich die Frage wie wir das Lösen können.
Ganz einfach. Jeder hat doch sicherlich schonmal die Rahmen gesehen, die um solche Buttons immer Rum sind. Da hab ich mir gedacht, dass man die Buttons doch dem jeweiligen Rahmen unterordnen kann und siehe da es funktioniert.

Also machen wir einen kleinen Abstecher zu den Rahmen und kommen später auf die Radiobutons zurück.

Rahmen:

Zuerst die Klasse, dann das Vergnügen. :-)

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
class Frame : public Element
{
public:
    // Erstellt den Rahmen

    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);

    // Fügt einen Radiobutton hinzu

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

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

    // Liefert einen Radiobutton

    Element *get_element (unsigned long ID);

    // Zersört das Element

    unsigned long destroy ();

private:
    std::list <Element*> m_Elements;
    std::list <Element*>::iterator m_i;
};


Wie man sieht kann man später Radiobuttons und Checkboxen einem Rahmen zuweißen. Diese Elemente werden wieder in einer Liste gespeichert, damit man sie leicht verwalten kann.

Ein alter bekannter Freund: create

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
unsigned long Frame::create(wchar_t *pText, 
                            unsigned int x, unsigned int y, 
                            unsigned int Width, unsigned int Height, 
                            HWND ParentWindow, unsigned long ID, 
                            HINSTANCE Instance)
{
    // Membervariablen initialisieren

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

    // Rahmen erstellen

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

    return OK;
}


Nichts neues. Sollte jeder selber hinbekommen.

Also weiter. add_radio und add_check. Da beides das gleiche ist werden wir uns nur eine anschauen.

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
unsigned long Frame::add_radio(wchar_t *pText,
                               unsigned int x, unsigned int y,
                               unsigned int Width, unsigned int Height,
                               unsigned long ID)
{
    Radio *pRadio = new Radio;
    Element *pElement = NULL;

    unsigned long Result = OK;

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

    pElement = reinterpret_cast<Element*>(pRadio);

    m_Elements.push_back (pElement);

    return OK;
}


Hier ist es dasselbe wie bei der Window Klasse. Eine Instanz der Radio Klasse wird ezeugt. Danach wird der Radiobutton erstellt, gecastet und in die Liste geschoben.
Bei get_element wird jeder Eintrag der Liste mit dem der angegeben ID verglichen und falls man erfolgreich war wird der gefundene Eintrag zurückgeliefert. Falls dies nicht der Fall ist NULL.
Die Methode destroy kennen wir ebenfalls schon. Jeder Eintrag wird durchgegangen, je nach Typ gecastet und die jweilige destroy wird aufgerufen.

Jetzt können wir auch schon Rahmen erstellen und diesen Radio und Checkbuttons zuordnen. Jetzt brauchen wir nur noch was? – Natürlich die Klassen für diese beiden Elemente.

Radiobuttons und Checkboxen:

C-/C++-Quelltext

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
class Radio : public Element
{
public:
    // erstellt einen Radiobutton

    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);

    // Ist der Button gecheckt?

    bool is_checked ();

    // Checkt den Button

    unsigned long check (bool check);

    // Zerstört den Button

    unsigned long destroy ();

};


und noch die zweite:

C-/C++-Quelltext

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

    // erstellt einen Checkbutton

    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);

    // Ist der Button gecheckt?

    bool is_checked ();

    // Checkt den Button

    unsigned long check (bool check);

    // Zerstört den Button

    unsigned long destroy ();
};


Was fällt auf? Ist doch ganz klar: Bei der Radio klasse ist keine Leerzeile
nach public. Nein Scherz beiseite. Wie man sieht gibt es zwischen den Klassen keine änderungen. Und genau so sieht es auch bei den implementierungen aus. Nur dass bei der create ein Style – Flag anders ist als bei der anderen.

C-/C++-Quelltext

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
unsigned long Radio::create(wchar_t *pText, 
                            unsigned int x, unsigned int y, 
                            unsigned int Width, unsigned int Height, 
                            HWND ParentWindow, unsigned long ID, 
                            HINSTANCE Instance)
{
    // Membervariabeln initialisieren

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

    if (!(m_Element = CreateWindowW (L"BUTTON", pText, WS_CHILD | WS_VISIBLE | BS_AUTORADIOBUTTON, 
                               x, y, Width, Height, ParentWindow, reinterpret_cast<HMENU>(ID), Instance, NULL)))
    {
        return ERROR_INVALID_API_CALL;
    }

    return OK;
}


Hier werden zuerst die Membervariablen gesetzt und dann das Fenster erstellt. Übrigends heißt das Flag für eine Checkbox BS_AUTOCHECKBOX.

Die Klasse Window:

Natürlich gibt es auch Änderungen in der Window Klasse. Es gibt drei neue Methoden zum hinzufügen von Elementen:

C-/C++-Quelltext

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
// Fügt eine Combobox hinzu

    unsigned long add_combo (unsigned long x, unsigned long y,
                             unsigned long Width, unsigned long Height,
                             unsigned long ID);

    // Fügt eine Liste hinzu

    unsigned long add_list (unsigned long x, unsigned long y, 
                            unsigned long Width, unsigned long Height,
                            unsigned long ID, bool ThirdDimension = true,
                            bool Sort = false);

    // Fügt einen Rahmen hinzu 

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


Ich denke wir müssen diese Methoden nicht besprechen, denn es sind genau dieselben wie im Tutorial zuvor.
Bei der destroy – Methode gab es aber ein paar kleine Ä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
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_COMBO: reinterpret_cast<Combo*>((*m_i))->destroy(); break;
        case WIN_FRAME: reinterpret_cast<Frame*>((*m_i))->destroy(); break;
        case WIN_LIST: reinterpret_cast<List*>((*m_i))->destroy(); break;
        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 kamen einfach nur neue Verzweigungen hinzu, nämlich WIN_COMBO, WIN_FRAME und WIN_LIST.

Fertig:

So, jetzt geht dieses Tutorial auch so langsam zum Ende.

Ich habe mir überlegt, wenn euch diese Standard – Elemente nicht genug sind könnte man sich ja auch noch mit etwas Exotischeren Elementen beschäftigen und man könnte zum Beispiel auch noch eine Klasse schreiben für ein Menü, dass man dann später an die create eines Fensters übergibt oder so.

Aber für heute ist erstmal Schluss.

Anfänger



Anhang:

Beipielprogramm mit Projektmappe im zip – Format(26,73 KB)

Kapselung der Windowsoberfläche – Teil1
Ich gebe bei der Arbeit immer 100%

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

Anonymous

unregistriert

2

24.09.2006, 11:08

ich fänds schön wenn man die Elemente nicht auf nem Fenster programmiert sondern auf den Elementen selber. Also kein WM_COMMAND eines Buttons an das Parent, sondern an den Button und dort reagieren.

3

24.09.2006, 11:22

:?:

Das versteh ich net ganz. Kannst du mir das näher erläutern?
Ich gebe bei der Arbeit immer 100%

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

Anonymous

unregistriert

4

24.09.2006, 11:24

Wenn Du auf einen Button klickst, wo willst Du das verarbeiten? Auf dem Windowsfenster - was sehr groß werden kann, bei 20 Buttons - oder auf dem Element selber per Funktionspointer.

5

24.09.2006, 12:08

Achso du meinst, dass die Callback funktion des Hauptfensters zu groß wird und somit unübersichtlich. Aber wie setze ich soetwas um, dass es nicht so wird?
Ich gebe bei der Arbeit immer 100%

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

Anonymous

unregistriert

6

24.09.2006, 12:18

Es ist relativ einfach, aber auch relativ hilfreich!

Deine Element Dinger bzw. die Controls haben ja bestimmte Fensternachrichten die immer ans Papafenster gesendet werden.

Jetzt gibst du Deiner Elementklasse mehrere Funktionspointer, auf die Du Funktionen legst, die aufgerufen werden sollen, wenn etwas passiert. z. B. OnCommand.

Klickt man jetzt auf einen Button, geht die Fensternachricht (leider) direkt an das Papafenster. Dort wählst du dann mit deiner Liste das richtige Control aus und rufst dann den Funktionspointer auf ;)

Resultat: Du erstellst 3 Buttons, gibst jedem einen anderen Funktionspointer und fertig. Die Programmierung wird also nicht mehr in die WndProc gestopft wie verrückt und es ist übersichtlicher und "logischer".

7

24.09.2006, 12:27

Gut danke!

Werds implementieren und ein drittes Tutorial schreiben falls erwünscht.
Ich gebe bei der Arbeit immer 100%

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

Anonymous

unregistriert

8

24.09.2006, 12:29

Anfänger
Wäre gut, denn es erleichert die Programmierung enorm wenn man sich nicht mehr um die WndProc kümmern muss und es ist viel dynamischer.

Wenn Du ein 3. schreibst, achte mal etwas mehr auf Warnungen, ich hab ca. 15 Warnungen beim Compilieren.

David Scherfgen

Administrator

Beiträge: 10 382

Wohnort: Hildesheim

Beruf: Wissenschaftlicher Mitarbeiter

  • Private Nachricht senden

9

24.09.2006, 12:44

Re: [Tutorial]Kapselung der Windowsoberfläche - Teil 2

Zitat von »"Anfänger"«

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
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_COMBO: reinterpret_cast<Combo*>((*m_i))->destroy(); break;
        case WIN_FRAME: reinterpret_cast<Frame*>((*m_i))->destroy(); break;
        case WIN_LIST: reinterpret_cast<List*>((*m_i))->destroy(); break;
        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;
}

Das ist unschön. So ist das fehleranfällig, wenn du etwas erweitern willst, weil man leicht vergessen kann, das hier einzutragen. Virtuelle Methoden würden die ganze switch-Klausel überflüssig machen.

10

24.09.2006, 13:14

Du meinst, dass ich in der Element Klasse eine virtuelle destroy Methode einfügen sollte, oder?

@ nix da

Die Warnungen sind ja immer dieselben. Die enstehen ja weil ich meinen unsigned long in einen HMENU casten will und weil ich einmal einen LRESULT in bool caste.
Soll ich die dann immer deaktivieren und wieder aktivieren?
Ich gebe bei der Arbeit immer 100%

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

Werbeanzeige