用户启动多个项目选择,如下图所示:
然后他将这个选择扩展到最后一个垂直可见项目之外。
Listview开始自动滚动,但其自动滚动速度会根据鼠标位置而变化。
我想知道如何重现这种行为,因为没有API为我这样做。因此,我必须自己做,但无法找到相关的例子。
为了使事情变得清晰,我希望重现默认的Windows 行为,而不是滚动我自己的自动滚动逻辑。
我使用Spy ++尝试查找执行描述行为但无法找到任何有用信息的WM_TIMER消息。
我能够确定的是,listview经常调用0x10C1
消息,该消息未正式记录。谷歌搜索帮助我发现这条消息被称为LVM_GETACCVERSION
,但这是我能找到的。没有关于此消息的文档,也没有代码示例,或者看起来如此。
我已经用Google搜索驱动自动滚动逻辑的等式,但没有找到任何内容。
出于任何其他想法,我转过来寻求帮助。
我们如何重现INTRODUCTION
部分中描述的默认自动滚动行为?
同样,为了使事情变得清晰,我想重现默认的Windows 行为,而不是滚动我自己的自动滚动逻辑。
答案 0 :(得分:1)
算法很简单:
ScrollWindow()
乘以那么多像素。 研究滚动量
测试代码是为垂直滚动列表视图提供的。
子类列表视图:
g_OriginalListViewProc = (WNDPROC)GetWindowLongPtr(m_hwndListView, GWLP_WNDPROC);
SetWindowLongPtr(m_hwndListView, GWLP_WNDPROC, (LONG_PTR)ListViewProc);
新窗口程序:
int GetCursorOffset(HWND a_Window)
{
RECT listRect;
GetClientRect(a_Window, &listRect);
ClientToScreen(a_Window, (POINT*)&listRect + 0);
ClientToScreen(a_Window, (POINT*)&listRect + 1);
POINT cursorPos;
GetCursorPos(&cursorPos);
if (cursorPos.y < listRect.top)
return cursorPos.y - listRect.top;
else if (cursorPos.y > listRect.bottom)
return cursorPos.y - listRect.bottom;
return 0;
}
WNDPROC g_OriginalListViewProc = 0;
LRESULT ListViewProc(HWND a_HWND, UINT a_Message, WPARAM a_WParam, LPARAM a_LParam)
{
switch (a_Message)
{
case WM_PAINT:
{
static int lastPos = 0;
int scrollPos = GetScrollPos(a_HWND, SB_VERT);
int delta = scrollPos - lastPos;
lastPos = scrollPos;
TRACE(_T("Ticks=%d Pos=%06d Delta=%05d Offs=%04d\n"), GetTickCount(), scrollPos, delta, GetCursorOffset(a_HWND));
}
break;
}
return g_OriginalListViewProc(a_HWND, a_Message, a_WParam, a_LParam);
}
滚动列表视图并观察scroll_amount == offset_from_control。
研究触发器
打开任务管理器,开始滚动。您将在一个处理器核心上看到CPU负载峰值达到100%。这证明他们在繁忙的循环中滚动而不是处理一些计时器。
研究循环
在父窗口,SetTimer()
到类似5000毫秒的东西,在计时器的消息到达时放置一个断点并开始滚动。当计时器命中时,你会在callstack上看到类似的东西:
MyExe.exe!DialogProc(WM_TIMER)
user32.dll!__InternalCallWinProc@20()
user32.dll!UserCallDlgProcCheckWow()
user32.dll!DefDlgProcWorker()
user32.dll!_DefDlgProcW@16()
user32.dll!__InternalCallWinProc@20()
user32.dll!UserCallWinProcCheckWow()
user32.dll!DispatchMessageWorker()
user32.dll!_DispatchMessageW@4()
comctl32.dll!_ListView_DragSelect@12()
comctl32.dll!_ListView_HandleMouse@24()
comctl32.dll!_ListView_WndProc@16()
...
这表明_ListView_DragSelect
运行内部消息循环,处理滚动迭代之间的任何消息。
额外备注
SetCapture
上WM_LBUTTONDOWN
接收WM_MOUSEMOVE
。不要忘记稍后ReleaseCapture
。WM_MOUSEMOVE
时,您可以停止循环/计时器。