滑鼠移動

當滑鼠移動時,Windows 會張貼 WM_MOUSEMOVE 訊息。 根據預設, WM_MOUSEMOVE 移至包含游標的視窗。 您可以擷 滑鼠來覆寫此行為,如下一節所述。

WM_MOUSEMOVE訊息包含與滑鼠按一下訊息相同的參數。 lParam的最低 16 位包含 x 座標,而下一個 16 位則包含 y 座標。 使用 GET_X_LPARAMGET_Y_LPARAM 宏,從 lParam解壓縮座標。 wParam參數包含旗標的位OR,表示其他滑鼠按鍵的狀態加上 SHIFT 和 CTRL 鍵。 下列程式碼會從 lParam取得滑鼠座標。

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

請記住,這些座標以圖元為單位,而不是裝置無關的圖元, (DIP) 。 本主題稍後將探討在兩個單位之間轉換的程式碼。

如果游標的位置相對於視窗而變更,視窗也可以接收 WM_MOUSEMOVE 訊息。 例如,如果游標位於視窗上,而且使用者隱藏視窗,即使滑鼠未移動,視窗仍會收到 WM_MOUSEMOVE 訊息。 此行為的其中一個結果,就是滑鼠座標可能不會在 WM_MOUSEMOVE 訊息之間變更。

在視窗外擷取滑鼠移動

根據預設,如果滑鼠移動超過工作區邊緣 視窗就會停止接收WM_MOUSEMOVE訊息。 但對於某些作業,您可能需要追蹤超過這個點的滑鼠位置。 例如,繪圖程式可能會讓使用者將選取矩形拖曳到視窗邊緣之外,如下圖所示。

滑鼠擷取的圖例。

若要接收超過視窗邊緣的滑鼠移動訊息,請呼叫 SetCapture 函式。 呼叫此函式之後,只要使用者至少按住一個滑鼠按鍵,即使滑鼠在視窗外移動,視窗仍會繼續接收 WM_MOUSEMOVE 訊息。 擷取視窗必須是前景視窗,而且一次只能有一個視窗是擷取視窗。 若要釋放滑鼠擷取,請呼叫 ReleaseCapture 函式。

您通常會以下列方式使用 SetCaptureReleaseCapture

  1. 當使用者按下滑鼠左鍵時,請呼叫 SetCapture 開始擷取滑鼠。
  2. 回應滑鼠移動訊息。
  3. 當使用者放開滑鼠左鍵時,請呼叫 ReleaseCapture

範例:繪製圓形

讓我們從 模組 3 擴充 Circle 程式,方法是讓使用者使用滑鼠繪製圓形。 從 Direct2D 圓形範例 程式開始。 我們將修改此範例中的程式碼,以新增簡單的繪圖。 首先,將新的成員變數新增至 MainWindow 類別。

D2D1_POINT_2F ptMouse;

當使用者拖曳滑鼠時,此變數會儲存滑鼠向下位置。 在建構函式中 MainWindow ,初始化 省略號ptMouse 變數。

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

移除 方法的 MainWindow::CalculateLayout 主體;此範例不需要此範例。

void CalculateLayout() { }

接下來,宣告左按鈕向下、左按鈕向上和滑鼠移動訊息的訊息處理常式。

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

滑鼠座標以實體圖元提供,但 Direct2D 預期裝置獨立圖元 (DIP) 。 若要正確處理高 DPI 設定,您必須將圖元座標轉譯為 DIP。 如需 DPI 的詳細資訊,請參閱 DPI 和 Device-Independent 圖元。 下列程式碼顯示將圖元轉換成 DIP 的協助程式類別。

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;

在您建立 Direct2D Factory 物件之後,在WM_CREATE處理常式中呼叫DPIScale::Initialize

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

若要從滑鼠訊息取得 DIP 中的滑鼠座標,請執行下列動作:

  1. 使用 GET_X_LPARAMGET_Y_LPARAM 宏來取得圖元座標。 這些巨集定義于 WindowsX.h 中,因此請記得在專案中包含該標頭。
  2. 呼叫 DPIScale::PixelsToDips 以將圖元轉換成 DIP。

現在,將訊息處理常式新增至您的視窗程式。

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;

最後,實作訊息處理常式本身。

左鍵向下鍵

針對左按鈕向下訊息,請執行下列動作:

  1. 呼叫 SetCapture 開始擷取滑鼠。
  2. 將滑鼠按一下的位置儲存在 ptMouse 變數中。 這個位置會定義省略號周框方塊的左上角。
  3. 重設橢圓形結構。
  4. 呼叫 InvalidateRect。 此函式會強制重新繪製視窗。
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);
}

滑鼠移動

針對滑鼠移動訊息,請檢查滑鼠左鍵是否關閉。 如果是,請重新計算省略號並重新繪製視窗。 在 Direct2D 中,橢圓形是由中心點和 x 和 y 弧度所定義。 我們想要繪製一個橢圓形,以符合滑鼠向下點所定義的周框方塊, (ptMouse) ,而目前的游標位置 (xy) ,因此需要一些算術來尋找橢圓形的寬度、高度和位置。

下列程式碼會重新計算省略號,然後呼叫 InvalidateRect 以重新繪製視窗。

此圖顯示具有 x 和 y 半徑的橢圓形。

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

左鍵向上

針對左按鈕向上訊息,只需呼叫 ReleaseCapture 以釋放滑鼠擷取。

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

下一個