我该如何处理 WM_MOUSEWHEEL?
How do I handle WM_MOUSEWHEEL?
我正在处理如下所示的 WM_MESSAGE
,但我根本无法移动 window。我不知道为什么。我错过了什么?
void handleMouseWheel(HWND hwnd, int delta)
{
SCROLLINFO si;
si.cbSize = sizeof(SCROLLINFO);
si.fMask = SIF_PAGE | SIF_POS;
if(!GetScrollInfo(hwnd, SB_VERT, &si)) {
ErrorExit(NAMEOF(GetScrollInfo), __LINE__, __FILENAME__);
}
int nOldPos = si.nPos;
int nPos = nOldPos + wheelScrollLines(hwnd, delta, si.nPage);
SetLastError(0);
nPos = SetScrollPos(hwnd, SB_VERT, nPos, TRUE);
POINT pt;
pt.x = 0;
pt.y = nOldPos - nPos;
HDC hdc = GetDC(hwnd);
if(!hdc) {
assert(!"GetDC failed");
}
if(!LPtoDP(hdc, &pt, 1)) {
assert(!"LPtoDP failed");
}
ReleaseDC(hwnd, hdc);
if(!ScrollWindow(hwnd, 0, -pt.y, NULL, NULL)) {
ErrorExit(NAMEOF(ScrollWindow), __LINE__, __FILENAME__);
}
}
其中 wheelScrollLines()
定义为:
int wheelScrollLines(HWND hwnd, int delta, int nPage)
{
static int accumulator;
static int lastActivity;
static HWND lastHwnd;
int lines;
int dwNow = GetTickCount();
if(nPage < 1) {
nPage = 1;
}
int linesPerDataWheelDelta = defScrollSpeed;
SystemParametersInfo(SPI_GETWHEELSCROLLLINES, 0, &linesPerDataWheelDelta, 0);
if(linesPerDataWheelDelta == WHEEL_PAGESCROLL || linesPerDataWheelDelta > nPage) {
linesPerDataWheelDelta = nPage;
}
if(lastHwnd != hwnd)
{
lastHwnd = hwnd;
accumulator = 0;
}
else if((dwNow - lastActivity) > GetDoubleClickTime() * 2)
{
accumulator = 0;
}
else if(accumulator > 0 && delta < 0)
{
accumulator = 0;
}
if(linesPerDataWheelDelta > 0)
{
accumulator += delta;
lines = (accumulator * linesPerDataWheelDelta) / WHEEL_DELTA;
accumulator -= (lines * WHEEL_DELTA) / linesPerDataWheelDelta;
}
else
{
lines = 0;
accumulator = 0;
}
lastActivity = dwNow;
return -lines;
}
完整代码:
#pragma comment(lib, "user32.lib")
#pragma comment(lib, "Comctl32.lib")
#pragma comment(lib, "Gdi32.lib")
#define WIN32_LEAN_AND_MEAN
#define UNICODE
#define _UNICODE
#include <windows.h>
#include <limits.h>
#include <Commctrl.h>
#include <crtdbg.h>
#include <strsafe.h>
#include <string.h>
#include <assert.h>
#ifdef UNICODE
#define STRSPLIT wcsrchr
#else
#define STRSPLIT strrchr
#endif
#define __FILENAME__ (STRSPLIT(TEXT(__FILE__), '/') ? STRSPLIT(TEXT(__FILE__), '/') + 1 : TEXT(__FILE__))
#define NAMEOF(s) TEXT(#s)
#define COUNTOF(a) (sizeof(a)/sizeof(a[0]))
#define defScrollSpeed 3
LRESULT CALLBACK WndProc(HWND, UINT, WPARAM, LPARAM);
LRESULT CALLBACK WndProc1(HWND hwnd, UINT msg, WPARAM wParam, LPARAM lParam);
LRESULT CALLBACK WndProc2(HWND hwnd, UINT msg, WPARAM wParam, LPARAM lParam);
void createWindow1(HWND);
void createWindow2(HWND);
int scrollHeight(void);
DWORD ShowLastError(LPWSTR lpszFunction, int line, LPWSTR filename);
void ErrorExit(LPWSTR lpszFunction, int line, LPWSTR filename);
int scrollHeight(void);
int getHeight(HWND control);
void setUpScrollBar(HWND hwnd);
void handleMouseWheel(HWND hwnd, int delta);
int wheelScrollLines(HWND hwnd, int delta, int nPage);
HBRUSH hBrush1, hBrush2;
int WINAPI wWinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance,
PWSTR lpCmdLine, int nCmdShow) {
MSG msg;
WNDCLASSW wc = {0};
wc.lpszClassName = L"my window";
wc.hInstance = hInstance;
wc.hbrBackground = GetSysColorBrush(COLOR_3DFACE);
wc.lpfnWndProc = WndProc;
wc.hCursor = LoadCursor(0, IDC_ARROW);
hBrush1 = CreateSolidBrush(RGB(173, 164, 237));
hBrush2 = CreateSolidBrush(RGB(171, 171, 171));
RegisterClassW(&wc);
HWND hWnd =
CreateWindowW(wc.lpszClassName, L"window",
WS_OVERLAPPEDWINDOW | WS_VISIBLE | ES_AUTOHSCROLL,
100, 100, 330, 270, NULL, 0, hInstance, 0);
while (GetMessage(&msg, NULL, 0, 0))
{
if (!IsDialogMessage(hWnd, &msg))
{
TranslateMessage(&msg);
DispatchMessage(&msg);
}
}
DeleteObject(hBrush1);
DeleteObject(hBrush2);
return (int) msg.wParam;
}
LRESULT CALLBACK WndProc(HWND hwnd, UINT msg,
WPARAM wParam, LPARAM lParam) {
switch(msg)
{
case WM_CREATE:
createWindow1(hwnd);
createWindow2(hwnd);
setUpScrollBar(hwnd);
break;
case WM_MOUSEWHEEL:
handleMouseWheel(hwnd, HIWORD(wParam));
return 0;
case WM_DESTROY:
PostQuitMessage(0);
break;
}
return DefWindowProcW(hwnd, msg, wParam, lParam);
}
void createWindow1(HWND hOwner)
{
WNDCLASSW wc = {0};
wc.lpszClassName = L"window2";
wc.hInstance = NULL;
wc.hbrBackground = hBrush1;
wc.lpfnWndProc = WndProc1;
wc.hCursor = LoadCursor(0, IDC_ARROW);
RegisterClassW(&wc);
CreateWindowW(wc.lpszClassName, L"window2",
WS_VISIBLE | WS_TABSTOP | WS_CHILD,
5, 5, 300, 200,
hOwner, 0, NULL, 0);
}
void createWindow2(HWND hOwner)
{
WNDCLASSW wc = {0};
wc.lpszClassName = L"window3";
wc.hInstance = NULL;
wc.hbrBackground = hBrush2;
wc.lpfnWndProc = WndProc2;
wc.hCursor = LoadCursor(0, IDC_ARROW);
RegisterClassW(&wc);
CreateWindowW(wc.lpszClassName, L"window3",
WS_VISIBLE | WS_TABSTOP | WS_CHILD,
5, 120, 300, 200,
hOwner, 0, NULL, 0);
}
LRESULT CALLBACK WndProc1(HWND hwnd, UINT msg, WPARAM wParam, LPARAM lParam)
{
switch(msg)
{
case WM_CREATE:
CreateWindow(L"Button", L"Button A",
WS_VISIBLE | WS_TABSTOP | WS_CHILD,
5, 5, 80, 25,
hwnd, 0, NULL, 0);
break;
case WM_KEYUP:
case WM_KEYDOWN:
MessageBox(NULL, L"hello from proc1", L"", MB_OK);
break;
case WM_DESTROY:
PostQuitMessage(0);
break;
}
return DefWindowProcW(hwnd, msg, wParam, lParam);
}
LRESULT CALLBACK WndProc2(HWND hwnd, UINT msg, WPARAM wParam, LPARAM lParam)
{
switch(msg)
{
case WM_CREATE:
CreateWindow(L"Button", L"Button B",
WS_VISIBLE | WS_TABSTOP | WS_CHILD,
5, 5, 80, 25,
hwnd, 0, NULL, 0);
break;
case WM_DESTROY:
PostQuitMessage(0);
break;
}
return DefWindowProcW(hwnd, msg, wParam, lParam);
}
void setUpScrollBar(HWND hwnd)
{
RECT rc = { 0 };
GetClientRect(hwnd, &rc);
SCROLLINFO si = { 0 };
si.cbSize = sizeof(SCROLLINFO);
si.fMask = SIF_ALL;
si.nMin = 0;
si.nMax = 300;
si.nPage = (rc.bottom - rc.top);
si.nPos = 0;
si.nTrackPos = 0;
SetScrollInfo(hwnd, SB_VERT, &si, TRUE);
}
int wheelScrollLines(HWND hwnd, int delta, int nPage)
{
static int accumulator;
static int lastActivity;
static HWND lastHwnd;
int lines;
int dwNow = GetTickCount();
if(nPage < 1) {
nPage = 1;
}
int linesPerDataWheelDelta = defScrollSpeed;
SystemParametersInfo(SPI_GETWHEELSCROLLLINES, 0, &linesPerDataWheelDelta, 0);
if(linesPerDataWheelDelta == WHEEL_PAGESCROLL || linesPerDataWheelDelta > nPage) {
linesPerDataWheelDelta = nPage;
}
if(lastHwnd != hwnd)
{
lastHwnd = hwnd;
accumulator = 0;
}
else if((dwNow - lastActivity) > GetDoubleClickTime() * 2)
{
accumulator = 0;
}
else if(accumulator > 0 && delta < 0)
{
accumulator = 0;
}
if(linesPerDataWheelDelta > 0)
{
accumulator += delta;
lines = (accumulator * linesPerDataWheelDelta) / WHEEL_DELTA;
accumulator -= (lines * WHEEL_DELTA) / linesPerDataWheelDelta;
}
else
{
lines = 0;
accumulator = 0;
}
lastActivity = dwNow;
return -lines;
}
void handleMouseWheel(HWND hwnd, int delta)
{
SCROLLINFO si;
si.cbSize = sizeof(SCROLLINFO);
si.fMask = SIF_PAGE | SIF_POS;
if(!GetScrollInfo(hwnd, SB_VERT, &si)) {
ErrorExit(NAMEOF(GetScrollInfo), __LINE__, __FILENAME__);
}
int nOldPos = si.nPos;
int nPos = nOldPos + wheelScrollLines(hwnd, delta, si.nPage);
SetLastError(0);
nPos = SetScrollPos(hwnd, SB_VERT, nPos, TRUE);
POINT pt;
pt.x = 0;
pt.y = nOldPos - nPos;
HDC hdc = GetDC(hwnd);
if(!hdc) {
assert(!"GetDC failed");
}
if(!LPtoDP(hdc, &pt, 1)) {
assert(!"LPtoDP failed");
}
ReleaseDC(hwnd, hdc);
if(!ScrollWindow(hwnd, 0, -pt.y, NULL, NULL)) {
ErrorExit(NAMEOF(ScrollWindow), __LINE__, __FILENAME__);
}
}
int getHeight(HWND control)
{
RECT rt;
if(!GetWindowRect(control, &rt)) {
ErrorExit(NAMEOF(getHeight), __LINE__, __FILENAME__);
}
return rt.bottom - rt.top;
}
void ErrorExit(LPWSTR lpszFunction, int line, LPWSTR filename)
{
DWORD dw = ShowLastError(lpszFunction, line, filename);
ExitProcess(dw);
}
DWORD ShowLastError(LPWSTR lpszFunction, int line, LPWSTR filename)
{
#define MAX_DIGITS 16
/*
* NOTE!!: calling GetLastError() must be done before calling
* any other function, that would reset the GetLastError(), making
* this function report error about the wrong function.
*/
DWORD dw = GetLastError();
LPVOID lpMsgBuf;
LPVOID lpDisplayBuf;
FormatMessage(
FORMAT_MESSAGE_ALLOCATE_BUFFER |
FORMAT_MESSAGE_FROM_SYSTEM |
FORMAT_MESSAGE_IGNORE_INSERTS,
NULL,
dw,
MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT),
(LPTSTR) &lpMsgBuf,
0,
NULL
);
lpDisplayBuf = (LPVOID) LocalAlloc(LMEM_ZEROINIT,
(lstrlen((LPCTSTR)lpMsgBuf) +
lstrlen((LPCTSTR)lpszFunction) + 40 +
(line > 0 ? MAX_DIGITS : 0) +
(filename != NULL ? lstrlen(filename) : 0)) *
sizeof(TCHAR)
);
StringCchPrintf((LPTSTR)lpDisplayBuf,
LocalSize(lpDisplayBuf) / sizeof(TCHAR),
TEXT("%s failed with %d: %s"),
lpszFunction, dw, lpMsgBuf
);
MessageBox(NULL, (LPCTSTR)lpDisplayBuf, TEXT("Error"), MB_OK);
LocalFree(lpMsgBuf);
LocalFree(lpDisplayBuf);
return dw;
}
您正在使用 HIWORD
从 WM_MOUSEWHEEL
消息中的 wParam
中提取增量,但根据 documentation 这是不正确的:
HIWORD
returns 一个无符号值;由于增量是有符号值,因此您需要改用 GET_WHEEL_DELTA_WPARAM
。
正如乔纳森所说,您需要使用:
handleMouseWheel(hwnd, GET_WHEEL_DELTA_WPARAM(wParam));
但是 window 没有移动,因为计算错误,而不是缺少 SW_SCROLLCHILDREN
标志。当你打电话时:
nPos = SetScrollPos(hwnd, SB_VERT, nPos, TRUE);
npos
会更新到滚动框的前一个位置。此时npos
等于nOldPos
。并且每次后续更新都将相等。
然而,pt.y = nOldPos-nPos;
。所以 pt.y
每次都是 0。当你调用ScrollWindow(hwnd, 0, pt.y, NULL, NULL)
时,window不会移动。
因此您需要将代码更改为:
//nPos = SetScrollPos(hwnd, SB_VERT, nPos, TRUE); //old code
SetScrollPos(hwnd, SB_VERT, nPos, TRUE);
...
if (!ScrollWindow(hwnd, 0, pt.y, NULL, NULL))
...
编辑:
因为你的pt.y = nOldPos-nPos;
可能会让pt.y在顶部计算出错误的值或者bottom.So你可以直接使用GetScrollInfo
,像这样:
void handleMouseWheel(HWND hwnd, int delta)
{
SCROLLINFO si;
si.cbSize = sizeof(SCROLLINFO);
si.fMask = SIF_PAGE | SIF_POS | SIF_RANGE;
if (!GetScrollInfo(hwnd, SB_VERT, &si)) {
ErrorExit(LPWSTR(NAMEOF(GetScrollInfo)), __LINE__, LPWSTR(__FILENAME__));
}
int nOldPos = si.nPos;
int nPos = nOldPos + wheelScrollLines(hwnd, delta, si.nPage);
si.fMask = SIF_POS;
SetScrollPos(hwnd, SB_VERT, nPos, TRUE);
GetScrollInfo(hwnd, SB_VERT, &si);
if (!ScrollWindow(hwnd, 0, nOldPos - si.nPos, NULL, NULL)) {
ErrorExit(LPWSTR(NAMEOF(ScrollWindow)), __LINE__, LPWSTR(__FILENAME__));
}
UpdateWindow(hwnd);
}
它对我有用。
我正在处理如下所示的 WM_MESSAGE
,但我根本无法移动 window。我不知道为什么。我错过了什么?
void handleMouseWheel(HWND hwnd, int delta)
{
SCROLLINFO si;
si.cbSize = sizeof(SCROLLINFO);
si.fMask = SIF_PAGE | SIF_POS;
if(!GetScrollInfo(hwnd, SB_VERT, &si)) {
ErrorExit(NAMEOF(GetScrollInfo), __LINE__, __FILENAME__);
}
int nOldPos = si.nPos;
int nPos = nOldPos + wheelScrollLines(hwnd, delta, si.nPage);
SetLastError(0);
nPos = SetScrollPos(hwnd, SB_VERT, nPos, TRUE);
POINT pt;
pt.x = 0;
pt.y = nOldPos - nPos;
HDC hdc = GetDC(hwnd);
if(!hdc) {
assert(!"GetDC failed");
}
if(!LPtoDP(hdc, &pt, 1)) {
assert(!"LPtoDP failed");
}
ReleaseDC(hwnd, hdc);
if(!ScrollWindow(hwnd, 0, -pt.y, NULL, NULL)) {
ErrorExit(NAMEOF(ScrollWindow), __LINE__, __FILENAME__);
}
}
其中 wheelScrollLines()
定义为:
int wheelScrollLines(HWND hwnd, int delta, int nPage)
{
static int accumulator;
static int lastActivity;
static HWND lastHwnd;
int lines;
int dwNow = GetTickCount();
if(nPage < 1) {
nPage = 1;
}
int linesPerDataWheelDelta = defScrollSpeed;
SystemParametersInfo(SPI_GETWHEELSCROLLLINES, 0, &linesPerDataWheelDelta, 0);
if(linesPerDataWheelDelta == WHEEL_PAGESCROLL || linesPerDataWheelDelta > nPage) {
linesPerDataWheelDelta = nPage;
}
if(lastHwnd != hwnd)
{
lastHwnd = hwnd;
accumulator = 0;
}
else if((dwNow - lastActivity) > GetDoubleClickTime() * 2)
{
accumulator = 0;
}
else if(accumulator > 0 && delta < 0)
{
accumulator = 0;
}
if(linesPerDataWheelDelta > 0)
{
accumulator += delta;
lines = (accumulator * linesPerDataWheelDelta) / WHEEL_DELTA;
accumulator -= (lines * WHEEL_DELTA) / linesPerDataWheelDelta;
}
else
{
lines = 0;
accumulator = 0;
}
lastActivity = dwNow;
return -lines;
}
完整代码:
#pragma comment(lib, "user32.lib")
#pragma comment(lib, "Comctl32.lib")
#pragma comment(lib, "Gdi32.lib")
#define WIN32_LEAN_AND_MEAN
#define UNICODE
#define _UNICODE
#include <windows.h>
#include <limits.h>
#include <Commctrl.h>
#include <crtdbg.h>
#include <strsafe.h>
#include <string.h>
#include <assert.h>
#ifdef UNICODE
#define STRSPLIT wcsrchr
#else
#define STRSPLIT strrchr
#endif
#define __FILENAME__ (STRSPLIT(TEXT(__FILE__), '/') ? STRSPLIT(TEXT(__FILE__), '/') + 1 : TEXT(__FILE__))
#define NAMEOF(s) TEXT(#s)
#define COUNTOF(a) (sizeof(a)/sizeof(a[0]))
#define defScrollSpeed 3
LRESULT CALLBACK WndProc(HWND, UINT, WPARAM, LPARAM);
LRESULT CALLBACK WndProc1(HWND hwnd, UINT msg, WPARAM wParam, LPARAM lParam);
LRESULT CALLBACK WndProc2(HWND hwnd, UINT msg, WPARAM wParam, LPARAM lParam);
void createWindow1(HWND);
void createWindow2(HWND);
int scrollHeight(void);
DWORD ShowLastError(LPWSTR lpszFunction, int line, LPWSTR filename);
void ErrorExit(LPWSTR lpszFunction, int line, LPWSTR filename);
int scrollHeight(void);
int getHeight(HWND control);
void setUpScrollBar(HWND hwnd);
void handleMouseWheel(HWND hwnd, int delta);
int wheelScrollLines(HWND hwnd, int delta, int nPage);
HBRUSH hBrush1, hBrush2;
int WINAPI wWinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance,
PWSTR lpCmdLine, int nCmdShow) {
MSG msg;
WNDCLASSW wc = {0};
wc.lpszClassName = L"my window";
wc.hInstance = hInstance;
wc.hbrBackground = GetSysColorBrush(COLOR_3DFACE);
wc.lpfnWndProc = WndProc;
wc.hCursor = LoadCursor(0, IDC_ARROW);
hBrush1 = CreateSolidBrush(RGB(173, 164, 237));
hBrush2 = CreateSolidBrush(RGB(171, 171, 171));
RegisterClassW(&wc);
HWND hWnd =
CreateWindowW(wc.lpszClassName, L"window",
WS_OVERLAPPEDWINDOW | WS_VISIBLE | ES_AUTOHSCROLL,
100, 100, 330, 270, NULL, 0, hInstance, 0);
while (GetMessage(&msg, NULL, 0, 0))
{
if (!IsDialogMessage(hWnd, &msg))
{
TranslateMessage(&msg);
DispatchMessage(&msg);
}
}
DeleteObject(hBrush1);
DeleteObject(hBrush2);
return (int) msg.wParam;
}
LRESULT CALLBACK WndProc(HWND hwnd, UINT msg,
WPARAM wParam, LPARAM lParam) {
switch(msg)
{
case WM_CREATE:
createWindow1(hwnd);
createWindow2(hwnd);
setUpScrollBar(hwnd);
break;
case WM_MOUSEWHEEL:
handleMouseWheel(hwnd, HIWORD(wParam));
return 0;
case WM_DESTROY:
PostQuitMessage(0);
break;
}
return DefWindowProcW(hwnd, msg, wParam, lParam);
}
void createWindow1(HWND hOwner)
{
WNDCLASSW wc = {0};
wc.lpszClassName = L"window2";
wc.hInstance = NULL;
wc.hbrBackground = hBrush1;
wc.lpfnWndProc = WndProc1;
wc.hCursor = LoadCursor(0, IDC_ARROW);
RegisterClassW(&wc);
CreateWindowW(wc.lpszClassName, L"window2",
WS_VISIBLE | WS_TABSTOP | WS_CHILD,
5, 5, 300, 200,
hOwner, 0, NULL, 0);
}
void createWindow2(HWND hOwner)
{
WNDCLASSW wc = {0};
wc.lpszClassName = L"window3";
wc.hInstance = NULL;
wc.hbrBackground = hBrush2;
wc.lpfnWndProc = WndProc2;
wc.hCursor = LoadCursor(0, IDC_ARROW);
RegisterClassW(&wc);
CreateWindowW(wc.lpszClassName, L"window3",
WS_VISIBLE | WS_TABSTOP | WS_CHILD,
5, 120, 300, 200,
hOwner, 0, NULL, 0);
}
LRESULT CALLBACK WndProc1(HWND hwnd, UINT msg, WPARAM wParam, LPARAM lParam)
{
switch(msg)
{
case WM_CREATE:
CreateWindow(L"Button", L"Button A",
WS_VISIBLE | WS_TABSTOP | WS_CHILD,
5, 5, 80, 25,
hwnd, 0, NULL, 0);
break;
case WM_KEYUP:
case WM_KEYDOWN:
MessageBox(NULL, L"hello from proc1", L"", MB_OK);
break;
case WM_DESTROY:
PostQuitMessage(0);
break;
}
return DefWindowProcW(hwnd, msg, wParam, lParam);
}
LRESULT CALLBACK WndProc2(HWND hwnd, UINT msg, WPARAM wParam, LPARAM lParam)
{
switch(msg)
{
case WM_CREATE:
CreateWindow(L"Button", L"Button B",
WS_VISIBLE | WS_TABSTOP | WS_CHILD,
5, 5, 80, 25,
hwnd, 0, NULL, 0);
break;
case WM_DESTROY:
PostQuitMessage(0);
break;
}
return DefWindowProcW(hwnd, msg, wParam, lParam);
}
void setUpScrollBar(HWND hwnd)
{
RECT rc = { 0 };
GetClientRect(hwnd, &rc);
SCROLLINFO si = { 0 };
si.cbSize = sizeof(SCROLLINFO);
si.fMask = SIF_ALL;
si.nMin = 0;
si.nMax = 300;
si.nPage = (rc.bottom - rc.top);
si.nPos = 0;
si.nTrackPos = 0;
SetScrollInfo(hwnd, SB_VERT, &si, TRUE);
}
int wheelScrollLines(HWND hwnd, int delta, int nPage)
{
static int accumulator;
static int lastActivity;
static HWND lastHwnd;
int lines;
int dwNow = GetTickCount();
if(nPage < 1) {
nPage = 1;
}
int linesPerDataWheelDelta = defScrollSpeed;
SystemParametersInfo(SPI_GETWHEELSCROLLLINES, 0, &linesPerDataWheelDelta, 0);
if(linesPerDataWheelDelta == WHEEL_PAGESCROLL || linesPerDataWheelDelta > nPage) {
linesPerDataWheelDelta = nPage;
}
if(lastHwnd != hwnd)
{
lastHwnd = hwnd;
accumulator = 0;
}
else if((dwNow - lastActivity) > GetDoubleClickTime() * 2)
{
accumulator = 0;
}
else if(accumulator > 0 && delta < 0)
{
accumulator = 0;
}
if(linesPerDataWheelDelta > 0)
{
accumulator += delta;
lines = (accumulator * linesPerDataWheelDelta) / WHEEL_DELTA;
accumulator -= (lines * WHEEL_DELTA) / linesPerDataWheelDelta;
}
else
{
lines = 0;
accumulator = 0;
}
lastActivity = dwNow;
return -lines;
}
void handleMouseWheel(HWND hwnd, int delta)
{
SCROLLINFO si;
si.cbSize = sizeof(SCROLLINFO);
si.fMask = SIF_PAGE | SIF_POS;
if(!GetScrollInfo(hwnd, SB_VERT, &si)) {
ErrorExit(NAMEOF(GetScrollInfo), __LINE__, __FILENAME__);
}
int nOldPos = si.nPos;
int nPos = nOldPos + wheelScrollLines(hwnd, delta, si.nPage);
SetLastError(0);
nPos = SetScrollPos(hwnd, SB_VERT, nPos, TRUE);
POINT pt;
pt.x = 0;
pt.y = nOldPos - nPos;
HDC hdc = GetDC(hwnd);
if(!hdc) {
assert(!"GetDC failed");
}
if(!LPtoDP(hdc, &pt, 1)) {
assert(!"LPtoDP failed");
}
ReleaseDC(hwnd, hdc);
if(!ScrollWindow(hwnd, 0, -pt.y, NULL, NULL)) {
ErrorExit(NAMEOF(ScrollWindow), __LINE__, __FILENAME__);
}
}
int getHeight(HWND control)
{
RECT rt;
if(!GetWindowRect(control, &rt)) {
ErrorExit(NAMEOF(getHeight), __LINE__, __FILENAME__);
}
return rt.bottom - rt.top;
}
void ErrorExit(LPWSTR lpszFunction, int line, LPWSTR filename)
{
DWORD dw = ShowLastError(lpszFunction, line, filename);
ExitProcess(dw);
}
DWORD ShowLastError(LPWSTR lpszFunction, int line, LPWSTR filename)
{
#define MAX_DIGITS 16
/*
* NOTE!!: calling GetLastError() must be done before calling
* any other function, that would reset the GetLastError(), making
* this function report error about the wrong function.
*/
DWORD dw = GetLastError();
LPVOID lpMsgBuf;
LPVOID lpDisplayBuf;
FormatMessage(
FORMAT_MESSAGE_ALLOCATE_BUFFER |
FORMAT_MESSAGE_FROM_SYSTEM |
FORMAT_MESSAGE_IGNORE_INSERTS,
NULL,
dw,
MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT),
(LPTSTR) &lpMsgBuf,
0,
NULL
);
lpDisplayBuf = (LPVOID) LocalAlloc(LMEM_ZEROINIT,
(lstrlen((LPCTSTR)lpMsgBuf) +
lstrlen((LPCTSTR)lpszFunction) + 40 +
(line > 0 ? MAX_DIGITS : 0) +
(filename != NULL ? lstrlen(filename) : 0)) *
sizeof(TCHAR)
);
StringCchPrintf((LPTSTR)lpDisplayBuf,
LocalSize(lpDisplayBuf) / sizeof(TCHAR),
TEXT("%s failed with %d: %s"),
lpszFunction, dw, lpMsgBuf
);
MessageBox(NULL, (LPCTSTR)lpDisplayBuf, TEXT("Error"), MB_OK);
LocalFree(lpMsgBuf);
LocalFree(lpDisplayBuf);
return dw;
}
您正在使用 HIWORD
从 WM_MOUSEWHEEL
消息中的 wParam
中提取增量,但根据 documentation 这是不正确的:
HIWORD
returns 一个无符号值;由于增量是有符号值,因此您需要改用 GET_WHEEL_DELTA_WPARAM
。
正如乔纳森所说,您需要使用:
handleMouseWheel(hwnd, GET_WHEEL_DELTA_WPARAM(wParam));
但是 window 没有移动,因为计算错误,而不是缺少 SW_SCROLLCHILDREN
标志。当你打电话时:
nPos = SetScrollPos(hwnd, SB_VERT, nPos, TRUE);
npos
会更新到滚动框的前一个位置。此时npos
等于nOldPos
。并且每次后续更新都将相等。
然而,pt.y = nOldPos-nPos;
。所以 pt.y
每次都是 0。当你调用ScrollWindow(hwnd, 0, pt.y, NULL, NULL)
时,window不会移动。
因此您需要将代码更改为:
//nPos = SetScrollPos(hwnd, SB_VERT, nPos, TRUE); //old code
SetScrollPos(hwnd, SB_VERT, nPos, TRUE);
...
if (!ScrollWindow(hwnd, 0, pt.y, NULL, NULL))
...
编辑:
因为你的pt.y = nOldPos-nPos;
可能会让pt.y在顶部计算出错误的值或者bottom.So你可以直接使用GetScrollInfo
,像这样:
void handleMouseWheel(HWND hwnd, int delta)
{
SCROLLINFO si;
si.cbSize = sizeof(SCROLLINFO);
si.fMask = SIF_PAGE | SIF_POS | SIF_RANGE;
if (!GetScrollInfo(hwnd, SB_VERT, &si)) {
ErrorExit(LPWSTR(NAMEOF(GetScrollInfo)), __LINE__, LPWSTR(__FILENAME__));
}
int nOldPos = si.nPos;
int nPos = nOldPos + wheelScrollLines(hwnd, delta, si.nPage);
si.fMask = SIF_POS;
SetScrollPos(hwnd, SB_VERT, nPos, TRUE);
GetScrollInfo(hwnd, SB_VERT, &si);
if (!ScrollWindow(hwnd, 0, nOldPos - si.nPos, NULL, NULL)) {
ErrorExit(LPWSTR(NAMEOF(ScrollWindow)), __LINE__, LPWSTR(__FILENAME__));
}
UpdateWindow(hwnd);
}
它对我有用。