|
Auf allem, wo "cool" draufsteht, muss auch Cooles drin
sein, nicht wahr? Immer noch ein ziemlich neuer Schrei, wenn auch
nicht mehr der allerneueste, sind sogenannte Cool-Controls. Sie
gehören inzwischen hundertprozentig zum Web- wie auch zum Internet
Explorer- und Windows 98-Stil. Und überhaupt sind solch coole
Effekte überall dort angesagt, wo dem Anwender klipp und klar
gezeigt werden muss, dass sich der Mauszeiger über irgendetwas
befindet - dass er sich genau dort befindet, und nicht etwa
woanders...
Beim Toolbar-Steuerelement aus der mit Visual Basic 6
mitgelieferten neueren Variante der Common Controls 2 (MSComctl.ocx)
können Sie über die neue Style-Eigenschaft den Stil tbrFlat (= 1)
wählen, und schon haben Sie coole, flache Toolbar-Buttons, die ganz
Internet Explorer-like beim Darüberfahren mit dem Mauszeiger ihre
Erhabenheit zur Schau stellen. Wollen Sie Ihre selbsterstellten
Steuerelemente auch in die Gattung der Cool-Schränke, pardon,
Cool-Controls einreihen, müssen Sie die notwendige Funktionalität
allerdings auch selbst programmieren. Eine Bitte dazu am Rande:
Seien Sie nicht gar ganz so cool, wie die Entwickler der Corel
8-Suite - dort machen in den Dialogen sämtliche Steuerelemente auf
derart coole Weise auf sich aufmerksam, dass es einem davon eiskalt
den Rücken herunterläuft. Kurz gesagt: Das ist des Guten zuviel,
und es nervt einfach nur noch.
Da UserControl-Coolness eben nicht mehr der neueste Schrei ist,
werden Sie vielleicht hier und da bereits Code-Beispiele dazu
gesehen haben. Die weniger ernstzunehmenden Beispiele verlassen sich
allein auf die Auswertung der Koordinaten X und Y im
MouseMove-Ereignis und vergessen dabei, dass die
MouseMove-Ereignisse zu den am unzuverlässigsten eintreffenden
Ereignissen zählen. Bei einer allzuschnellen Bewegung der Maus
bekommt das Steuerelement gar nicht mehr mit, dass der Mauszeiger
seinen Bereich verlassen hat und macht seine Coolness nicht
ordentlich rückgängig. Die ausführlichere Darstellung derartigen
Codes können wir uns daher schenken.
Die meisten besseren Beispiele setzen dagegen auf die
API-Funktionen SetCapture
und ReleaseCapture.
Der Aufruf von SetCapture mit dem Fenster-Handle eines
Steuerelements sorgt dafür, dass danach alle Mausereignisse
weiterhin an dieses Steuerelement geschickt werden, auch wenn der
Mauszeiger sich nicht mehr darüber befinden sollte. Erst ein Klick
auf ein anderes Fenster oder Steuerelement gibt die Mausereignisse
wieder frei - dieser erste Klick gelangt jedoch noch nicht an sein
darunter liegendes Ziel, sondern zunächst noch an den mit
SetCapture eingefangenen Empfänger. Ein Task-Wechsel (Wechsel der
aktiven Anwendung) beendet dieses Einfangen ebenfalls, da SetCapture
nur innerhalb der gleichen Anwendung wirkt. Außerdem kann der
Empfänger ausdrücklich wieder über die API-Funktion
ReleaseCapture freigegeben werden.
Auf diesem Verfahren, nämlich Einfangen des Mauszeigers per
SetCapture und kontrolliertes Freigeben per ReleaseCapture, beruht
das Grundprinzip der steuerelementaren Coolness. Der oben
beschriebene Effekt kann so nicht eintreten - das Steuerelement, das
den Mauszeiger eingefangen hat, weiß so lange über die Position
desselben Bescheid, wie es ihn noch nicht ausdrücklich wieder
freigegeben hat. Die Frage, wann der Mauszeiger denn nun wieder
freizugeben wäre, wird wieder anhand einer Prüfung der X- und
Y-Koordinaten des MouseMove-Ereignisses entschieden.
Das folgende Code-Beispiel zeigt ein mögliches Grundgerüst für
den simplen Cool-Effekt, bei dem die Hintergrundfarbe eines
UserControls geändert wird, wenn sich der Mauszeiger darüber
bewegt.
Private mBackColor As Long
Private Sub UserControl_ReadProperties(PropBag As PropertyBag)
mBackColor = UserControl.BackColor
End Sub
Private Sub UserControl_MouseMove(Button As Integer, Shift As _
Integer, X As Single, Y As Single)
With UserControl
Select Case X
Case 0 To .ScaleWidth
Select Case Y
Case 0 To .ScaleHeight
SetCapture .hwnd
.BackColor = vbBlue
Exit Sub
End Select
End Select
ReleaseCapture
.BackColor = mBackColor
End With
End Sub
Befindet sich der Mauszeiger innerhalb der Fläche des
UserControls, wird der Mauszeiger eingefangen, die Hintergrundfarbe
geändert und die Ereignis-Prozedur wieder verlassen. In allen
übrigen Fällen wird der Mauszeiger wieder freigegeben und die
Hintergrundfarbe auf den Originalwert zurück gesetzt.
Handelt es sich um einen komplexeren Effekt, bei dem zusätzliche
oder aufwändigere Zeichenoperationen anfallen, sollte die
Notwendigkeit der Ausführung des Cool-Effekts vorher geprüft
werden, um Zeit zu sparen oder unschöne Flackereffekte zu
vermeiden. Der sicherste Weg ist die Prüfung über die API-Funktion
GetCapture,
ob das UserControl bereits den Mauszeiger eingefangen hat.
GetCapture gibt das Fenster-Handle zurück, dem der Mauszeiger
aktuell zugeordnet ist.
'...
If GetCapture() <> .hwnd Then
SetCapture .hwnd
.BackColor = vbBlue
End If
Exit Sub
'...
Aber schauen Sie sich einmal die folgende animierte Grafik an.
Das coole Steuerelement rechts in Form1 ist genau so programmiert.
Sie können das auch anhand des Code-Beispiels selbst ausprobieren (
Download
am Ende des Artikels).
Drei Schwachstellen werden hier sichtbar. Die erste tritt wegen
der optischen Wirkung sehr deutlich zu Tage: Befindet sich das Form
mit dem coolen Steuerelement nicht im Vordergrund (beide Forms
gehören zur gleichen Anwendung), bekommt das Steuerelement dies gar
nicht mit. Es prüft weiterhin seine eigene Fläche, auch wenn diese
zu einem guten Teil verdeckt ist. Es bleibt weiterhin im coolen
Zustand (und wird sicher irgend wann erfrieren...).
Die zweite Schwachstelle zeigt sich beim Klicken auf die rechte
Schaltfläche der davor liegenden Form - sie reagiert nicht, weil
der Empfänger der Mausereignisse immer noch das dahinterliegende
Steuerelement ist. Der vergebliche Klick kann aber stattdessen, wie
dargestellt, bewirken, dass das Form mit jenem Steuerelement
aktiviert wird und in den Vordergrund springt. Dies war aber
bestimmt nicht die Absicht des klickenden Anwenders. Die dritte
Schwachstelle ist, dass das angeklickte Form bestenfalls im
Vordergrund bleibt, die Schaltfläche jedoch erst nach einem zweiten
Klick darauf reagiert.
Statt nun komplizierte API-Operationen anzustellen, um Vorder-
und Hintergrund zu ermitteln, die überdeckten und freien Flächen
dazu und mehr, können Sie es sich einfacher machen. Es reicht eine
Prüfung, ob gerade der Punkt unter dem Mauszeiger von Windows
selbst dem UserControl zugerechnet wird. Windows geht dabei von der
Reihenfolge der Fenster (Forms und Steuerelemente - alles, was ein
Fenster-Handle hat und nicht durchsichtig ist) in der z-Achse aus
und gibt beim Aufruf der API-Funktion WindowFromPoint
mit den Koordinaten des Mauszeigers das Fenster-Handle jeweils
obersten Elements zurück. Dieses kann sowohl ein Hauptfenster
(Form) oder ein Steuerelement sein. Allerdings müssen der Funktion
WindowFromPoint auf den ganzen Bildschirm bezogene absolute
Pixel-Koordinaten übergeben werden. Es gibt zwar einige
Möglichkeiten, mit Hilfe von diversen API-Funktionen die lokalen X-
und Y-Koordinaten des UserControl-MouseMove-Ereignisses in absolute
Bildschirm-Koordinaten umzurechnen. Doch dazu müssten die
MouseMove-Koordinaten erst einmal in Pixel umgerechnet werden. Viel
schneller und einfacher liefert uns der Aufruf der API-Funktion GetCursorPos
die gewünschten Koordinaten, sogar gleich als Pixel-Werte.
Ein zeitsparender Nebeneffekt dieser Technik ist dazu, dass die
ursprüngliche, letztlich das Problem verursachende Prüfung
entfällt, ob sich die X- und Y-Koordinaten innerhalb des
UserControls befinden. Das "coolere" linke Steuerelement
in Form1 beruht auf dem folgenden Code:
Private Type POINTAPI
X As Long
Y As Long
End Type
Private Declare Function GetCapture Lib "user32" () As Long
Private Declare Function GetCursorPos Lib "user32" (lpPoint _
As POINTAPI) As Long
Private Declare Function ReleaseCapture Lib "user32" () As Long
Private Declare Function SetCapture Lib "user32" (ByVal hwnd _
As Long) As Long
Private Declare Function WindowFromPoint Lib "user32" _
(ByVal xPoint As Long, ByVal yPoint As Long) As Long
Private Sub UserControl_MouseMove(Button As Integer, Shift _
As Integer, X As Single, Y As Single)
Dim nPoint As POINTAPI
Dim nHWnd As Long
GetCursorPos nPoint
nHWnd = WindowFromPoint(nPoint.X, nPoint.Y)
With UserControl
If nHWnd = .hwnd Then
If GetCapture() <> nHWnd Then
SetCapture nHWnd
.BackColor = vbBlue
End If
Exit Sub
End If
ReleaseCapture
.BackColor = mBackColor
End With
End Sub
 |
So sieht ein "richtig cooles"
Code-Grundgerüst für coole Steuerlemente aus

|

|