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 21.09.2001

Diese Seite wurde zuletzt aktualisiert am 21.09.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...

Differenzierte Trefferprüfung

Zurück...


Anzeige

(-hg) mailto:hg_tvwhittestex2@aboutvb.de

Wenn Sie in den Mausereignissen (MouseDown, MouseMove und MouseUp) oder in den (OLE)Drag-Ereignissen eins TreeView-Steuerelements wissen möchten ob sich ein Knoten unter dem Mauszeiger befindet, und welcher dies ist, können Sie dies über die Methode HitTest des TreeViews ermitteln. Leider ist der Informationsgehalt dieses Tests recht beschränkt und wenig differenziert. Sie erhalten nur als "Ja" eine Referenz auf einen Knoten, wenn sich der Mauszeiger über dessen Symbol, seinem Text oder, falls vorhanden, über seiner CheckBox befindet. Oder Sie erhalten als "Nein" ein schlichtes Nothing.

Das TreeView-Steuerelement selbst hat hingegen eigentlich viel mehr mitzuteilen. Es kann durchaus zwischen Symbol, Text und CheckBox differenzieren. Es kann sogar sagen, ob sich der Mauszeiger rechts oder links von einem Knoten befindet. Und bei letzterem steht sogar die Information zur Verfügung, ob der Mauszeiger über der Einrückung oder über der Schaltfläche ("+" bzw. "-") des Knotens steht.

Erst wenn keine dieser Informationen zutrifft, wird ein Fehlschlag gemeldet. Doch selbst dieser wird noch weiter differenziert. Zum einen wird festgestellt, ob sich der Mauszeiger im Client-Bereich unterhalb des untersten Knotens befindet. Zum anderen wird aber auch mitgeteilt, in welchem "Quadranten" sich der Mauszeiger befindet, sollte die Test-Koordinaten außerhalb des Clientbereichs liegen.

Alle diese Informationen stellt ein TreeView auf die Nachricht TVM_HITTEST zur Verfügung, die ihm mittels der API-Funktion SendMessage zugestellt wird. Im Parameter lParam wird dazu die benutzerdefinierte Variable TVHITTESTINFO übergeben. In deren Unterelement des benutzerdefinierten Typs POINTAPI übergeben Sie die Koordinaten (in Pixels umgerechnet). Nach dem Aufruf von SendMessage enthält das Element Flags die Positionsinformationen, und das Element hItem liefert gegebenenfalls das Handle zu einem getroffenen Knoten.

Leider können wir in Visual Basic nichts mit diesem Knoten-Handle anfangen. Das TreeView-Steuerelement stellt macht uns mit seinen Knoten auf eine andere Weise bekannt, nämlich über einen Schlüssel. Wir könnten zwar über das Handle den Text des betreffenden Knotens ermitteln, was uns aber herzlich wenig nützt, wenn ein Knoten-Text nicht im gesamten Baum oder zumindest im gleichen Ast eindeutig ist. An den Schlüssel eines Knotens kommen wir auf API-Wegen über das Knoten-Handle jedoch auf keinen Fall heran.

Doch wenigstens in dem Fall, dass sich der Mauszeiger über dem Symbol, über dem Text oder über der CheckBox eines Knotens befindet, können wir zusätzlich noch wie gewohnt die TreeView-Methode HitTest aufrufen und somit eine Referenz auf den Knoten erhalten. Für die übrigen drei Fälle, dass sich der Mauszeiger auf der Höhe eines Knotens, aber nicht genau darüber befinden sollte, können wir eine kleine Umweglösung gehen.

Da wir ja bereits wissen, dass sich ein Knoten irgendwo dort befinden muss, und wir ebenfalls wissen, ob sich die Koordinaten rechts vom Knoten oder links von ihm (über der Einrückung oder der Schaltfläche) befinden, brauchen wir mit der HitTest-Methode lediglich alle X-Koordinaten zum jeweils gegenüberliegenden Rand hin bei gleich bleibender Y-Koordinate zu testen, bis ein Knoten angetroffen wird (Dieser kleine Trick versagt nur noch dann, wenn der Knoten horizontal aus dem Client-Bereich hinausgerollt sein sollte). Für den Fall, dass wir uns links des Knotens befinden, besorgen wir uns noch über die API-Funktion GetClientRect die Abmessungen der Innenfläche des TreeViews, um einen eventuellen vertikalen Rollbalken als auch die Rahmenstärke auszuschließen (in TreeView.Width wären diese nämlich eingeschlossen) und ermitteln gegen den rechten Rand des Rechtecks hin. Befinden wir uns rechts vom Knoten, ermitteln einfach gegen 0 hin.

Diese ganzen Ermittlungen funktionieren im Prinzip einwandfrei bei sowohl gedrückten als auch nicht gedrückten Mausknöpfen, wie auch beim Aufruf mit per Code oder anderweitig beliebig erzeugten Koordinaten. Doch wenn ein Mausknopf über dem Symbol oder über dem Text eines Knotens niedergedrückt worden ist, bringt die interne Capture-Verwaltung des TreeViews die Ermittlungen ein wenig aus dem Tritt - es wird nicht mehr angezeigt, wenn sich der Mauszeiger aus dem TreeView hinausbewegt. Abhilfe können Sie schaffen, indem Sie die Capture-Verwaltung des TreeViews überlisten und selbst die Mauszeigerverfolgung mit einem Aufruf der API-Funktion SetCapture dem TreeView wieder zuweisen, wenn ein Mausknopf niedergedrückt ist - und natürlich beim Verlassen des TreeViews wieder entziehen.

Die ganzen Mühen der verschiedenen Aufrufe und Prüfungen erledigt nun die folgende Funktion TvwHitTestEx für Sie. Sie übergeben ihr das betreffende TreeView-Steuerelement, die X- und die Y-Koordinate. Im optionalen Parameter Part können Sie eine Variable übergeben, die Ihnen die genauen Positionsinformationen zurückgibt. Der Rückgabewert der Funktion selbst enthält wie gewohnt einen Verweis auf einen gefundenen Knoten bzw. bei Misserfolg den Wert Nothing.

Beim Aufruf aus einem Mausereignis heraus sollten Sie im nächsten optionalen Parameter Button den Zustand der Mausknöpfe übergeben und den folgenden Parameter Capture auf True setzen, um die Behandlung des oben geschilderten Capture-Problems einzuschalten.

Da das Absuchen der Koordinatenzeile, falls die Koordinaten auf der Höhe eines Knotens, aber nicht direkt darüber, liegen sollten, etwas Zeit kostet, können Sie diese Suche abschalten. Setzen Sie den letzten optionalen Parameter TestRow auf False, wenn Sie darauf verzichten können oder wollen.

Private Type POINTAPI
  x As Long
  y As Long
End Type

Private Type RECT
  Left As Long
  Top As Long
  Right As Long
  Bottom As Long
End Type

Private Type TVHITTESTINFO
  pt As POINTAPI
  Flags As Long
  hItem As Long
End Type

Private Const TVHT_NOWHERE = &H1
Private Const TVHT_ONITEMICON = &H2
Private Const TVHT_ONITEMLABEL = &H4
Private Const TVHT_ONITEMINDENT = &H8
Private Const TVHT_ONITEMBUTTON = &H10
Private Const TVHT_ONITEMRIGHT = &H20
Private Const TVHT_ONITEMSTATEICON = &H40
Private Const TVHT_ABOVE = &H100
Private Const TVHT_BELOW = &H200
Private Const TVHT_TORIGHT = &H400
Private Const TVHT_TOLEFT = &H800

Private Declare Function GetClientRect Lib "user32" _
 (ByVal hwnd As Long, lpRect As RECT) As Long
Private Declare Function ReleaseCapture Lib "user32" () As Long
Private Declare Function SendMessage Lib "user32" _
 Alias "SendMessageA" (ByVal hwnd As Long, ByVal wMsg As Long, _
 ByVal wParam As Long, lParam As Any) As Long
Private Declare Function SetCapture Lib "user32" _
 (ByVal hwnd As Long) As Long

Public Enum thtPartConstants
  thtClientNowhere = TVHT_NOWHERE
  thtItemIcon = TVHT_ONITEMICON
  thtItemLabel = TVHT_ONITEMLABEL
  thtItemIndent = TVHT_ONITEMINDENT
  thtItemButton = TVHT_ONITEMBUTTON
  thtItemRight = TVHT_ONITEMRIGHT
  thtItemCheck = TVHT_ONITEMSTATEICON
  thtClientAbove = TVHT_ABOVE
  thtClientBelow = TVHT_BELOW
  thtClientRight = TVHT_TORIGHT
  thtClientLeft = TVHT_TOLEFT
  thtClientLeftAbove = TVHT_TOLEFT Or TVHT_ABOVE
  thtClientLeftBelow = TVHT_TOLEFT Or TVHT_BELOW
  thtClientRightAbove = TVHT_TORIGHT Or TVHT_ABOVE
  thtClientRightBelow = TVHT_TORIGHT Or TVHT_BELOW
End Enum

Public Function TvwHitTestEx(TreeView As TreeView, _
 ByVal x As Single, ByVal y As Single, _
 Optional Part As thtPartConstants, _
 Optional Button As Integer, _
 Optional ByVal Capture As Boolean, _
 Optional ByVal TestRow As Single = 1) As Node

  Dim nHitTestInfo As TVHITTESTINFO
  Dim nHitNode As Node
  Dim nHit As Boolean
  Dim nX As Single
  Dim nRect As RECT
  
  Const TVM_HITTEST = &H1111

  With TreeView
    If Capture And CBool(Button) Then
      SetCapture .hwnd
    End If
    Set nHitNode = .HitTest(x, y)
  End With
  With nHitTestInfo
    With .pt
      .x = x \ Screen.TwipsPerPixelX
      .y = y \ Screen.TwipsPerPixelX
    End With
    nHit = _
     CBool(SendMessage(TreeView.hwnd, TVM_HITTEST, 0, nHitTestInfo))
    Part = .Flags
  End With
  If nHit Then
    If nHitNode Is Nothing Then
      If TestRow Then
        Select Case Part
          Case thtItemButton, thtItemIndent
            With TreeView
              GetClientRect .hwnd, nRect
              For nX = x To nRect.Right * Screen.TwipsPerPixelX _
               Step Abs(TestRow) * Screen.TwipsPerPixelX
                Set nHitNode = .HitTest(nX, y)
                If Not (nHitNode Is Nothing) Then
                  Set TvwHitTestEx = nHitNode
                  Exit For
                End If
              Next 'nX
            End With
          Case thtItemRight
            With TreeView
              For nX = x To 0 _
               Step Abs(TestRow) * -Screen.TwipsPerPixelX
                Set nHitNode = .HitTest(nX, y)
                If Not (nHitNode Is Nothing) Then
                  Set TvwHitTestEx = nHitNode
                  Exit For
                End If
              Next 'nX
            End With
        End Select
      End If
    Else
      Set TvwHitTestEx = nHitNode
      If (Part And TVHT_ONITEMSTATEICON) = _
       TVHT_ONITEMSTATEICON Then
        Part = thtItemCheck
      ElseIf (Part And TVHT_ONITEMICON) = TVHT_ONITEMICON Then
        Part = thtItemIcon
      ElseIf (Part And TVHT_ONITEMLABEL) = TVHT_ONITEMLABEL Then
        Part = thtItemLabel
      End If
    End If
  End If
  If Capture Then
    If Part >= TVHT_ABOVE Then
      If Button = 0 Then
        ReleaseCapture
      End If
    End If
  End If
End Function

Beispiel-Projekt und Modul modTvwHitTestEx2 (tvwhittestex2.zip - ca. 9,4 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