Mausbewegung
Wenn sich die Maus bewegt, sendet Windows eine WM_MOUSEMOVE Nachricht. Standardmäßig wechselt WM_MOUSEMOVE zu dem Fenster, das den Cursor enthält. Sie können dieses Verhalten überschreiben, indem Sie die Maus erfassen , was im nächsten Abschnitt beschrieben wird.
Die WM_MOUSEMOVE Nachricht enthält dieselben Parameter wie die Meldungen für Mausklicks. Die niedrigsten 16 Bits von lParam enthalten die x-Koordinate, und die nächsten 16 Bits enthalten die y-Koordinate. Verwenden Sie die GET_X_LPARAM und GET_Y_LPARAM Makros, um die Koordinaten aus lParam zu entpacken. Der wParam-Parameter enthält ein bitweises OR von Flags, das den Zustand der anderen Maustasten sowie die UMSCHALT- und STRG-TASTE angibt. Der folgende Code ruft die Mauskoordinaten von lParam ab.
int xPos = GET_X_LPARAM(lParam);
int yPos = GET_Y_LPARAM(lParam);
Denken Sie daran, dass diese Koordinaten in Pixeln und nicht geräteunabhängigen Pixeln (DEVICE-Independent Pixels, DIPs) sind. Weiter unten in diesem Thema sehen wir uns Code an, der zwischen den beiden Einheiten konvertiert wird.
Ein Fenster kann auch eine WM_MOUSEMOVE Nachricht empfangen, wenn sich die Position des Cursors relativ zum Fenster ändert. Wenn der Cursor beispielsweise über einem Fenster positioniert ist und der Benutzer das Fenster ausblendet, empfängt das Fenster WM_MOUSEMOVE Nachrichten, auch wenn sich die Maus nicht bewegt hat. Eine Folge dieses Verhaltens ist, dass sich die Mauskoordinaten zwischen WM_MOUSEMOVE Nachrichten möglicherweise nicht ändern.
Erfassen von Mausbewegungen außerhalb des Fensters
Standardmäßig empfängt ein Fenster nicht mehr WM_MOUSEMOVE Nachrichten, wenn die Maus am Rand des Clientbereichs vorbeifährt. Bei einigen Vorgängen müssen Sie jedoch möglicherweise die Mausposition über diesen Punkt hinaus nachverfolgen. Beispielsweise kann ein Zeichenprogramm es dem Benutzer ermöglichen, das Auswahlrechteck über den Rand des Fensters zu ziehen, wie im folgenden Diagramm dargestellt.
Rufen Sie die SetCapture-Funktion auf, um Nachrichten mit Mausbewegung über den Rand des Fensters hinaus zu empfangen. Nachdem diese Funktion aufgerufen wurde, empfängt das Fenster weiterhin WM_MOUSEMOVE Nachrichten, solange der Benutzer mindestens eine Maustaste gedrückt hält, auch wenn sich die Maus außerhalb des Fensters bewegt. Das Erfassungsfenster muss das Vordergrundfenster sein, und nur ein Fenster kann gleichzeitig das Erfassungsfenster sein. Rufen Sie zum Freigeben der Mausaufnahme die ReleaseCapture-Funktion auf .
In der Regel verwenden Sie SetCapture und ReleaseCapture wie folgt.
- Wenn der Benutzer die linke Maustaste drückt, rufen Sie SetCapture auf, um mit der Erfassung der Maus zu beginnen.
- Reagieren Sie auf Nachrichten mit Mausbewegung.
- Wenn der Benutzer die linke Maustaste loslässt, rufen Sie ReleaseCapture auf.
Beispiel: Zeichnen von Kreisen
Erweitern Wir das Circle-Programm ab Modul 3 , indem der Benutzer einen Kreis mit der Maus zeichnen kann. Beginnen Sie mit dem Direct2D Circle Sample-Programm . Wir ändern den Code in diesem Beispiel, um eine einfache Zeichnung hinzuzufügen. Fügen Sie zunächst der MainWindow
Klasse eine neue Membervariable hinzu.
D2D1_POINT_2F ptMouse;
Diese Variable speichert die Maus nach unten, während der Benutzer die Maus zieht. Initialisieren Sie im MainWindow
Konstruktor die Variablen Ellipse und ptMouse .
MainWindow() : pFactory(NULL), pRenderTarget(NULL), pBrush(NULL),
ellipse(D2D1::Ellipse(D2D1::Point2F(), 0, 0)),
ptMouse(D2D1::Point2F())
{
}
Entfernen Sie den Text der MainWindow::CalculateLayout
-Methode. Für dieses Beispiel ist dies nicht erforderlich.
void CalculateLayout() { }
Deklarieren Sie als Nächstes Nachrichtenhandler für die Linksschaltfläche nach unten, nach oben mit der linken Schaltfläche und für Die Maus bewegende Nachrichten.
void OnLButtonDown(int pixelX, int pixelY, DWORD flags);
void OnLButtonUp();
void OnMouseMove(int pixelX, int pixelY, DWORD flags);
Mauskoordinaten werden in physischen Pixeln angegeben, aber Direct2D erwartet geräteunabhängige Pixel (DEVICE-Independent Pixels, DIPs). Damit Einstellungen mit hohen DPI-Werten ordnungsgemäß verarbeitet werden können, müssen Sie die Pixelkoordinaten in DIPs übersetzen. Weitere Informationen zu DPI finden Sie unter DPI und Device-Independent Pixel. Der folgende Code zeigt eine Hilfsklasse, die Pixel in DIPs konvertiert.
class DPIScale
{
static float scale;
public:
static void Initialize(HWND hwnd)
{
float dpi = GetDpiForWindow(hwnd);
scale = dpi/96.0f;
}
template <typename T>
static D2D1_POINT_2F PixelsToDips(T x, T y)
{
return D2D1::Point2F(static_cast<float>(x) / scale, static_cast<float>(y) / scale);
}
};
float DPIScale::scale = 1.0f;
Rufen Sie DPIScale::Initialize in Ihrem WM_CREATE-Handler auf, nachdem Sie das Direct2D-Factoryobjekt erstellt haben.
case WM_CREATE:
if (FAILED(D2D1CreateFactory(D2D1_FACTORY_TYPE_SINGLE_THREADED, &pFactory)))
{
return -1; // Fail CreateWindowEx.
}
DPIScale::Initialize(hwnd);
return 0;
Gehen Sie wie folgt vor, um die Mauskoordinaten in DIPs aus den Mausnachrichten abzurufen:
- Verwenden Sie die GET_X_LPARAM und GET_Y_LPARAM Makros, um die Pixelkoordinaten abzurufen. Diese Makros sind in WindowsX.h definiert. Denken Sie also daran, diesen Header in Ihr Projekt aufzunehmen.
- Rufen Sie
DPIScale::PixelsToDips
auf, um Pixel in DIPs zu konvertieren.
Fügen Sie nun der Fensterprozedur die Nachrichtenhandler hinzu.
case WM_LBUTTONDOWN:
OnLButtonDown(GET_X_LPARAM(lParam), GET_Y_LPARAM(lParam), (DWORD)wParam);
return 0;
case WM_LBUTTONUP:
OnLButtonUp();
return 0;
case WM_MOUSEMOVE:
OnMouseMove(GET_X_LPARAM(lParam), GET_Y_LPARAM(lParam), (DWORD)wParam);
return 0;
Implementieren Sie schließlich die Nachrichtenhandler selbst.
Linke Schaltfläche nach unten
Gehen Sie für die Meldung links unten wie folgt vor:
- Rufen Sie SetCapture auf, um mit der Erfassung der Maus zu beginnen.
- Speichern Sie die Position des Mausklicks in der variablen ptMouse . Diese Position definiert die obere linke Ecke des Begrenzungsrahmens für die Ellipse.
- Setzen Sie die Ellipsenstruktur zurück.
- Rufen Sie InvalidateRect auf. Diese Funktion erzwingt, dass das Fenster neu gestrichen wird.
void MainWindow::OnLButtonDown(int pixelX, int pixelY, DWORD flags)
{
SetCapture(m_hwnd);
ellipse.point = ptMouse = DPIScale::PixelsToDips(pixelX, pixelY);
ellipse.radiusX = ellipse.radiusY = 1.0f;
InvalidateRect(m_hwnd, NULL, FALSE);
}
Mausbewegung
Überprüfen Sie bei der Mausbewegungsmeldung, ob die linke Maustaste nicht aktiviert ist. Wenn dies der Grund ist, berechnen Sie die Auslassungspunkte neu, und streichen Sie das Fenster neu. In Direct2D wird eine Ellipse durch den Mittelpunkt und x- und y-Radien definiert. Wir möchten eine Ellipse zeichnen, die dem durch den Mauszeigerpunkt (ptMouse) definierten Begrenzungsrahmen und der aktuellen Cursorposition (x, y) entspricht, sodass ein wenig Arithmetik erforderlich ist, um die Breite, Höhe und Position der Ellipse zu ermitteln.
Der folgende Code berechnet die Auslassungspunkte neu und ruft dann InvalidateRect auf, um das Fenster neu zu streichen.
void MainWindow::OnMouseMove(int pixelX, int pixelY, DWORD flags)
{
if (flags & MK_LBUTTON)
{
const D2D1_POINT_2F dips = DPIScale::PixelsToDips(pixelX, pixelY);
const float width = (dips.x - ptMouse.x) / 2;
const float height = (dips.y - ptMouse.y) / 2;
const float x1 = ptMouse.x + width;
const float y1 = ptMouse.y + height;
ellipse = D2D1::Ellipse(D2D1::Point2F(x1, y1), width, height);
InvalidateRect(m_hwnd, NULL, FALSE);
}
}
Linke Schaltfläche nach oben
Rufen Sie für die Meldung mit der linken Schaltfläche einfach ReleaseCapture auf, um die Mausaufnahme loszulassen.
void MainWindow::OnLButtonUp()
{
ReleaseCapture();
}