• Keine Ergebnisse gefunden

4. CommonControls

4.11. Tabsheets

4.11.1. Vorbereiten der Ressourcen

Ich habe mich für das Beispiel für Dialogressourcen entschieden, da es so etwas einfacher wird. Wir brauchen nämlich für jeden Tabsheet ein Fenster bzw. einen Dialog als Container für die jeweiligen Controls. Und dies lässt sich mit Dialogen einfacher bewerkstelligen und ist im Code dann auch übersichtlicher. Wenn Sie auf Dialoge verzichten und Fenster benutzen wollen, dann lautet der Klassenname, den Sie bei "CreateWindowEx" angeben müssen, SysTabcontrol32.

Zusätzlich weisen wir dann im Code jedem "Tabsheet-Dialog" die gleiche Dialog-Prozedur zu, um alle Klicks usw. an einem Ort behandeln zu können.

In dem Ressourcenskript des Beispiels habe ich drei Dialoge definiert: Einen für das Hauptfenster (ID: 100) und zwei mit den Controls für die Tabsheets (ID: 200 und 300). Wichtig ist nun, dass die Dialoge für die Tabsheets die Eigenschaft

"Steuerelement" gesetzt haben, damit sie wie Steuerelemente reagieren - sprich: damit man mittels TAB-Taste von einem Reiter zum nächsten springen kann. Im Code des Ressourcenskriptes ist das die fett markierte Einstellung:

200 DIALOG DISCARDABLE 0, 0, 145, 95 STYLE DS_CONTROL | WS_CHILD

FONT 8, "MS Sans Serif"

BEGIN

EDITTEXT 201,10,15,115,12,ES_AUTOHSCROLL EDITTEXT 202,10,35,115,12,ES_AUTOHSCROLL PUSHBUTTON "Button1",203,60,60,65,20

END

4.11.2. Die Tabsheets erzeugen

Die einzelnen Tabsheets müssen zur Laufzeit dynamisch im Code erzeugt werden. Weil der Quellcode mit einem Tabsheet-Control und mehreren Registerseiten leicht unübersichtlich werden kann, ist es wichtig, von Anfang an effizienten und leicht zu wartenden Code zu schreiben. Aus diesem Grund erzeuge ich die einzelnen Tabsheets in einer Schleife:

for i := 0 to CNT_TABS - 1 do begin

In dieser Schleife wird ein paar der benötigten Membervariablen des TCItem-Records gefüllt. Hier ist der Text des Reiters ausreichend, der in pszText angegeben wird. Damit er auch akzeptiert wird, ist die mask-Variable vorher auf TCIF_TEXT zu setzen:

tcItem.mask := TCIF_TEXT;

tcItem.pszText := pointer(tabSheetCaptions[i]);

Das Record wird dann als lParam-Wert mit der Nachricht "TCM_INSERTITEM" an das Tabsheet-Control übergeben:

SendMessage(hTab, TCM_INSERTITEM, i, Integer(@tcItem));

end;

Das TCItem-Record enthält natürlich noch ein paar andere Membervariablen: Beispiele sollen stellvertretend die List-View und der Tree-View genannt werden. In aller Kürze: mask gibt an, welche Membervariablen Gültigkeiten haben. pszText ist logischerweise der Text der Seitenreiter, und cchTextMax ist die Anzahl der Textzeichen (diese Variable wird aber nur beim Auslesen der Beschriftungen benötigt). iImage ist der Index eines Bildes einer Imageliste.

Bleiben noch dwState und dwStateMask. Diese beiden Membervariablen sind von der Version des Internet Explorer abhängig. Wie man an der bedingten Compilierung erkennen kann, ist der IE3 das Minimum, um auf die beiden Variablen zugreifen zu können. Das bedeutet, lediglich unter Windows 95 und NT stehen Ihnen diese beiden Variablen nicht zur Verfügung, sondern wären dort durch reservierte (und damit nicht benutzbare) UINT-Variablen ersetzt.

TCM_INSERTITEM-Definition

TCM_INSERTITEM

wParam = (WPARAM) (int) iItem;

lParam = const (LPARAM) (LPTCITEM) pitem;

4.11.3. Tabsheet-Dialoge laden und anzeigen

Es verbleiben noch zwei Schritte: Für jedes Tabsheet einen Dialog aus dem Ressourcenskript laden und den Dialog des ersten Tabsheets anzeigen. Ersteres geschieht wieder in einer Schleife:

for i := 0 to length(hTabDlgs) - 1 do

hTabDlgs[i] := CreateDialog(hInstance, MAKEINTRESOURCE((i + 2) * 100), hDlg, @tabdlgfunc);

Hier werden also die Dialoge aus der Ressource geladen (clevererweise haben sie die IDs 200 und 300) und einer Dialogprozedur zugewiesen. Man kann natürlich auch jedem Dialog eine separate Dialogprozedur zuweisen, aber das ist Geschmackssache.

Zu guter Letzt wollen wir noch den Dialog für den ersten Tabsheet im selbigen anzeigen. Dazu müssen wir dessen Position und Größe ermitteln, was wir mit Hilfe der Nachricht "TCM_GETITEMRECT" tun. Der wParam gibt den Index des Tabsheets an, dessen Position wir benötigen und dem lParam wird ein Zeiger auf ein TRect-Record übergeben, die dann die Position enthält.

SendMessage(hTab, TCM_GETITEMRECT, 0, Longint(@rect));

Der Funktionsaufruf von "SetWindowPos" sollte keine Probleme bereiten. Er sorgt dafür, dass die jeweilige Registerseite an der richtigen Position erscheint:

SetWindowPos(hTabDlgs[0], 0, 50, (rect.Bottom - rect.Top) + 50, 0, 0, SWP_NOSIZE or SWP_NOZORDER or SWP_SHOWWINDOW);

Eine kleine Anmerkung meinerseits: +50, weil sich meine Dialogelemente nicht am oberen und linken Rand des Dialogfensters befinden.

4.11.4. Wechseln des Seitenreiters

Zuerst einmal das Prinzip: Wie schon gesagt hat jeder Tabsheet ein Dialogfenster als Container für die Controls. Das erspart uns die Arbeit, bei einem Wechsel des Seitenreiters alle Elemente, die sich auf einem Tabsheet befinden, zu verwalten. Wird also nun der Tabsheet gewechselt, gehen wir alle Dialogfenster durch und gucken, was mit ihnen geschen muss: anzeigen oder verbergen. Damit man sieht, wovon ich rede, hier der entsprechende Ausschnitt aus dem Code:

// which tabsheet is selected

index := SendMessage(hTab, TCM_GETCURSEL, 0, 0);

Mit der Nachricht "TCM_GETCURSEL" ermitteln wir, welcher Tabsheet angeklickt wurde. Die Nachricht hat keine Parameter und liefert uns den Index des Tabsheets zurück.

Dann gehen wir in einer Schleife alle Dialogfenster durch und machen alle unsichtbar, die zu dem nicht ausgewählten Tabsheet gehören - und das Fenster, das zum Tabsheet gehört, wird natürlich sichtbar gemacht:

// enum all available tabsheets

for i := 0 to length(hTabDlgs) - 1 do begin

// number of tabsheet does not equal selected tabsheet -> hide it if i <> index then

ShowWindow(hTabDlgs[i], SW_HIDE) else // else make dialog visible begin

SendMessage(hdr^.hwndFrom, TCM_GETITEMRECT, i, Longint(@rect));

SetWindowPos(hTabDlgs[index], 0, 50, (rect.Bottom - rect.Top) + 50, 0, 0, SWP_NOSIZE or SWP_NOZORDER or SWP_SHOWWINDOW);

end;

end;

Wie bekommen wir nun mit, dass ein Tabsheet angeklickt wurde? Das ist recht einfach: klickt der Benutzer einen Seitenreiter an, bekommen wir eine "WM_NOTIFY"-Nachricht. In der NMHDR-Struktur erhalten wir als Benachrichtigungscode "TCN_SELCHANGE" für einen Klick auf einen Seitenreiter:

WM_NOTIFY:

begin

hdr := PNMHDR(lParam);

case hdr^.code of TCN_SELCHANGE:

// tabsheet selection has changed { ... }

end;

end;

Im Dokument Win32API-Tutorials für Delphi (Seite 158-161)