Seku
HomeProjekteTutorialsToolsGFXLinksForumGästebuchKontaktImpressumLogin
Stoppt die Vorratsdatenspeicherung - www.vorratsdatenspeicherung.de

tutorial anzeigen

Durch lesen dieses Artikels akzeptieren Sie folgende Nutzungsbedingungen:
Dieser Artikel und alle verwendeten Materialien (Bilder, Quelltexte, etc.) unterliegen dem Copyright des Autors, hier Seku. Das kopieren dieses Artikels, Auszügen oder verwendeten Materialien und Einfügen auf anderen Seiten als dieser (seku.info) ist nur mit einer ausdrücklichen, schriftlichen Einverständniserklärung des Autors erlaubt. Weder Autor noch der Betreiber dieser Seite haften für eventuelle Schäden, die durch diesen Artikel oder herunterladbare Objekte ( > Downloads ) verursacht wurden. Die Richtigkeit dieses Textes ist nicht garantiert.

TabCtrls

Guten Tag, im dritten Teil meines Tutorials über die WinApi, erkläre ich Tab Controls (künftig nur noch TabCtrls). Alle kennen sie, doch keiner benutzt sie - oft weil man nicht weiss, wie. Doch vorab will ich TabCtrls mit einem Bild veranschaulichen.

Ein Tab Control


Der äußere rote Rahmen umfasst das komplette TabCtrl. Der obeere kleine Kasten umfasst eine Registerkarte, ein Item oder ein Label innerhalb dieses TabCtrls. Klickt man auf so ein Item, verändert sich der Inhalt und etwas ganz anderes kommt zum Vorschein. Mit TabCtrls lässt sich also Platz sparen. Im Beispiel eines Einstellungsdialogs kann man so verschiedene Kategorien (Grafik, Sound, Sprache) trennen, ohne Verwirrung zu verursachen. Genug um uns das mal genauer anzuschauen.

Natürlich sind TabCtrls auch Fenster. Wir erstellen sie also mit CreateWindow(). TabCtrls benötigen allerdings CommonControls. Wir müssen also zuvor noch die Funktion "void InitCommonControls(void);" aufrufen (und "CommCtrl.h" einfügen und "comctl32.lib" linken). Und dann können wir auch schon das Fenster erstellen. Die Fensterklasse ist gespeichert in dem Makro WC_TABCONTROL.

Und schon haben wir ein TabCtrl, doch es fehlen noch die Registerkarten. Und die können wir einzeln hinzufügen. Wir senden einfach eine Nachricht an unser TabCtrl uns sagen ihm, dass wir einen Eintrag machen wollen. Die Nachricht heißt TCM_INSERTITEM. WParam ist die Position, wo die Registerkarte eingefügt werden soll (0 ist erster Eintrag also ganz links, 1 ist nach dem Ersten, usw.). LParam ist ein Pointer auf eine TCItem-Struktur.

struct TCItem
{
    unsigned int            mask;
    DWORD                   dwState;
    DWORD                   dwStateMask;
    char*                   pszText;
    int                     cchTextMax;
    int                     iImage;
    LPARAM                  lParam;
};

  • mask: Dieser Wert gibt an, welche anderen Variablen beachtet werden.
  • dwState: In unserem Fall kann diese Variable 0 sein
  • dwStateMask: In unserem Fall kann diese Variable 0 sein
  • pszText: Wenn das Flag TCIF_TEXT in mask gesetzt wurde, können wir hier die Aufschrift der Registerkarte festlegen
  • cchTextMax: Die Textlänge von pszText. Natürlich auch nur bei TCIF_TEXT in mask
  • iImage: Ist das Flag TCIF_IMAGE gesetzt, so wird als "Aufschrift" der Registerkarte das Bild genommen, welches in der Bilderliste des TabCtrls diesen Index hat. Mehr dazu unten
  • lParam: Für uns 0.


Gut. Wir wissen nun alles Wichtige über TabCtrls. Und deshalb wollen wir nun ein kleines Beispielprogramm schreiben. Nicht vergessen: InitCommonControls() aufrufen, die Headerdatei einfügen und die Library linken.
Den kompletten Code gibt es unten zu downloaden

// [...]

// Globale Variablen
HINSTANCE  g_hInstance;      // Die Programminstanz
HWND       g_hTabCtrl;       // Das TabCtrl
char*      g_apcLabels[] = { // Die Beschriftungen
                             "Grafikoptionen",
                             "Soundoptionen",
                             "Sprachoptionen",
                             "Keine Optionen"
                           };

// [...]

// Die Fensterfunktion
LRESULT CALLBACK WindowProc(HWND hWindow, unsigned int uiMsg, WPARAM WParam, LPARAM LParam)
{
    TCITEM    TabCtrlItem;
    RECT      Rect;
    int       iItem;

    // Nachricht verarbeiten
    switch(uiMsg)
    {
        // [...]

        case WM_CREATE:
            // Das Fenster wird erstellt
            // TabCtrl erstellen
            g_hTabCtrl = CreateWindow(WC_TABCONTROL,
                                      "",
                                      WS_CHILD | WS_CLIPSIBLINGS | WS_VISIBLE,
                                      20, 20,
                                      Rect.right-40, Rect.bottom-40,
                                      hWindow,
                                      NULL,
                                      g_hInstance,
                                      NULL);
            if(!g_hTabCtrl)
            {
                // Fehler!
                MessageBox(hWindow, "Fehler beim"
                                    "Erstellen des TabCtrls",
                                    "Fehler", MB_OK | MB_ICONERROR);
                return FALSE;
            }

            // 4 Registerkarten hinzufügen
            for(iItem = 0; iItem < 4; iItem++)
            {
                // Struktur ausfüllen
                TabCtrlItem.mask         = TCIF_TEXT;
                TabCtrlItem.pszText      = g_apcLabels[iItem];
                TabCtrlItem.cchTextMax   = strlen(g_apcLabels[iItem]);

                // Hinzufügen
                if(SendMessage(g_hTabCtrl, TCM_INSERTITEM, (WPARAM)iItem, (LPARAM)&TabCtrlItem) == -1)
                {
                    // Fehler!
                    MessageBox(hWindow, "Fehler beim Hinzufügen"
                                        "einer Registerkarte zu TabCtrl",
                                        "Fehler", MB_OK | MB_ICONERROR);
                    return FALSE;
                }
            }

        // [...]
        }

    return DefWindowProc(hWindow, unsigned int uiMsg, WParam, LParam);
}

Wenn wir das Fenster starten, sehen wir ein wunderschönes TabCtrl. Hier ein Screenshot

Unser Beispielprogramm


Der Inhalt

Nun, wir haben ein TabCtrl. Wir können hin- und herschalten. Doch wie wechseln wir den Inhalt, wenn die Registerkarte gewechselt wird? Dazu wenden wir einen Trick an. Wir erstellen für jede Registerkarte einen Dialog (oder bei einzelnen Elementen eben jene) mit seinem Inhalt. Im Dialogeditor setzen wir bei allen das Flag für ein Child-Window. Am besten entfernen wir noch den Rand für die Dialoge. Nun erstellen wir nach dem Erstellen des TabCtrls alle Dialoge mit CreateDialog() (nicht DialogBox, weil DialogBox ein BlockingCall ist - sprich, wartet bis der Dialog beendet wird). Dann bringen wir sie nach oben, damit sie nicht hinter dem TabCtrl angezeigt werden, sprich verdeckt sind. Dazu verwenden wir die Funktion BringWindowToTop(HWND). Wenn sich nun die Auswahl des TabCtrls ändert, verstecken wir den bisherige angezeigten Dialog und zeigen den der ausgewählten Registerkarte an. Und voilà, es klappt. Jedoch nicht vergessen, den ChildDialog mittels MoveWindow() an die richtige Stelle zu bewegen.

Bleibt nur noch zu klären, wie wir mitbekommen, wann beim TabCtrl eine andere Registerkarte ausgewählt wurde. Das TabCtrls sendet in diesem Falle eine WM_NOTIFY-Message an sein ParentWindow. LParam ist der Pointer auf eine NMHDR-Struktur. Diese hat für uns zwei Variablen. "HWND hWndFrom" - Das Handle zu unserem TabCtrl zur Unterscheidung. Und "unsigned int code" - Die Nachricht was los ist. In Unserem Fall ist letzteres TCN_SELCHANGE. Nun können wir das Makro TabCtrl_GetCurSel(HWND) anwenden oder dem TabCtrl die Nachricht TCM_GETCURSEL senden. In beiden Fällen wird der Index der ausgewählten Registerkarte zurückgegeben oder -1, wenn keine Registerkarte ausgewählt ist.
Hui, das war ein ganzer Batzen Theorie. Am Besten, wir veranschaulichen das ganze mal mit einem Beispiel.
Den kompletten Code gibt es unten zu downloaden

// --------------------------------------------------
// Bei den globalen Variablen
// --------------------------------------------------
HWND    g_hChilds[4];

// --------------------------------------------------
// In der Schleife welche die Registerkarten erstellt
// --------------------------------------------------

// [...]

// Gleichzeitig laden wir noch das Fenster
g_hChilds[iItem] = CreateDialog(g_hInstance,
                                MAKEINTRESOURCE(ID_GRAPHIC+iItem),
                                hWindow, /* Bei einem Child Wichtig! */
                                ChildDlgProc);
MoveWindow(g_hChilds[iItem], 35, 45,  Rect.right-65, Rect.bottom-75, TRUE); // An die richtige Stelle bewegen
BringWindowToTop(g_hChilds[iItem]);           // In den Vordergrund bringen
ShowWindow(g_hChilds[iItem], SW_HIDE);        // Verstecken

// --------------------------------------------------
// Direkt außerhalb der Schleife
// --------------------------------------------------

// Ersten Eintrag anzeigen
ShowWindow(g_hChilds[0], SW_SHOW);

// --------------------------------------------------
// In der WindowProc des Hauptfensters
// --------------------------------------------------
switch(uiMsg)
{
    // [...]

   case WM_NOTIFY:
       // Notification.. ist sie von unserem TabCtrl?
       // Gleichzeitig prüfen, wir noch ob sich die Auswahl geändert hat
       if(((NMHDR*)LParam)->hwndFrom == g_hTabCtrl && ((NMHDR*)LParam)->code == TCN_SELCHANGE)
       {
           // Ja, wir zeigen den entsprechenden Dialog an
           for(iItem = 0; iItem < 4; iItem++)
               ShowWindow(g_hChilds[iItem], SW_HIDE);   // Alle (anderen) Dialog verstecken

           // Und unseren anzeigen
           ShowWindow(g_hChilds[TabCtrl_GetCurSel(g_hTabCtrl)], SW_SHOW);
       }
       break;

    // [...]
}

Denkt wieder an den Header und an die Bibliothek!
Hier gibt es den Sourcecode der Hauptdatei zu downloaden: TabCtrl.cpp (Copy&Paste fähig).

Ende

Ja, das Tutorial ist zuende. Ich hoffe alles ist klar. Ansonsten schickt mir eine Mail, oder benutzt das Forum (welches bald da sein wird^^).




Alle Inhalte stehen, wenn nicht anders angegeben, unter dem Copyright von Seku.
Für die Inhalte externer Seiten ist der jeweilige Betreiber verantwortlich.
seku.info v.3.2, Benötigte Zeit: 0,128 Sekunde(n)
Ausgeführte mySQL-Anfragen: 0
17:28