Verschiedene Mausoperationen
In den vorherigen Abschnitten wurden Mausklicks und Mausbewegungen erläutert. Hier sind einige andere Vorgänge, die mit der Maus ausgeführt werden können.
Ziehen von Benutzeroberflächenelementen
Wenn Ihre Benutzeroberfläche das Ziehen von Benutzeroberflächenelementen unterstützt, gibt es eine andere Funktion, die Sie in Ihrem Meldungshandler mit der Maus nach unten aufrufen sollten: DragDetect. Die DragDetect-Funktion gibt TRUE zurück, wenn der Benutzer eine Mausbewegung initiiert, die als Ziehen interpretiert werden soll. Der folgende Code zeigt, wie diese Funktion verwendet wird.
case WM_LBUTTONDOWN:
{
POINT pt = { GET_X_LPARAM(lParam), GET_Y_LPARAM(lParam) };
if (DragDetect(m_hwnd, pt))
{
// Start dragging.
}
}
return 0;
Dies ist die Idee: Wenn ein Programm Drag & Drop unterstützt, soll nicht jeder Mausklick als Drag & Drop interpretiert werden. Andernfalls kann der Benutzer versehentlich etwas ziehen, wenn er einfach darauf klicken möchte (z. B. um es auszuwählen). Wenn eine Maus jedoch besonders empfindlich ist, kann es schwierig sein, die Maus beim Klicken perfekt zu halten. Daher definiert Windows einen Ziehschwellenwert von einigen Pixeln. Wenn der Benutzer die Maustaste drückt, wird dies nur dann als Ziehbewegung betrachtet, wenn die Maus diesen Schwellenwert überschreitet. Die DragDetect-Funktion testet, ob dieser Schwellenwert erreicht wird. Wenn die Funktion TRUE zurückgibt, können Sie den Mausklick als Ziehbewegung interpretieren. Andernfalls nicht.
Hinweis
Wenn DragDetect FALSE zurückgibt, Windows die _ WM-LBUTTONUP-Meldung unterdrückt, wenn der Benutzer die Maustaste loslässt. Rufen Sie daher DragDetect nur dann auf, wenn sich das Programm derzeit in einem Modus befindet, der Ziehen unterstützt. (Wenn beispielsweise bereits ein ziehbares Benutzeroberflächenelement ausgewählt ist.) Am Ende dieses Moduls sehen wir ein längeres Codebeispiel, das die DragDetect-Funktion verwendet.
Einordnen des Cursors
Manchmal möchten Sie den Cursor auf den Clientbereich oder einen Teil des Clientbereichs beschränken. Die ClipCursor-Funktion schränkt die Bewegung des Cursors auf ein angegebenes Rechteck ein. Dieses Rechteck wird in Bildschirmkoordinaten und nicht in Clientkoordinaten angegeben, sodass der Punkt (0, 0) die obere linke Ecke des Bildschirms bedeutet. Um Clientkoordinaten in Bildschirmkoordinaten zu übersetzen, rufen Sie die Funktion ClientToScreen auf.
Der folgende Code beschränkt den Cursor auf den Clientbereich des Fensters.
// Get the window client area.
RECT rc;
GetClientRect(m_hwnd, &rc);
// Convert the client area to screen coordinates.
POINT pt = { rc.left, rc.top };
POINT pt2 = { rc.right, rc.bottom };
ClientToScreen(m_hwnd, &pt);
ClientToScreen(m_hwnd, &pt2);
SetRect(&rc, pt.x, pt.y, pt2.x, pt2.y);
// Confine the cursor.
ClipCursor(&rc);
ClipCursor verwendet eine RECT-Struktur, ClientToScreen jedoch eine POINT-Struktur. Ein Rechteck wird durch seine oberen linken und unteren rechten Punkte definiert. Sie können den Cursor auf einen beliebigen rechteckigen Bereich beschränken, einschließlich der Bereiche außerhalb des Fensters, aber das Beschränken des Cursors auf den Clientbereich ist eine typische Möglichkeit, die Funktion zu verwenden. Das Ausschließliche Verwenden des Cursors in einen Bereich außerhalb Des Fensters wäre ungewöhnlich, und Benutzer würden ihn wahrscheinlich als Fehler betrachten.
Um die Einschränkung zu entfernen, rufen Sie ClipCursor mit dem Wert NULL auf.
ClipCursor(NULL);
Mausverfolgungsereignisse: Bewegen und Verlassen
Zwei weitere Mausmeldungen sind standardmäßig deaktiviert, können aber für einige Anwendungen nützlich sein:
- WM _ MOUSEHOVER:Der Cursor hat für einen festen Zeitraum mit dem Mauszeiger auf den Clientbereich gezeigert.
- WM _ MOUSELEAVE:Der Cursor hat den Clientbereich verlassen.
Um diese Nachrichten zu aktivieren, rufen Sie die TrackMouseEvent-Funktion auf.
TRACKMOUSEEVENT tme;
tme.cbSize = sizeof(tme);
tme.hwndTrack = hwnd;
tme.dwFlags = TME_HOVER | TME_LEAVE;
tme.dwHoverTime = HOVER_DEFAULT;
TrackMouseEvent(&tme);
Die TRACKMOUSEEVENT-Struktur enthält die Parameter für die Funktion. Der dwFlags-Member der -Struktur enthält Bitflags, die angeben, an welchen Nachverfolgungsmeldungen Sie interessiert sind. Sie können sowohl WM _ MOUSEHOVER als auch WM _ MOUSELEAVE, wie hier gezeigt, oder nur eine von beiden erhalten. Der dwHoverTime-Member gibt an, wie lange die Maus mit dem Mauszeiger zeigen muss, bevor das System eine Hovermeldung generiert. Dieser Wert wird in Millisekunden angegeben. Die Konstante HOVER _ DEFAULT bedeutet, dass die Standardeinstellung des Systems verwendet wird.
Nachdem Sie eine der angeforderten Nachrichten erhalten haben, wird die TrackMouseEvent-Funktion zurückgesetzt. Sie müssen ihn erneut aufrufen, um eine weitere Nachverfolgungsmeldung zu erhalten. Sie sollten jedoch warten, bis die nächste Nachricht mit der Maus bewegt wird, bevor Sie TrackMouseEvent erneut aufrufen. Andernfalls wird Ihr Fenster möglicherweise mit Nachverfolgungsmeldungen überflutet. Wenn die Maus z. B. mit dem Mauszeiger darauf zeigt, generiert das System weiterhin einen Stream von WM _ MOUSEHOVER-Nachrichten, während die Maus fest ist. Sie möchten keine weitere WM _ MOUSEHOVER-Nachricht, bis die Maus zu einer anderen Stelle bewegt und erneut mit der Maus bewegt wird.
Hier ist eine kleine Hilfsklasse, mit der Sie Mausnachverfolgungsereignisse verwalten können.
class MouseTrackEvents
{
bool m_bMouseTracking;
public:
MouseTrackEvents() : m_bMouseTracking(false)
{
}
void OnMouseMove(HWND hwnd)
{
if (!m_bMouseTracking)
{
// Enable mouse tracking.
TRACKMOUSEEVENT tme;
tme.cbSize = sizeof(tme);
tme.hwndTrack = hwnd;
tme.dwFlags = TME_HOVER | TME_LEAVE;
tme.dwHoverTime = HOVER_DEFAULT;
TrackMouseEvent(&tme);
m_bMouseTracking = true;
}
}
void Reset(HWND hwnd)
{
m_bMouseTracking = false;
}
};
Im nächsten Beispiel wird die Verwendung dieser Klasse in der Fensterprozedur veranschaulicht.
LRESULT MainWindow::HandleMessage(UINT uMsg, WPARAM wParam, LPARAM lParam)
{
switch (uMsg)
{
case WM_MOUSEMOVE:
mouseTrack.OnMouseMove(m_hwnd); // Start tracking.
// TODO: Handle the mouse-move message.
return 0;
case WM_MOUSELEAVE:
// TODO: Handle the mouse-leave message.
mouseTrack.Reset(m_hwnd);
return 0;
case WM_MOUSEHOVER:
// TODO: Handle the mouse-hover message.
mouseTrack.Reset(m_hwnd);
return 0;
}
return DefWindowProc(m_hwnd, uMsg, wParam, lParam);
}
Mausverfolgungsereignisse erfordern zusätzliche Verarbeitung durch das System. Lassen Sie sie daher deaktiviert, wenn Sie sie nicht benötigen.
Der Vollständigkeit halber finden Sie hier eine Funktion, die das System nach dem standardmäßigen Hover-Timeout abfragt.
UINT GetMouseHoverTime()
{
UINT msec;
if (SystemParametersInfo(SPI_GETMOUSEHOVERTIME, 0, &msec, 0))
{
return msec;
}
else
{
return 0;
}
}
Mausrad
Die folgende Funktion überprüft, ob ein Mausrad vorhanden ist.
BOOL IsMouseWheelPresent()
{
return (GetSystemMetrics(SM_MOUSEWHEELPRESENT) != 0);
}
Wenn der Benutzer das Mausrad dreht, empfängt das Fenster mit dem Fokus eine WM _ MOUSEWHEEL-Meldung. Der wParam-Parameter dieser Nachricht enthält einen ganzzahligen Wert namens Delta, der misst, wie weit das Rad gedreht wurde. Das Delta verwendet beliebige Einheiten, wobei 120 Einheiten als Drehung definiert sind, die zum Ausführen einer "Aktion" erforderlich ist. Natürlich hängt die Definition einer Aktion von Ihrem Programm ab. Wenn z. B. das Mausrad zum Scrollen von Text verwendet wird, würde jede 120 Drehungseinheit eine Textzeile scrollen.
Das Vorzeichen des Deltas gibt die Richtung der Drehung an:
- Positiv: Rotieren Sie nach vorn, weg vom Benutzer.
- Negativ: Rückwärts drehen, in Richtung des Benutzers.
Der Wert des Deltas wird zusammen mit einigen zusätzlichen Flags in wParam platziert. Verwenden Sie das _ _ _ WPARAM-Makro GET WHEEL DELTA, um den Wert des Deltas zu erhalten.
int delta = GET_WHEEL_DELTA_WPARAM(wParam);
Wenn das Mausrad über eine hohe Auflösung verfügt, kann der absolute Wert des Deltas kleiner als 120 sein. Wenn es in diesem Fall sinnvoll ist, dass die Aktion in kleineren Schritten erfolgt, können Sie dies tun. Beispielsweise kann der Text um Inkremente von weniger als einer Zeile scrollen. Andernfalls akkumulieren Sie das gesamte Delta, bis das Rad so lange gedreht wird, bis es die Aktion ausführen kann. Store nicht verwendeten Deltas in einer Variablen und wenn sich 120 Einheiten ansammeln (entweder positiv oder negativ), führen Sie die Aktion aus.