滑鼠移動
本文內容
在視窗外擷取滑鼠移動
範例:繪製圓形
下一個
當滑鼠移動時,Windows 會張貼 WM_MOUSEMOVE 訊息。 根據預設, WM_MOUSEMOVE 移至包含游標的視窗。 您可以擷 取 滑鼠來覆寫此行為,如下一節所述。
WM_MOUSEMOVE 訊息包含與滑鼠按一下訊息相同的參數。
lParam 的最低 16 位包含 x 座標,而下一個 16 位則包含 y 座標。 使用 GET_X_LPARAM 和 GET_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 函式。
您通常會以下列方式使用 SetCapture 和 ReleaseCapture 。
當使用者按下滑鼠左鍵時,請呼叫 SetCapture 開始擷取滑鼠。
回應滑鼠移動訊息。
當使用者放開滑鼠左鍵時,請呼叫 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 中的滑鼠座標,請執行下列動作:
使用 GET_X_LPARAM 和 GET_Y_LPARAM 宏來取得圖元座標。 這些巨集定義于 WindowsX.h 中,因此請記得在專案中包含該標頭。
呼叫 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;
最後,實作訊息處理常式本身。
針對左按鈕向下訊息,請執行下列動作:
呼叫 SetCapture 開始擷取滑鼠。
將滑鼠按一下的位置儲存在 ptMouse 變數中。 這個位置會定義省略號周框方塊的左上角。
重設橢圓形結構。
呼叫 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 ) ,而目前的游標位置 (x 、 y ) ,因此需要一些算術來尋找橢圓形的寬度、高度和位置。
下列程式碼會重新計算省略號,然後呼叫 InvalidateRect 以重新繪製視窗。
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();
}