|
Das TabStrip-Steuerelement aus den Microsoft Common Controls
sorgt selbst lediglich für die Verwaltung und Darstellung der
Registerkartenköpfe bzw. Button-Leiste. Für die Umschaltung der
Inhalte der Registerkarten und für die Darstellung und
Positionierung der Steuerelemente auf den einzelnen Registerkarten
müssen Sie selbst sorgen. Am besten fassen Sie alle diejenigen
Steuerelemente, die auf jeweils einer Registerkarte erscheinen
sollen, in einem anderen Container-Steuerelement zusammen, etwa in
einem Frame, einer PictureBox oder in einem UserControl. Dann
brauchen Sie beim Wechsel einer Registerkarte nur noch den zuvor
aktuellen Container unsichtbar und den aktuellen Container sichtbar
zu machen. Und bei einer Größenänderung des
TabStrip-Steuerelements passen Sie diese Container an die neue
Größe und Position des Client-Bereichs des TabStrip-Steuerelements
(Eigenschaften ClientLeft, ClientTop, ClientWidth, ClientHeight) an.
In der Einstellung (Eigenschaft) "Style = 0 - tabTabs"
ist das nicht sonderlich schwierig. Beim Wechsel einer Registerkarte
wird zunächst das BeforeClick-Ereignis ausgelöst, während die
Eigenschaft SelectedItem noch auf die alte Registerkarte verweist.
Hier setzen Sie die Visible-Eigenschaft des zugehörigen Containers
auf False. Im unmittelbar darauffolgenden Click-Ereignis verweist
die Eigenschaft SelectedItem auf die neue Registerkarte, so dass Sie
hier nun die Visible-Eigenschaft des hierzu gehörenden Containers
auf True setzen können. In den beiden anderen Einstellungen der
Style-Eigenschaft wird das BeforeClick-Ereignis jedoch nicht
ausgelöst, so dass Sie selbst nachhalten müssen, welche
Registerkarte (und welcher dazugehörende Container) vor dem
Eintreffen des Click-Ereignisses aktiv gewesen war. In beiden
Fällen aber müssen Sie die Zuordnung der Container zu einer
Registerkarte selbst verwalten.
Beim TabStrip-Steuerelement aus den Microsoft Common Controls der
Version 6 können Sie das jeweilige
Container-Steuerelement der Tag-Eigenschaft einer Registerkarte
(Tab) zuweisen. Zur Deaktivierung bzw. Aktivierung des zu einer
Registerkarte gehörenden Containers greifen dann einfach auf die
Tag-Eigenschaft der Registerkarte zu.
Da Sie in der Version 5 der Tag-Eigenschaft leider
keine Objekte zuweisen können, behelfen Sie sich hier mit der
Zuweisung des Steuerelement-Namens (Eigenschaft Name) des jeweiligen
Containers an die Tag-Eigenschaft einer Registerkarte. Zur
Deaktivierung bzw. Aktivierung des zu einer Registerkarte
gehörenden Containers ermitteln Sie dann diesen Container anhand
des in der Tag-Eigenschaft abgelegten Namens aus der
Controls-Collection des Forms (Parent), auf dem das
TabStrip-Steuerelement platziert ist. Allerdings können Sie in
diesem Fall keine Container aus einem Steuerelement-Array verwenden,
da Sie dazu noch die Index-Eigenschaft ermitteln und einbeziehen
müssten - das ist nicht ganz so einfach. Doch meines Erachtens
erübrigt sich die Berücksichtigung von Control-Arrays, da
Registerkarten wohl immer verschiedene Inhalte zeigen und es daher
wenig Sinn macht, diese überhaupt als Control-Arrray anzulegen.
Die Aufgabe der Zuordnung von Containern zu Registerkarten beim
Laden eines Forms oder beim späteren, nachträglichen Hinzufügen
von Registerkarten können Sie in Hilfsfunktionen bzw. -Prozeduren
in ein Standard-Modul verlagern und für alle
TabStrip-Steuerelemente im gleichen Projekt verwenden. Zwar müssen
Sie für jedes TabStrip-Steuerelement separat die jeweils zuletzt
aktive Registerkarte verwalten, so dass eigentlich die Verwendung
einer instanzierbaren Klasse in Betracht käme. Doch mit einem
kleinen Trick können Sie die zuletzt aktiven Registerkarten auch
für alle TabStrip-Steuerelemente gleichzeitig in einer Collection
in dem Standard-Modul verwalten: Sie legen einfach den Schlüssel
(Key) der jeweiligen Registerkarte unter einem für jedes
TabStrip-Steuerelement eindeutigen Schlüssel in der Collection ab.
Da es wahrscheinlich ist, dass die verschiedenen
TabStrip-Steuerelemente eines Projekts über mehrere Forms verstreut
sind, ist der Name eines TabStrip-Steuerelements nicht unbedingt
eindeutig - er kann ja auf verschiedenen Forms gleich lauten.
Eindeutig ist hingegen der Objekt-Zeiger eines
TabStrip-Steuerelements, den Sie über die nicht dokumentierte
Visual Basic-Funktion ObjPtr erhalten und in einen String
konvertiert (CStr) als Schlüssel in der Collection verwenden
können.
Betrachten wir nun die einzelnen Hilfsfunktionen bzw. -Prozeduren
zur Verwaltung der ganzen Angelegenheit für die Version 6.
Die Funktionen und Prozeduren für die Version 5
unterscheiden sich nur unwesentlich (das Beispiel und das Modul zu Version
5 finden Sie in der Download-Datei zu diesem Artikel). Zum
einen ist wie oben beschrieben die Zuweisung zur Tag-Eigenschaft
etwas anders, zum anderen fehlt bei den Registerkarten der Version
5 die Möglichkeit der zusätzlichen Markierung (Highlight).
Und in Visual Basic 5 können Sie noch keine beliebigen
Steuerelemente dynamisch nachladen - wozu wir das brauchen, werden
Sie gleich erfahren.
Zunächst sehen wir uns die Funktion TabStripAddContainer an.
Über diese Funktion erledigen Sie die Zuordnung eines
Container-Steuerelements zu einer Registerkarte und legen beim
allerersten Aufruf die besagte Collection zur Verwaltung der zuletzt
aktiven Registerkarten an. Sie übergeben das betreffende
TabStrip-Steuerelement, den betreffenden Container und den
Schlüssel oder Index der dazu gehörenden Registerkarte. Optional
können Sie im Parameter Highlighting noch angeben, ob die
Markierungsfähigkeit der Registerkarten mit verwaltet werden soll.
Die API-Funktion LockWindowUpdate
sorgt dafür, dass die Darstellung des Forms, auf dem sowohl das
TabStrip-Steuerelement als auch das/die Container-Steuerelement(e)
platziert sind, während der Zuordnung eingefroren wird.
Als kleines Extra können Sie bei der Version für Visual Basic
6 anstelle eines bereits existierenden
Container-Steuerelements im Parameter Container auch die ProgId und
einen Namen für ein dynamisch nachzuladendes Steuerelement
(externes OCX oder ein UserControl im gleichen Projekt) angeben. Als
Rückgabewert der Funktion erhalten Sie eine Referenz auf das
übergebene bzw. auf das nachgeladene Container-Steuerelement.
Neben der Zuordnung von Registerkarte und Container-Steuerelement
erfolgt auch gleich die Positionierung des Container-Steuerelements
im Client-Bereich des TabStrip-Steuerelements. Falls der angegebene
Registerkarten-Schlüssel mit dem Schlüssel der bereits aktiven
Registerkarte übereinstimmt, wird das Container-Steuerelement über
einen Aufruf der TabStripClick-Prozedur (auf diese gehen wir weiter
unten noch ein) auch gleich sichtbar gemacht.
Private Declare Function LockWindowUpdate Lib "user32" _
(ByVal hwndLock As Long) As Long
Private mLastTabs As Collection
Public Function TabStripAddContainer(TabStrip As TabStrip, _
Container As Variant, KeyIndex As Variant, _
Optional Highlighting As Boolean) As Object
Dim nContainer As Control
If mLastTabs Is Nothing Then
Set mLastTabs = New Collection
End If
With TabStrip
On Error Resume Next
LockWindowUpdate .Parent.hWnd
On Error GoTo 0
If IsObject(Container) Then
If TypeOf Container Is Control Then
Set nContainer = Container
Else
Exit Function
End If
ElseIf IsArray(Container) Then
Set nContainer = .Parent.Controls.Add(Container(0), _
Container(1))
Else
Exit Function
End If
Set nContainer.Container = .Container
Set .Tabs(KeyIndex).Tag = nContainer
nContainer.Move .ClientLeft, .ClientTop, .ClientWidth, _
.ClientHeight
If .SelectedItem Is .Tabs(KeyIndex) Then
TabStripClick TabStrip, Highlighting
Else
nContainer.Visible = False
End If
LockWindowUpdate 0&
End With
Set TabStripAddContainer = nContainer
End Function
Soll eine Registerkarte aus dem TabStrip-Steuerelement entfernt
werden, muss auch das dazu gehörende Container-Steuerelement
unsichtbar gemacht werden. Falls dieses dynamisch nachgeladen worden
war, sollte es auch gleich entladen werden. Die Prozedur
TabStripRemove hebt die Zuordnung von Registerkarte und
Container-Steuerelement auf und entfernt zugleich die Registerkarte.
Sie übergeben das TabStrip-Steuerelement und den Schlüssel oder
Index der zu entfernenden Registerkarte. Optional können Sie im
Parameter ActivateKeyIndex angeben, welche Registerkarte danach
aktiviert werden soll. Geben Sie hier nichts an, bleibt entweder die
aktive Registerkarte weiterhin aktiv, oder es wird versucht, die
zuvor aktive Registerkarte zu aktivieren. Misslingt dies, wird
versucht, die erste Registerkarte (Index 1) zu
aktiveren. Setzen Sie den letzten optionalen Parameter
RemoveContainer auf True, wird nach Aufhebung der Zuordnung
versucht, das Container-Element zu entladen - das gelingt natürlich
nur, wenn dieses auch zuvor dynamisch geladen worden war.
Public Function TabStripRemoveTab(TabStrip As TabStrip, _
KeyIndex As Variant, _
Optional ByVal ActivateKeyIndex As Variant, _
Optional ByVal RemoveContainer As Boolean) As Object
Dim nTab As MSComctlLib.Tab
Dim nContainer As Control
Dim nActivateKeyIndex As Variant
Dim nObjKey As String
nObjKey = CStr(ObjPtr(TabStrip))
With TabStrip
On Error Resume Next
LockWindowUpdate .Parent.hWnd
On Error GoTo 0
Set nTab = .Tabs(KeyIndex)
If IsMissing(ActivateKeyIndex) Then
If Not (.SelectedItem Is Nothing) Then
If nTab Is .SelectedItem Then
If Not (mLastTabs Is Nothing) Then
On Error Resume Next
nActivateKeyIndex = mLastTabs(nObjKey)
If Err.Number = 0 Then
mLastTabs.Remove nObjKey
Else
nActivateKeyIndex = .SelectedItem.Key
End If
On Error GoTo 0
End If
End If
End If
End If
If IsEmpty(nActivateKeyIndex) Then
If Not (.SelectedItem Is Nothing) Then
nActivateKeyIndex = .SelectedItem.Key
End If
End If
On Error Resume Next
Set nContainer = nTab.Tag
If Not (nContainer Is Nothing) Then
nContainer.Visible = False
If RemoveContainer Then
.Parent.Controls.Remove nContainer.Name
Else
Set TabStripRemoveTab = nContainer
End If
End If
.Tabs.Remove KeyIndex
Err.Clear
.Tabs(nActivateKeyIndex).Selected = True
If Err.Number Then
If .Tabs.Count Then
.Tabs(1).Selected = True
End If
End If
LockWindowUpdate 0&
End With
End Sub
Sind die Container-Steuerelemente den Registerkarten zugeordnet,
wird deren Anpassung nach einer Änderung der Größe und Position
des TabStrip-Steuerelements an dessen Client-Bereich einfach. Sie
durchlaufen alle Registerkarten, ermitteln über deren
Tag-Eigenschaften die betreffenden Container-Steuerelemente, und
positionieren diese entsprechend neu.
Public Sub TabStripResize(TabStrip As TabStrip, _
Optional ByVal Left As Variant, _
Optional ByVal Top As Variant, _
Optional ByVal Width As Variant, _
Optional ByVal Height As Variant)
Dim nLeft As Single
Dim nTab As MSComctlLib.Tab
Dim nControl As Control
With TabStrip
If IsMissing(Left) Then
nLeft = .Left
Else
nLeft = Left
End If
On Error Resume Next
.Move nLeft, Top, Width, Height
For Each nTab In .Tabs
With nTab
If IsObject(.Tag) Then
If TypeOf .Tag Is Control Then
Set nControl = .Tag
End If
End If
End With
If Not (nControl Is Nothing) Then
nControl.Move .ClientLeft, .ClientTop, .ClientWidth, _
.ClientHeight
End If
Next
End With
End Sub
Nun folgt noch die Prozedur TabStripClick, die den Wechsel der
Registerkarten verwaltet. Sie rufen Sie im Click-Ereignis des
TabStrip-Steuerelements auf und übergeben wieder dieses
TabStrip-Steuerelement. Optional können Sie hier wieder angeben, ob
das Higlighting berücksichtigt werden soll.
Public Sub TabStripClick(TabStrip As TabStrip, _
Optional Highlighting As Boolean)
Dim nPrevTabKey As String
Dim nObjKey As String
nObjKey = CStr(ObjPtr(TabStrip))
With TabStrip
On Error Resume Next
LockWindowUpdate .Parent.hWnd
On Error GoTo 0
If Not (mLastTabs Is Nothing) Then
On Error Resume Next
nPrevTabKey = mLastTabs(nObjKey)
If Err.Number = 0 Then
mLastTabs.Remove nObjKey
End If
On Error GoTo 0
End If
If Not (.SelectedItem Is Nothing) Then
With .SelectedItem
If IsObject(.Tag) Then
If TypeOf .Tag Is Control Then
With .Tag
.Visible = True
.ZOrder 0
End With
End If
End If
If Highlighting Then
.HighLighted = True
End If
mLastTabs.Add .Key, nObjKey
End With
If Len(nPrevTabKey) Then
If .SelectedItem.Key <> nPrevTabKey Then
With .Tabs(nPrevTabKey)
If IsObject(.Tag) Then
If TypeOf .Tag Is Control Then
.Tag.Visible = False
End If
End If
If Highlighting Then
.HighLighted = False
End If
End With
End If
End If
End If
LockWindowUpdate 0&
End With
End Sub
|