|
Mangels einer TopIndex-Eigenschaft wie bei einer ListBox können Sie bei einem ListView- oder einem TreeView-Steuerelement aus den Microsoft Common Controls nicht so einfach festlegen, welches Element als oberstes angezeigt werden soll.
Mit einem kleinen Trick lässt sich das jedoch leicht erreichen. Denn sowohl ListItems als auch Nodes verfügen über die EnsureVisible-Methode. Diese sorgt dafür, dass ein ListItem bzw. ein Node in den sichtbaren Bereich des Steuerelements gerollt wird. Bei einem Node wird dazu auch noch gegebenenfalls der übergeordnete Zweig geöffnet. Allerdings erscheint das Element dann lediglich am unteren Rand des Steuerelements, und noch nicht wie gewünscht am oberen Rand. Der Trick besteht nun darin, dafür zu sorgen, dass zunächst das unterste Element sichtbar wird, und dann erst das gewünschte. Für ein ListItem könnte das etwa so aussehen:
With ListView1.ListItems
.Item(.Count).EnsureVisible
.Item(GewünschterTopIndex).EnsureVisible
End With
Da anders als bei einer einfachen ListBox ein Element nicht nur über einen Index, sondern auch über einen Schlüssel angesprochen werden kann, und da die Elemente selbst auch Objekte sind, wäre vielleicht eine Prozedur praktisch, bei der sich das gewünschte Element sowohl per Index oder Key als auch direkt angeben ließe. Als kleine Verfeinerung könnte das Element auch optional gleich markiert werden (Eigenschaft Selected = True).
Die folgende Funktion leistet dies für ein ListView-Steuerelement. Sie unterdrückt über die API-Funktion LockWindowUpdate auch das Flackern, das bei größeren zu "rollenden" Wegen auftreten könnte. Sie übergeben ihr das ListView-Steuerelement und im zweiten Parameter alternativ den Index, den Schlüssel oder direkt das ListItem, das zum "TopIndex" werden soll. Im dritten. optionalen Parameter Selected legen Sie fest, ob das ListItem auch gleich markiert werden soll.
Private Declare Function LockWindowUpdate Lib "user32" _
(ByVal hwndLock As Long) As Long
Public Sub LvwTopIndex(Lvw As ListView, TopIndex As Variant, _
Optional ByVal Selected As Boolean)
Dim nTopItem As ListItem
Dim nBottomItem As ListItem
With Lvw
.ListItems
Select Case True
Case IsObject(TopIndex)
If TypeName(TopIndex) = "IListItem" Then
Set nTopItem = TopIndex
Else
Err.Raise 5
End If
Case IsNumeric(TopIndex)
Select Case VarType(TopIndex)
Case vbInteger, vbLong
On Error Resume Next
Set nTopItem = .Item(TopIndex)
If Err.Number Then
Err.Raise Err.Number
End If
Case Else
Err.Raise 5
End Select
Case VarType(TopIndex) = vbString
On Error Resume Next
Set nTopItem = .Item(TopIndex)
If Err.Number Then
Err.Raise Err.Number
End If
Case Else
Err.Raise 5
End Select
Set nBottomItem = .Item(.Count)
End With
LockWindowUpdate .Parent.hWnd
If Not (nBottomItem Is nTopItem) Then
nBottomItem.EnsureVisible
End If
With nTopItem
.EnsureVisible
.Selected = Selected
End With
LockWindowUpdate 0&
End With
End Sub
Bei einem TreeView ist es allerdings ein wenig aufwändiger, das zunächst sichtbar zu machende unterste Element zu ermitteln. Denn die Index-Reihenfolge stimmt nicht unbedingt mit der vertikalen Reihenfolge der Knoten überein. Außerdem könnte sich der tatsächlich unterste Knoten nicht in einem geöffneten Zweig befinden. Es muss daher zu diesem Zweck der unterste geöffnete Knoten ermittelt werden.
Die folgende Prozedur TvwTopIndex ähnelt im wesentlichen der oben stehenden Prozedur LvwTopIndex und wird im Prinzip genauso aufgerufen.
Public Sub TvwTopIndex(Tvw As TreeView, TopIndex As Variant, _
Optional ByVal Selected As Boolean)
Dim nTopNode As Node
Dim nBottomNode As Node
Dim nNode As Node
With Tvw.Nodes
Select Case True
Case IsObject(TopIndex)
If TypeName(TopIndex) = "INode" Then
Set nTopNode = TopIndex
Else
Err.Raise 5
End If
Case IsNumeric(TopIndex)
Select Case VarType(TopIndex)
Case vbInteger, vbLong
On Error Resume Next
Set nTopNode = .Item(TopIndex)
If Err.Number Then
Err.Raise Err.Number
End If
Case Else
Err.Raise 5
End Select
Case VarType(TopIndex) = vbString
On Error Resume Next
Set nTopNode = .Item(TopIndex)
If Err.Number Then
Err.Raise Err.Number
End If
Case Else
Err.Raise 5
End Select
Hier wird nun zuerst der erste Knoten ermittelt, der keinen Elternknoten hat, also ein Knoten der obersten Ebene ist:
For Each nNode In Tvw.Nodes
If nNode.Parent Is Nothing Then
Exit For
End If
Next
Jetzt wird der letzte Knoten auf gleicher Ebene ermittelt - der am weitesten unten befindliche Knoten der obersten Ebene:
Set nBottomNode = nNode.LastSibling
Ist er expandiert, wird der erste Kindknoten ermittelt. Ist er nicht expandiert, oder gibt es keinen Kindknoten, bleibt er weiterhin der unterste Knoten. Gibt es Kindknoten, wird wiederum der unterste Knoten auf der Ebene des Kindknotens ermittelt. Die umgebende Schleife wird so lange ausgeführt, bis der unterste sichtbare Knoten ermittelt worden ist:
Do
If nBottomNode.Expanded Then
Set nNode = nBottomNode.Child
If nNode Is Nothing Then
Exit Do
Else
Set nBottomNode = nNode.LastSibling
End If
Else
Exit Do
End If
Loop
LockWindowUpdate Tvw.Parent.hWnd
If Not (nBottomNode Is Nothing) Then
If Not (nBottomNode Is nTopNode) Then
nBottomNode.EnsureVisible
End If
End If
With nTopNode
.EnsureVisible
.Selected = Selected
End With
LockWindowUpdate 0&
End With
End Sub
|