ABOUT Visual Basic Programmieren Programmierung Download Downloads Tips & Tricks Tipps & Tricks Know-How Praxis VB VBA Visual Basic for Applications VBS VBScript Scripting Windows ActiveX COM OLE API ComputerPC Microsoft Office Microsoft Office 97 Office 2000 Access Word Winword Excel Outlook Addins ASP Active Server Pages COMAddIns ActiveX-Controls OCX UserControl UserDocument Komponenten DLL EXE
Diese Seite wurde zuletzt aktualisiert am 11.05.2001

Diese Seite wurde zuletzt aktualisiert am 11.05.2001
Aktuell im ABOUT Visual Basic-MagazinGrundlagenwissen und TechnologienKnow How, Tipps und Tricks rund um Visual BasicActiveX-Komponenten, Controls, Klassen und mehr...AddIns für die Visual Basic-IDE und die VBA-IDEVBA-Programmierung in MS-Office und anderen AnwendungenScripting-Praxis für den Windows Scripting Host und das Scripting-ControlTools, Komponenten und Dienstleistungen des MarktesRessourcen für Programmierer (Bücher, Job-Börse)Dies&Das...

Themen und Stichwörter im ABOUT Visual Basic-Magazin
Code, Beispiele, Komponenten, Tools im Überblick, Shareware, Freeware
Ihre Service-Seite, Termine, Job-Börse
Melden Sie sich an, um in den vollen Genuss des ABOUT Visual Basic-Magazins zu kommen!
Informationen zum ABOUT Visual Basic-Magazin, Kontakt und Impressum

Zurück...

Navigation im ListView

Zurück...


Anzeige

(-hg) mailto:hg_lvwscroll@aboutvb.de

Leider bietet das ListView-Steuerelement aus den Microsoft Common Controls so gut wie keine Möglichkeiten zum Scrollen und zur Navigation per Code. Zwar können Sie sicherstellen, dass ein ListItem in den sichtbaren Bereich gerollt wird, indem Sie dessen Selected-Eigenschaft auf True setzen. Doch ist die Änderung des Selektionszustandes sicher nicht immer erwünscht. Die Methode EnsureVisible eines ListItems vermeidet zwar diese Änderung, dennoch haben Sie keine genaue Kontrolle über die Position dieses ListItems im sichtbaren Bereich. Die horizontale Rollposition entzieht sich dabei gänzlich Ihrer Kontrolle - die Sichtbarkeit eine bestimmten (List)SubItems können Sie nicht kontrollieren.

Immerhin existiert die API-Nachricht MSDN-Library - API LVM_SCROLLLVM_SCROLL, die Sie über die API-Funktion MSDN-Library - API SendMessageSendMessage an das ListView senden können. Damit können Sie den Inhalt eines ListViews zum einen pixelweise horizontal rollen. Vertikales Rollen zum anderen hängt vom View-Modus des ListViews ab. Im Report-Modus kann es nur zeilenweise gerollt werden - wobei die Rollweite zwar in Pixels angegeben werden muss, aber automatisch auf ganze Zeilen auf- bzw. abgerundet wird. Für eine einigermaßen zuverlässige Navigation, vor allem für seiten- oder spaltenweises Blättern im Report-Modus ist da noch einiges an Kalkulationen darum herum notwendig.

Das einfachste ist noch das saubere zeilenweise Rollen. Immerhin können Sie die Höhe beispielsweise des ersten ListItems (Index 1 - wenn kein ListItem vorhanden sein sollte, wäre schließlich auch das Rollen sinnlos), also die Zeilenhöhe, dessen Eigenschaft Height entnehmen. Durch die Anzahl der vertikalen Twips pro Pixel geteilt (Screen.TwipsPerPixelY), ergibt sich so ein sauberer Faktor - und damit die erste Prozedur zum zeilenweisen Rollen auf- oder abwärts (negative bzw. positive Werte im Parameter Rows):

Private Declare Function SendMessage Lib "user32" _
 Alias "SendMessageA" (ByVal hwnd As Long, ByVal wMsg As Long, _
 ByVal wParam As Long, ByVal lParam As Long) As Long

Private Const LVM_SCROLL = &H1014

Public Sub LvwScrollRows(ListView As ListView, _
 Optional ByVal Rows As Long = 1)

  Dim nWnd As Long
  
  With ListView
    nWnd = .hwnd
    With .ListItems
      If .Count Then
        SendMessage nWnd, LVM_SCROLL, 0, _
         Rows * (.Item(1).Height \ Screen.TwipsPerPixelY)
      End If
    End With
  End With
End Sub

Über die Ober- und Untergrenzen brauchen Sie sich glücklicherweise keine Sorgen zu machen - über die Grenzen hinaus lässt sich das ListView auch per Code nicht rollen.

Zur Ansteuerung einer bestimmten Zeile dient die folgende Prozedur. Da das ListView in der Eigenschaft GetFirstVisible das oberste sichtbare ListItem liefert, erhalten Sie aus dessen Index-Eigenschaft den obersten Index. Somit können Sie nun die notwendige Differenz der zu rollenden Zeilen zur gewünschten Zeile ermitteln.

Public Sub LvwScrollToRow(ListView As ListView, _
 Optional ByVal Row As Long = 1)

  Dim nWnd As Long
  Dim nTop As Long
  Dim nRows As Long
  
  With ListView
    nWnd = .hwnd
    nTop = .GetFirstVisible.Index
    With .ListItems
      If .Count Then
        nRows = Row - nTop
        SendMessage nWnd, LVM_SCROLL, 0, _
         nRows * (.Item(1).Height \ Screen.TwipsPerPixelY)
      End If
    End With
  End With
End Sub

Seitenweises Rollen erfordert ein klein wenig mehr Arbeit. Die Anzahl der in einer Seitenhöhe darstellbaren Zeilen liefert uns das ListView freundlicherweise über die Nachricht LVM_GETCOUNTPERPAGE (hier auch als eigenständige Funktion zur Verfügung gestellt, siehe aber auch: Sichtbare Zeilen im ListView"Sichtbare Zeilen im ListView") - und damit den benötigten Faktor:

Private Const LVM_GETCOUNTPERPAGE = &H1028

Public Function LvwRowsPerPage(ListView As ListView) As Long
  LvwRowsPerPage = SendMessage(ListView.hwnd, _
   LVM_GETCOUNTPERPAGE, 0&, 0&)
End Function

Public Sub LvwScrollPages(ListView As ListView, _
 Optional ByVal Pages As Long = 1)

  Dim nRowsPerPage As Long
  Dim nWnd As Long
  
  With ListView
    nWnd = .hwnd
    nRowsPerPage = SendMessage(nWnd, LVM_GETCOUNTPERPAGE, 0&, 0&)
    With .ListItems
      If .Count Then
        SendMessage nWnd, LVM_SCROLL, 0, _
         nRowsPerPage * Pages * _
         (.Item(1).Height \ Screen.TwipsPerPixelY)
      End If
    End With
  End With
End Sub

Das Ansteuern einer bestimmten Seite erfolgt im Prinzip wieder wie beim Ansteuern einer bestimmten Zeile:

Public Sub LvwScrollToPage(ListView As ListView, _
 Optional ByVal Page As Long = 1)

  Dim nRowsPerPage As Long
  Dim nWnd As Long
  Dim nTop As Long
  Dim nRows As Long
  
  With ListView
    nWnd = .hwnd
    nRowsPerPage = SendMessage(nWnd, _
     LVM_GETCOUNTPERPAGE, 0&, 0&)
    nTop = .GetFirstVisible.Index
    With .ListItems
      If .Count Then
        nRows = nRowsPerPage * Page - nTop + 1
        SendMessage nWnd, LVM_SCROLL, 0, _
         nRows * (.Item(1).Height \ Screen.TwipsPerPixelY)
      End If
    End With
  End With
End Sub

Das Ansteuern einer bestimmten Spalte ist nur unwesentlich aufwändiger. Zwar liefert Ihnen die API-Funktion MSDN-Library - API GetScrollPosGetScrollPos mit der Kennung SB_HORZ die absolute horizontale Rollposition in Pixels. Doch da die Spalten unterschiedlich breit sein können, können wir hier mit einer relativen Rollweite nicht viel anfangen. Der Trick besteht nun darin, zwei Mal zu rollen. Nämlich einmal um die absolute Position ganz nach links, und dann einmal an die Startposition der gewünschten Spalte (dabei wird die Bildschirmaktualisierung mittels der API-Funktion LockWindowUpdate kurzzeitig unterdrückt - siehe auch: Flackerfreiheit und mehr Rasanz"Flackerfreiheit und mehr Rasanz"). Diese Startposition liefert Ihnen die Left-Eigenschaft des entsprechenden ColumnHeader-Elements des ListViews. Da das ListView intern mit einer sehr hohen Auflösung rechnet (HIMETRIC) und die Spaltenpositionen in die gröbere Einheit Twips umgerechnet liefert, und da die Umrechnung in die zum Rollen benötigte Maßeinheit Pixels noch einmal weitere Rundungsfehler mit sich bringt, ist der sich ergebende Pixelwert zum Rollen an eine Spalte außer der äußerst linken Spalte noch ein wenig zu manipulieren, um einigermaßen sauber den Spaltenanfang zu treffen. Einige Versuche haben ergeben, dass im Falle einer zu krassen Abrundung einfach noch ein Pixel hinzugefügt werden kann. Ganz perfekt wird das vor allem bei unregelmäßigen Spaltenbreiten zwar auch nicht, aber besser kann es auch Outlook nicht, wie ich festgestellt habe.

Private Declare Function GetScrollPos Lib "user32" _
 (ByVal hwnd As Long, ByVal nBar As Long) As Long
Private Declare Function SendMessage Lib "user32" _
 Alias "SendMessageA" (ByVal hwnd As Long, ByVal wMsg As Long, _
 ByVal wParam As Long, ByVal lParam As Long) As Long
Private Declare Function LockWindowUpdate Lib "user32" _
 (ByVal hwndLock As Long) As Long

Private Const SB_HORZ = 0
Private Const SB_VERT = 1
Private Const LVM_SCROLL = &H1014

Public Sub LvwScrollToColumn(ListView As ListView, _
 ByVal Column As Long)

  Dim nWnd As Long
  Dim nLeftScroll As Long
  Dim c As Long
  Dim nLeft As Long
  
  With ListView
    nWnd = .hwnd
    nLeftScroll = GetScrollPos(nWnd, SB_HORZ)
    With .ColumnHeaders
      Select Case Column
        Case 1
        Case 2 To .Count
          For c = 1 To Column - 1
            nLeft = nLeft + CLng(.Item(c).Width)
          Next 'c
      End Select
    End With
    LockWindowUpdate nWnd
    SendMessage nWnd, LVM_SCROLL, -nLeftScroll - 1, 0
    If nLeft > 0 Then
      If nLeft Mod Screen.TwipsPerPixelX = 0 Then
        nLeft = nLeft \ Screen.TwipsPerPixelX
      Else
        nLeft = (nLeft \ Screen.TwipsPerPixelX) + 1
      End If
      SendMessage nWnd, LVM_SCROLL, nLeft, 0
    End If
    LockWindowUpdate 0&
  End With
End Sub

Aufgrund der Rundungsfehler etwas vertrackter ist das relative spaltenweise horizontale Rollen. Die gleiche Rundungsnachbearbeitung wird auch hier eingesetzt und auch die absolute Rollposition wird hier ebenfalls benötigt. Diesmal müssen wir jedoch von der absoluten Position ausgehend entweder nach links oder rechts, je nach gewünschter Rollrichtung und zu rollender Spaltenzahl, die neue Zielspalte ermitteln. Eine linksseitig "angebrochene" Spalte wird dabei wie eine vollständig sichtbare Spalte gezählt - ein Rollen um eine Spalte setzt dabei die Startposition auf den Anfang oder das Ende der unvollständig sichtbaren Spalte.

Public Sub LvwScrollColumns(ListView As ListView, _
 Optional ByVal Columns As Long = 1)

  Dim nLeftScroll As Long
  Dim c As Long
  Dim cc As Long
  Dim nMax As Long
  Dim nWnd As Long
  Dim nLeft As Long
  
  With ListView
    nWnd = .hwnd
    nLeftScroll = GetScrollPos(nWnd, SB_HORZ)
    Select Case Sgn(Columns)
      Case 0
        Exit Sub
      Case 1
        With .ColumnHeaders
          For c = 1 To .Count
            Select Case CLng(.Item(c).Left) _
             \ Screen.TwipsPerPixelX
              Case Is <= nLeftScroll
              Case Is > nLeftScroll
                nLeft = CLng(.Item(c).Left)
                If Columns > 1 Then
                  nMax = c + Columns - 2
                  If nMax > .Count Then
                    nMax = .Count
                  End If
                  For cc = c To nMax
                    nLeft = nLeft + CLng(.Item(cc).Width)
                  Next
                End If
                If nLeft Mod Screen.TwipsPerPixelX <> 0 Then
                  nLeft = (nLeft \ Screen.TwipsPerPixelX) + 1
                Else
                  nLeft = nLeft \ Screen.TwipsPerPixelX
                End If
                LockWindowUpdate nWnd
                SendMessage nWnd, LVM_SCROLL, _
                 -nLeftScroll - 1, 0
                SendMessage nWnd, LVM_SCROLL, nLeft, 0
                LockWindowUpdate 0&
                Exit Sub
            End Select
          Next
        End With
      Case -1
        With .ColumnHeaders
          For c = .Count To 1 Step -1
            nLeft = CLng(.Item(c).Left)
            If nLeft Mod Screen.TwipsPerPixelX = 0 Then
              nLeft = nLeft \ Screen.TwipsPerPixelX
            Else
              nLeft = (nLeft \ Screen.TwipsPerPixelX) + 1
            End If
            Select Case nLeft
              Case Is >= nLeftScroll
              Case Is < nLeftScroll
                If Columns < -1 Then
                  nMax = c - (Abs(Columns) - 1)
                  If nMax <= 1 Then
                    nLeft = 0
                  Else
                    For cc = c - 1 To nMax Step -1
                      nLeft = nLeft - (.Item(cc).Width _
                       \ Screen.TwipsPerPixelX)
                    Next
                  End If
                End If
                LockWindowUpdate nWnd
                SendMessage nWnd, LVM_SCROLL, _
                 -nLeftScroll - 1, 0
                If nLeft > 0 Then
                  SendMessage nWnd, LVM_SCROLL, nLeft, 0
                End If
                LockWindowUpdate 0&
                Exit Sub
            End Select
          Next
        End With
    End Select
  End With
End Sub

Beispiel-Projekt und Modul modLvwScroll (lvwscroll.zip - ca. 4,7 KB)


Artikel
Zum Download-Bereich dieses Artikel
Mail an den Autor dieses Artikels

KnowHow
Zur KnowHow-Übersicht

KnowHow-Themen
Themen - Allgemeines
Themen - Entwicklungsumgebung (VB-IDE)
Themen - Forms
Themen - Steuerelemente (Controls)
Themen - Grafik
Themen - Dateien
Themen - UserControls
Themen - Einsteiger-Tipps
Themen - Wussten Sie...?

Übersicht nach Titeln in alphabetischer Reihenfolge
Übersicht nach Erscheinungsdatum

Schnellsuche




Zum Seitenanfang

Copyright © 1999 - 2017 Harald M. Genauck, ip-pro gmbh  /  Impressum

Zum Seitenanfang

Zurück...

Zurück...

Download Internet Explorer