共用方式為


滑鼠移動

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

WM_MOUSEMOVE 訊息包含與按兩下滑鼠的訊息相同的參數。 最低 16 位的 lParam 包含 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(); 
}

下一個