Delen via


Muisbeweging

Wanneer de muis beweegt, plaatst Windows een WM_MOUSEMOVE bericht. Standaard gaat WM_MOUSEMOVE naar het venster met de cursor. U kunt dit gedrag overschrijven door vastleggen de muis, die in de volgende sectie wordt beschreven.

Het WM_MOUSEMOVE bericht bevat dezelfde parameters als de berichten voor muisklikken. De laagste 16 bits van lParam de x-coördinaat bevatten en de volgende 16 bits bevatten de y-coördinaat. Gebruik de macro's GET_X_LPARAM en GET_Y_LPARAM om de coördinaten uit lParam-uit te pakken. De parameter wParam bevat een bitsgewijze OF van vlaggen, waarmee de status van de andere muisknoppen wordt aangegeven, plus de Shift- en Ctrl-toetsen. Met de volgende code worden de muiscoördinaten opgehaald van lParam.

int xPos = GET_X_LPARAM(lParam); 
int yPos = GET_Y_LPARAM(lParam);

Houd er rekening mee dat deze coördinaten zich in pixels bevinden, niet apparaatonafhankelijke pixels (DIPs). Verderop in dit onderwerp bekijken we code die wordt geconverteerd tussen de twee eenheden.

Een venster kan ook een WM_MOUSEMOVE bericht ontvangen als de positie van de cursor verandert ten opzichte van het venster. Als de cursor bijvoorbeeld over een venster wordt geplaatst en de gebruiker het venster verbergt, ontvangt het venster WM_MOUSEMOVE berichten, zelfs als de muis niet is verplaatst. Een gevolg van dit gedrag is dat de muiscoördinaten mogelijk niet veranderen tussen WM_MOUSEMOVE berichten.

Het vastleggen van muisbewegingen buiten het venster

Standaard ontvangt een venster geen WM_MOUSEMOVE berichten meer als de muis voorbij de rand van het clientgebied beweegt. Maar voor sommige bewerkingen moet u mogelijk de muispositie buiten dit punt bijhouden. Met een tekenprogramma kan de gebruiker bijvoorbeeld de selectierechthoek voorbij de rand van het venster slepen, zoals wordt weergegeven in het volgende diagram.

een illustratie van het vastleggen van de muis.

Als u berichten met muisverplaatsing wilt ontvangen voorbij de rand van het venster, roept u de functie SetCapture aan. Nadat deze functie is aangeroepen, blijft het venster WM_MOUSEMOVE berichten ontvangen zolang de gebruiker ten minste één muisknop ingedrukt houdt, zelfs als de muis buiten het venster beweegt. Het venster voor vastleggen moet het voorgrondvenster zijn en slechts één venster kan tegelijk het venster voor vastleggen zijn. Als u muisopname wilt vrijgeven, roept u de ReleaseCapture functie aan.

Doorgaans gebruikt u SetCapture en ReleaseCapture op de volgende manier.

  1. Wanneer de gebruiker op de linkermuisknop drukt, roept u SetCapture- aan om de muis vast te leggen.
  2. Reageren op berichten met muisverplaatsing.
  3. Wanneer de gebruiker de linkermuisknop loslaat, roept u ReleaseCapture-aan.

Voorbeeld: cirkels tekenen

Laten we het Circle-programma uitbreiden van module 3 door de gebruiker in staat te stellen een cirkel met de muis te tekenen. Begin met het Direct2D Circle Sample-programma. De code in dit voorbeeld wordt gewijzigd om een eenvoudige tekening toe te voegen. Voeg eerst een nieuwe lidvariabele toe aan de klasse MainWindow.

D2D1_POINT_2F ptMouse;

Met deze variabele wordt de positie van de muis opgeslagen terwijl de gebruiker de muis sleept. Initialiseer in de MainWindow constructor het beletselteken en ptMouse variabelen.

    MainWindow() : pFactory(NULL), pRenderTarget(NULL), pBrush(NULL),
        ellipse(D2D1::Ellipse(D2D1::Point2F(), 0, 0)),
        ptMouse(D2D1::Point2F())
    {
    }

Verwijder de hoofdtekst van de methode MainWindow::CalculateLayout; Dit is niet vereist voor dit voorbeeld.

void CalculateLayout() { }

Declareer vervolgens berichthandlers voor de berichten met de linkerknop omlaag, de linkerknop omhoog en het verplaatsen van de muis.

void OnLButtonDown(int pixelX, int pixelY, DWORD flags);
void OnLButtonUp();
void OnMouseMove(int pixelX, int pixelY, DWORD flags);

Muiscoördinaten worden gegeven in fysieke pixels, maar Direct2D verwacht apparaatonafhankelijke pixels (DIPs). Als u instellingen voor hoge DPI correct wilt afhandelen, moet u de pixelcoördinaten omzetten in DIPs. Zie DPI en Device-Independent Pixelsvoor meer informatie over DPI. De volgende code toont een helperklasse die pixels converteert naar DIPs.

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;

Roep DPIScale::Initialiseer in uw WM_CREATE handler nadat u het Direct2D Factory-object hebt gemaakt.

case WM_CREATE:
    if (FAILED(D2D1CreateFactory(D2D1_FACTORY_TYPE_SINGLE_THREADED, &pFactory)))
    {
        return -1;  // Fail CreateWindowEx.
    }
    DPIScale::Initialize(hwnd);
    return 0;

Ga als volgt te werk om de muiscoördinaten in DIPs uit de muisberichten op te halen:

  1. Gebruik de GET_X_LPARAM en GET_Y_LPARAM macro's om de pixelcoördinaten op te halen. Deze macro's zijn gedefinieerd in WindowsX.h, dus vergeet niet die koptekst op te nemen in uw project.
  2. Roep DPIScale::PixelsToDips aan om pixels te converteren naar DIPs.

Voeg nu de berichthandlers toe aan uw vensterprocedure.

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;

Implementeer ten slotte de berichtenhandlers zelf.

Knop Links omlaag

Ga als volgt te werk voor het bericht met de linkerknop omlaag:

  1. Roep SetCapture- aan om de muis vast te leggen.
  2. Sla de positie van de muisklik op in de ptMouse variabele. Deze positie definieert de linkerbovenhoek van het begrenzingsvak voor het beletselteken.
  3. Stel de structuur van het beletselteken opnieuw in.
  4. Roep InvalidateRect-aan. Met deze functie wordt het venster opnieuw geschilderd.
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);
}

Muis verplaatsen

Controleer of de linkermuisknop omlaag is voor het bericht met de muisverplaatsing. Als dat het is, berekent u het beletselteken opnieuw en plakt u het venster opnieuw. In Direct2D wordt een beletselteken gedefinieerd door het middelpunt en x- en y-radii. We willen een beletselteken tekenen dat past bij het begrenzingsvak dat is gedefinieerd door de muis-omlaag (ptMouse) en de huidige cursorpositie (x, y), zodat er een beetje rekenkundig nodig is om de breedte, hoogte en positie van het beletselteken te vinden.

De volgende code berekent het beletselteken opnieuw en roept vervolgens InvalidateRect- aan om het venster opnieuw te plaatsen.

diagram met een beletselteken met x- en y-radiussen.

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);
    }
}

Knop Links omhoog

Voor het bericht aan de linkerkant roept u ReleaseCapture- aan om de muisopname los te laten.

void MainWindow::OnLButtonUp()
{
    ReleaseCapture(); 
}

Volgend