Выполните поведение прокрутки по умолчанию, когда пользователь перетаскивает элемент listview через его scrolbar
введение:
Я не носитель английского языка, и я не очень опытный программист либо.
я столкнулся с проблемой, которую мне трудно описать, поэтому, пожалуйста, имейте это в виду, читая этот вопрос.
СООТВЕТСТВУЮЩАЯ ИНФОРМАЦИЯ:
Я работаю над реализацией функций перетаскивания в listview. Я просто хочу иметь возможность переставлять строки внутри listview, не будет перетаскивания элементов в другие окна.
Я не хочу использовать OLE для этого, и я не удовлетворен реализацией "по умолчанию", которую я нашел по многочисленным ссылкам.
у меня есть свое представление о том, как я хотел бы это сделать, но моя неопытность мешает мне реализовать мои мысли.
я разрабатываю в Visual Studio, на C++ и raw WinAPI. Я не использую никаких библиотек, и я не хотел бы начинать использовать их сейчас.
:Я хочу реализовать следующие поведение:
пользователь нажимает левую кнопку мыши и начинает перетаскивать элемент -> пользователь перемещает мышь по вертикальной полосы прокрутки -> прокрутка по умолчанию происходит.
после прокрутки считается неклиентской области, это означает, что я должен как-то проанализировать поведение по умолчанию для WM_NCMOUSEMOVE
и WM_NCLBUTTONDOWN
но я не знаю как это сделать.
позвольте мне попытаться объяснить лучше, что я имею в виду:
когда вы перетаскиваете элемент, логично, что приложение указывает, где он будет отброшен когда мышь находится над элементом (в клиентской области listview ).
когда вы перетаскиваете элемент через полосу прокрутки, очевидно, что пользователь не может удалить элемент там. Вместо указания недопустимой точки падения (например, путем изменения курсора, как это делает OLE), я хочу выполнить следующее:
Я хочу выполнить поведение полосы прокрутки по умолчанию (как будто пользователь вообще не перетаскивает элемент ). Это будет так, как если бы пользователь зависает над полосой прокрутки, нажимает и удерживает левую кнопку мыши, и необязательно, перемещает мышь вверх или вниз.
когда пользователь перемещает мышь из полосы прокрутки обратно в клиентскую область listview, перетаскивание продолжается.
SSCCE
мой английский был недостаточно хорош, чтобы провести надлежащее исследование ( как я обычно делаю перед публикацией здесь), и я не знаю ни одного приложения, которое имеет такой тип поведения, поэтому мне было очень трудно попытаться решить это самостоятельно.
все-таки, продираясь сквозь Блог Рэймонда Чена я пришел к идее.
приведенный ниже пример кода прекрасно демонстрирует поведение, о котором я говорил выше. Это не идеально, но это ближе всего к реализации поведения, которое я хочу.
создать пустой проект C++ и просто скопируйте/вставьте код ниже.
затем попробуйте перетащить элемент через полосу прокрутки.
важно: я не реализовал перестановку элементов и не изменил форму курсора, чтобы сохранить код минимальным. Этот цель этого SSCCE-продемонстрировать поведение, которое я хочу.
#include <windows.h>
#include <windowsx.h> // various listview macros etc
#include <CommCtrl.h>
#include <stdio.h> // swprintf_s()
// enable Visual Styles
#pragma comment( linker, "/manifestdependency:"type='win32'
name='Microsoft.Windows.Common-Controls' version='6.0.0.0'
processorArchitecture='*' publicKeyToken='6595b64144ccf1df'
language='*'"")
// link with Common Controls library
#pragma comment( lib, "comctl32.lib")
//global variables
HINSTANCE hInst;
BOOL g_bDrag;
// subclass procedure for listview -> implements drag and drop
LRESULT CALLBACK DragAndDrop(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam,
UINT_PTR uIdSubclass, DWORD_PTR dwRefData)
{
switch (message)
{
case WM_CAPTURECHANGED: // in case user ALT+TAB to another window, for example
{
g_bDrag = FALSE;
}
return DefSubclassProc(hwnd, message, wParam, lParam);
case WM_LBUTTONUP: // do the drop ->omitted for brewity
{
if (g_bDrag)
{
POINT pt = { 0 };
pt.x = GET_X_LPARAM(lParam);
pt.y = GET_Y_LPARAM(lParam);
g_bDrag = FALSE;
ReleaseCapture();
}
}
return DefSubclassProc(hwnd, message, wParam, lParam);
case WM_MOUSEMOVE:
{
if (g_bDrag)
{
POINT pt = { 0 };
pt.x = GET_X_LPARAM(lParam);
pt.y = GET_Y_LPARAM(lParam);
LVHITTESTINFO lvhti = { 0 };
lvhti.pt = pt;
ListView_HitTest(hwnd, &lvhti);
ClientToScreen(hwnd, &pt); // WM_NCHITTEST takes screen coordinates
UINT hittest = SendMessage(hwnd, WM_NCHITTEST, 0, MAKELPARAM(pt.x, pt.y));
if (hittest == HTVSCROLL) // my try to do the default behavior
{
SendMessage(hwnd, WM_NCLBUTTONDOWN, (WPARAM)hittest, (LPARAM)POINTTOPOINTS(pt));
//SendMessage(hwnd, WM_NCMOUSEMOVE, (WPARAM)hittest, (LPARAM)POINTTOPOINTS(pt));
}
}
}
return DefSubclassProc(hwnd, message, wParam, lParam);
case WM_NCDESTROY:
::RemoveWindowSubclass(hwnd, DragAndDrop, 0);
return DefSubclassProc(hwnd, message, wParam, lParam);
}
return ::DefSubclassProc(hwnd, message, wParam, lParam);
}
// main window procedure
LRESULT CALLBACK WndProc(HWND hwnd, UINT msg, WPARAM wParam, LPARAM lParam)
{
switch (msg)
{
case WM_CREATE:
{
g_bDrag = FALSE; // user is not dragging listview item
//================ create an example listview
RECT rec = { 0 };
GetClientRect(hwnd, &rec);
HWND hwndLV = CreateWindowEx(0, WC_LISTVIEW,
L"", WS_CHILD | WS_VISIBLE | WS_BORDER | LVS_REPORT,
50, 50, 250, 200, hwnd, (HMENU)2000, hInst, 0);
// set extended listview styles
ListView_SetExtendedListViewStyle(hwndLV, LVS_EX_FULLROWSELECT | LVS_EX_GRIDLINES | LVS_EX_DOUBLEBUFFER);
// add some columns
LVCOLUMN lvc = { 0 };
lvc.mask = LVCF_FMT | LVCF_WIDTH | LVCF_TEXT | LVCF_SUBITEM;
lvc.fmt = LVCFMT_LEFT;
for (long nIndex = 0; nIndex < 5; nIndex++)
{
wchar_t txt[50];
swprintf_s(txt, 50, L"Column %d", nIndex);
lvc.iSubItem = nIndex;
lvc.cx = 60;
lvc.pszText = txt;
ListView_InsertColumn(hwndLV, nIndex, &lvc);
}
// add some items
LVITEM lvi;
lvi.mask = LVIF_TEXT;
for (lvi.iItem = 0; lvi.iItem < 10000; lvi.iItem++)
{
for (long nIndex = 0; nIndex < 5; nIndex++)
{
wchar_t txt[50];
swprintf_s(txt, 50, L"Item %d%d", lvi.iItem, nIndex);
lvi.iSubItem = nIndex;
lvi.pszText = txt;
if (!nIndex) // item
SendDlgItemMessage(hwnd, 2000, LVM_INSERTITEM, 0, reinterpret_cast<LPARAM>(&lvi));
else // sub-item
SendDlgItemMessage(hwnd, 2000, LVM_SETITEM, 0, reinterpret_cast<LPARAM>(&lvi));
}
}
//============================ subclass it
SetWindowSubclass(hwndLV, DragAndDrop, 0, 0);
}
return 0L;
case WM_NOTIFY:
{
switch (((LPNMHDR)lParam)->code)
{
case LVN_BEGINDRAG: // user started dragging listview item
{
g_bDrag = TRUE;
SetCapture(((LPNMHDR)lParam)->hwndFrom); // listview must capture the mouse
}
break;
default:
break;
}
}
break;
case WM_CLOSE:
::DestroyWindow(hwnd);
return 0L;
case WM_DESTROY:
{
::PostQuitMessage(0);
}
return 0L;
default:
return ::DefWindowProc(hwnd, msg, wParam, lParam);
}
return 0;
}
// WinMain
int WINAPI WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance, LPSTR lpCmdLine,
int nCmdShow)
{
// store hInstance in global variable for later use
hInst = hInstance;
WNDCLASSEX wc;
HWND hwnd;
MSG Msg;
// register main window class
wc.cbSize = sizeof(WNDCLASSEX);
wc.style = 0;
wc.lpfnWndProc = WndProc;
wc.cbClsExtra = 0;
wc.cbWndExtra = 0;
wc.hInstance = hInst;
wc.hIcon = LoadIcon(hInstance, IDI_APPLICATION);
wc.hCursor = LoadCursor(NULL, IDC_ARROW);
wc.hbrBackground = GetSysColorBrush(COLOR_WINDOW);
wc.lpszMenuName = NULL;
wc.lpszClassName = L"Main_Window";
wc.hIconSm = LoadIcon(hInstance, IDI_APPLICATION);
if (!RegisterClassEx(&wc))
{
MessageBox(NULL, L"Window Registration Failed!", L"Error!", MB_ICONEXCLAMATION |
MB_OK);
return 0;
}
// initialize common controls
INITCOMMONCONTROLSEX iccex;
iccex.dwSize = sizeof(INITCOMMONCONTROLSEX);
iccex.dwICC = ICC_LISTVIEW_CLASSES;
InitCommonControlsEx(&iccex);
// create main window
hwnd = CreateWindowEx(0, L"Main_Window", L"Listview Drag and Drop",
WS_OVERLAPPEDWINDOW,
50, 50, 400, 400, NULL, NULL, hInstance, 0);
ShowWindow(hwnd, nCmdShow);
UpdateWindow(hwnd);
while (GetMessage(&Msg, NULL, 0, 0) > 0)
{
TranslateMessage(&Msg);
DispatchMessage(&Msg);
}
return Msg.wParam;
}
теперь начните перетаскивать элемент, а затем переместите мышь над/под / над большим пальцем полосы прокрутки - > поведение, которое вы наблюдаете, я ищу.
эта программа имеет недостаток:
когда я пытаюсь перетащить элемент обратно в клиентскую область listview, вместо моего перетаскивания кода полоса прокрутки по-прежнему контролируется. Это поведение по умолчанию, но мне нужно изменить его таким образом, чтобы мой перетащив код вместо этого исполняет.
Это лучшее, что я смог сделать сам. Теперь вы видите, что я пытаюсь сделать.
если требуется дополнительная информация, я обновлю свой пост. Между тем я буду продолжать пытаться самостоятельно и обновлять этот пост, если я добьюсь прогресса.
Спасибо за ваше время и помощь. С уважением.
2 ответов
единственный способ для этого подхода к работе-найти способ получить mouse move
сообщения во время прокрутки. Мышь "lost"
но захват все еще остается в listview(полоса прокрутки). Итак, когда мышь покидает область прокрутки, нам нужно освободить захват (из полосы прокрутки) и снова установить его в listview. Для этого мы применим WH_MOUSE_LL
крюк, когда мы получим LVN_BEGINDRAG
уведомления сообщения и отцепить когда мы закончим dragging
(это для вертикальной полосы прокрутки. Идея точно так же для горизонтального):
HHOOK mouseHook = NULL;
unsigned char g_bDrag = false, g_bScroll = false; //if we are scrolling
unsigned char g_bVsrollExist = false;
RECT scrollRect; //the scrollbar rectangle, in screen coordinates
int thumbTop, thumbBottom; //the y in screen coordinates
int arrowHeight; //the height of the scrollbar up-down arrow buttons
LRESULT CALLBACK WindowProcedure(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam){
NMHDR *nmr;
switch(message){ //handle the messages
case WM_NOTIFY:
nmr = (NMHDR *)lParam;
if( nmr->code == LVN_BEGINDRAG ){
//printf("BeginDrag \n");
g_bDrag = true;
SetCapture(hwndListView); // listview must capture the mouse
if( g_bVsrollExist == true ){
mouseHook = SetWindowsHookEx(WH_MOUSE_LL, (HOOKPROC)LowLevelMouseProc, NULL, NULL);
}
}
default: //for messages that we don't deal with
return DefWindowProc(hwnd, message, wParam, lParam);
}
return DefWindowProc(hwnd, message, wParam, lParam);
}
LRESULT CALLBACK LowLevelMouseProc(int nCode, WPARAM wParam, LPARAM lParam){
HWND hwnd;
MSLLHOOKSTRUCT *mslhs;
if(nCode == HC_ACTION){
switch( (int)wParam ){ //handle the messages
case WM_LBUTTONUP:
//check if we are dragging and release the mouse and unhook
if( g_bDrag == true ){
g_bDrag = false;
g_bScroll = false;
hwnd = GetCapture();
if( hwnd == hwndListView ){
ReleaseCapture();
}
if( mouseHook != NULL ){UnhookWindowsHookEx(mouseHook); mouseHook = NULL;}
}
break;
case WM_MOUSEMOVE:
if( g_bDrag == true ){
mslhs = (MSLLHOOKSTRUCT *)lParam;
// check if we are outside the area which is: scrollbar area minus the arrow buttons
if( mslhs->pt.x < scrollRect.left || mslhs->pt.x >= scrollRect.right ||
mslhs->pt.y <= scrollRect.top + arrowHeight + 1 || mslhs->pt.y > scrollRect.bottom - arrowHeight - 1 ){
if( g_bScroll == true ){
//we need to release the capture from scrollbar
ReleaseCapture();
//set it again to listview
SetTimer(hwndListView, 1, 10, NULL);
g_bScroll = false;
}
}
}
break;
default: //for messages that we don't deal with
return CallNextHookEx(NULL, nCode, wParam, lParam);
}
}
return CallNextHookEx(NULL, nCode, wParam, lParam);
}
в подклассе listview:
LRESULT CALLBACK ListViewWndProc(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam, UINT_PTR uIdSubclass, DWORD_PTR dwrefData){
POINT pnt;
SCROLLBARINFO sf;
//UNREFERENCED_PARAMETER(uIdSubclass)
//UNREFERENCED_PARAMETER(dwrefData)
switch(message){ //handle the messages
case WM_MOUSEMOVE:
if( g_bDrag == true && g_bScroll == false && g_bVsrollExist == true ){
sf.cbSize = sizeof(SCROLLBARINFO);
GetScrollBarInfo(hwndListView, OBJID_VSCROLL, &sf);
//in client coordinates
thumbTop = sf.xyThumbTop;
thumbBottom = sf.xyThumbBottom;
//in screen coordinates
thumbTop += scrollRect.top + 1;
thumbBottom += scrollRect.top - 2;
pnt.x = GET_X_LPARAM(lParam);
pnt.y = GET_Y_LPARAM(lParam);
ClientToScreen(hwnd, &pnt);
//we check if we enter the thumb area
if( pnt.x >= scrollRect.left && pnt.x <= scrollRect.right &&
pnt.y > thumbTop + 1 && pnt.y <= thumbBottom - 1 ){
g_bScroll = true;
SendMessage(hwnd, WM_NCLBUTTONDOWN, (WPARAM)HTVSCROLL, (LPARAM)POINTTOPOINTS(pnt));
}
}
break;
case WM_TIMER:
//set the capture to listview to continue getting mouse move messages
if( (int)wParam == 1 ){
UpdateWindow(hwndListView);
SetCapture(hwndListView);
KillTimer(hwndListView, 1);
}
break;
case WM_LBUTTONDOWN:
sf.cbSize = sizeof(SCROLLBARINFO);
GetScrollBarInfo(hwndListView, OBJID_VSCROLL, &sf);
//check if vertical scrolbar exist
if( sf.rgstate[0] == STATE_SYSTEM_INVISIBLE ){
g_bVsrollExist = false;
break;
}
else{g_bVsrollExist = true;}
arrowHeight = sf.dxyLineButton;
scrollRect = sf.rcScrollBar;
//in client coordinates
thumbTop = sf.xyThumbTop;
thumbBottom = sf.xyThumbBottom;
//in screen coordinates
thumbTop += scrollRect.top + 1;
thumbBottom += scrollRect.top - 2;
break;
case WM_LBUTTONUP:
if(g_bDrag == true){
pnt.x = GET_X_LPARAM(lParam);
pnt.y = GET_Y_LPARAM(lParam);
g_bDrag = false;
ReleaseCapture();
}
break;
default: //for messages that we don't deal with
return DefSubclassProc(hwnd, message, wParam, lParam);
}
return DefSubclassProc(hwnd, message, wParam, lParam);
}
EDIT (прокрутка по умолчанию)
unsigned char scrollUp = false, scrollDown = false, scrollLeft = false,
scrollRight = false, scrolling = false, vertScrollIsVisible = false,
horzScrollIsVisible = false;
int top, down, left, right; //client window in screen coordinates
LRESULT CALLBACK ListViewWndProc(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam, UINT_PTR uIdSubclass, DWORD_PTR dwrefData){
POINT pnt;
SCROLLBARINFO sbiVert, sbiHorz;
//UNREFERENCED_PARAMETER(uIdSubclass)
//UNREFERENCED_PARAMETER(dwrefData)
switch(message){ //handle the messages
case WM_MOUSEMOVE:
pnt.x = GET_X_LPARAM(lParam);
pnt.y = GET_Y_LPARAM(lParam);
ClientToScreen(hwnd, &pnt);
if( g_bDrag == true && (horzScrollIsVisible == true || vertScrollIsVisible == true) ){
CheckMouse(pnt);
}
break;
case WM_LBUTTONDOWN:
sbiVert.cbSize = sizeof(SCROLLBARINFO);
sbiHorz.cbSize = sizeof(SCROLLBARINFO);
GetScrollBarInfo(hwndListView, OBJID_VSCROLL, &sbiVert);
GetScrollBarInfo(hwndListView, OBJID_HSCROLL, &sbiHorz);
if( sbiVert.rgstate[0] == STATE_SYSTEM_INVISIBLE ){
vertScrollIsVisible = false;
}
else{
vertScrollIsVisible = true;
}
if( sbiHorz.rgstate[0] == STATE_SYSTEM_INVISIBLE ){
horzScrollIsVisible = false;
}
else{
horzScrollIsVisible = true;
}
if( vertScrollIsVisible == true ){
//you can get the header handle with hwndHeader = ListView_GetHeader(hwndListView);
GetWindowRect(hwndHeader, &rt);
top = rt.bottom;
GetWindowRect(hwndListView, &rt);
if( horzScrollIsVisible == true ){
bottom = rt.bottom - sbiHorz.dxyLineButton;
}
else{
bottom = rt.bottom;
}
}
if( horzScrollIsVisible == true ){
GetWindowRect(hwndListView, &rt);
left = rt.left;
if( vertScrollIsVisible == true ){
right = rt.right - sbiVert.dxyLineButton;
}
else{
right = rt.right;
}
}
break;
case WM_LBUTTONUP:
if(g_bDrag == true){
KillTimer(hwndWin, 1); //hwndWin is your main window
g_bDrag = false;
ReleaseCapture();
}
break;
default: //for messages that we don't deal with
return DefSubclassProc(hwnd, message, wParam, lParam);
}
return DefSubclassProc(hwnd, message, wParam, lParam);
}
void CheckMouse(POINT pnt){
if( pnt.y < top ){
scrollUp = true;
scrollDown = false;
}
else if( pnt.y >= bottom ){
scrollDown = true;
scrollUp = false;
}
else{
scrollUp = false;
scrollDown = false;
}
if( pnt.x >= right ){
scrollRight = true;
scrollLeft = false;
}
else if( pnt.x < left ){
scrollLeft = true;
scrollRight = false;
}
else{
scrollRight = false;
scrollLeft = false;
}
if( scrollUp == true || scrollDown == true || scrollLeft == true || scrollRight == true ){
if( scrolling == false ){
scrolling = true;
SetTimer(hwndWin, 1, 20, NULL);
}
}
else{
if( scrolling == true ){
scrolling = false;
KillTimer(hwndWin, 1);
}
}
return;
}
LRESULT CALLBACK WindowProcedure(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam){
NMHDR *nmr;
switch(message){ //handle the messages
case WM_NOTIFY:
nmr = (NMHDR *)lParam;
if( nmr->code == LVN_BEGINDRAG ){
//printf("BeginDrag \n");
g_bDrag = true;
SetCapture(hwndListView); // listview must capture the mouse
}
break;
case WM_TIMER:
if( (int)wParam == 1 ){
if( scrollUp == true && vertScrollIsVisible == true ){
SendMessage(hwndListView, WM_VSCROLL, (WPARAM)0x00000000, (LPARAM)0x0); //up
}
if( scrollDown == true && vertScrollIsVisible ){
SendMessage(hwndListView, WM_VSCROLL, (WPARAM)0x00000001, (LPARAM)0x0); //down
}
if( scrollRight == true && horzScrollIsVisible ){
SendMessage(hwndListView, WM_HSCROLL, (WPARAM)0x00000001, (LPARAM)0x0); //right
}
if( scrollLeft == true && horzScrollIsVisible ){
SendMessage(hwndListView, WM_HSCROLL, (WPARAM)0x00000000, (LPARAM)0x0); //left
}
}
break;
default: //for messages that we don't deal with
return DefWindowProc(hwnd, message, wParam, lParam);
}
return DefWindowProc(hwnd, message, wParam, lParam);
}
я добавляю еще один ответ, потому что код немного longgggg, но не сложно. Следующий пример для простоты для vertical scrollbar
только. Проверьте его и если он работает правильно, я добавлю к horizontal scrollbar
также:
переменные, которые вам понадобятся:
enum{NO_SCROLLING, VERT_TRACK, VERT_UP_LINE, VERT_DOWN_LINE, VERT_PAGE_UP, VERT_PAGE_DOWN,
HORZ_TRACK, HORZ_LEFT_LINE, HORZ_RIGHT_LINE, HORZ_PAGE_RIGHT, HORZ_PAGE_LEFT};
//function pointer
void (*scrollStatePointer[10])(POINT) = {
VerticalTrack, VerticalUpLine, VerticalDownLine, VerticalPageUp, VerticalPageDown,
HorizontalTrack, HorizontalLeftLine, HorizontalRightLine, HorizontalPageRight,
HorizontalPageLeft
};
unsigned char g_bDrag = false, vertScrollIsVisible = false, horzScrollIsVisible = false;
HWND hwndWin = NULL, hwndListView = NULL;
HHOOK mouseHook = NULL;
//vertScrollRect is the whole vertical scrollbar rectangle
//vertFreeScrollRect is the whole vertical scrollbar rectangle without the up and down
//arrows. All in screen coordinates
RECT vertScrollRect, vertFreeScrollRect, vertUpArrowRect, vertDownArrowRect, vertThumbRect,
horzScrollRect, horzFreeScrollRect, horzLeftArrowRect, horzRightArrowRect,
horzThumbRect;
главное окно:
LRESULT CALLBACK WindowProcedure(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam){
NMHDR *nmr;
POINT pnt;
switch(message){ //handle the messages
case WM_NOTIFY:
nmr = (NMHDR *)lParam;
if( nmr->code == LVN_BEGINDRAG ){
//printf("BeginDrag \n");
g_bDrag = true;
SetCapture(hwndListView); // listview must capture the mouse
if( g_bVsrollExist == true ){
mouseHook = SetWindowsHookEx(WH_MOUSE_LL, (HOOKPROC)LowLevelMouseProc, NULL, NULL);
}
}
break;
case WM_TIMER:
if( (int)wParam == 1 ){
SetCapture(hwndListView);
KillTimer(hwndWin, 1);
}
if( (int)wParam == 2 ){ //vert line up
SendMessage(hwndListView, WM_VSCROLL, (WPARAM)0x00000000, (LPARAM)0x0);
}
if( (int)wParam == 3 ){ //vert line down
SendMessage(hwndListView, WM_VSCROLL, (WPARAM)0x00000001, (LPARAM)0x0);
}
if( (int)wParam == 4 ){ //vert page Down
SendMessage(hwndListView, WM_VSCROLL, (WPARAM)0x00000003, (LPARAM)0x0);
GetCursorPos(&pnt);
VerticalPageDown(pnt);
}
if( (int)wParam == 5 ){ //vert page Up
SendMessage(hwndListView, WM_VSCROLL, (WPARAM)0x00000002, (LPARAM)0x0);
GetCursorPos(&pnt);
VerticalPageUp(pnt);
}
if( (int)wParam == 6 ){ //horz line right
}
if( (int)wParam == 7 ){ //horz line left
}
if( (int)wParam == 8 ){ //horz page right
}
if( (int)wParam == 9 ){ //horz page left
}
break;
default: //for messages that we don't deal with
return DefWindowProc(hwnd, message, wParam, lParam);
}
return DefWindowProc(hwnd, message, wParam, lParam);
}
окно ListView:
LRESULT CALLBACK ListViewWndProc(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam, UINT_PTR uIdSubclass, DWORD_PTR dwrefData){
POINT pnt;
SCROLLBARINFO sbiVert, sbiHorz;
//UNREFERENCED_PARAMETER(uIdSubclass)
//UNREFERENCED_PARAMETER(dwrefData)
switch(message){ //handle the messages
case WM_MOUSEMOVE:
pnt.x = GET_X_LPARAM(lParam);
pnt.y = GET_Y_LPARAM(lParam);
ClientToScreen(hwnd, &pnt);
if( g_bDrag == true ){
if( vertScrollIsVisible == true && horzScrollIsVisible == false ){
CheckMouseInVert(pnt);
}
else if( vertScrollIsVisible == false && horzScrollIsVisible == true ){
CheckMouseInHorz(pnt);
}
else if( vertScrollIsVisible == true && horzScrollIsVisible == true ){
CheckMouseInBoth(pnt);
}
else{ //Both scrollbars are NOT visible
break;
}
}
break;
case WM_LBUTTONDOWN:
sbiVert.cbSize = sizeof(SCROLLBARINFO);
sbiHorz.cbSize = sizeof(SCROLLBARINFO);
GetScrollBarInfo(hwndListView, OBJID_VSCROLL, &sbiVert);
GetScrollBarInfo(hwndListView, OBJID_HSCROLL, &sbiHorz);
if( sbiVert.rgstate[0] == STATE_SYSTEM_INVISIBLE ){
vertScrollIsVisible = false;
}
else{
vertScrollIsVisible = true;
}
if( sbiHorz.rgstate[0] == STATE_SYSTEM_INVISIBLE ){
horzScrollIsVisible = false;
}
else{
horzScrollIsVisible = true;
}
if( vertScrollIsVisible == true ){
SetVertRects(&sbiVert);
}
if( horzScrollIsVisible == true ){
}
break;
default: //for messages that we don't deal with
return DefSubclassProc(hwnd, message, wParam, lParam);
}
return DefSubclassProc(hwnd, message, wParam, lParam);
}
крючок функция:
LRESULT CALLBACK LowLevelMouseProc(int nCode, WPARAM wParam, LPARAM lParam){
MSLLHOOKSTRUCT *mslhs;
HWND hwnd;
if(nCode == HC_ACTION){
switch((int)wParam){ //handle the messages
case WM_LBUTTONUP:
if( g_bDrag == true ){
g_bDrag = false;
scrolling = NO_SCROLLING;
KillTimer(hwndWin, 1);
KillTimer(hwndWin, 2);
KillTimer(hwndWin, 3);
KillTimer(hwndWin, 4);
KillTimer(hwndWin, 5);
//KillTimer(hwndWin, 6);
//KillTimer(hwndWin, 7);
//KillTimer(hwndWin, 8);
//KillTimer(hwndWin, 9);
hwnd = GetCapture();
if( hwnd == hwndListView ){
ReleaseCapture();
}
if( mouseHook != NULL ){UnhookWindowsHookEx(mouseHook); mouseHook = NULL;}
}
break;
case WM_MOUSEMOVE:
if( g_bDrag == true && scrolling != NO_SCROLLING ){
mslhs = (MSLLHOOKSTRUCT *)lParam;
if( scrolling == VERT_TRACK ){
VerticalTrack( mslhs->pt );
}
else if( scrolling == HORZ_TRACK ){
HorizontalTrack( mslhs->pt );
}
else{
//Nothing
}
}
break;
default: //for messages that we don't deal with
return CallNextHookEx(NULL, nCode, wParam, lParam);
}
}
return CallNextHookEx(NULL, nCode, wParam, lParam);
}
различные функции:
void SetVertRects(SCROLLBARINFO *sbiVert){
vertScrollRect = sbiVert->rcScrollBar;
vertThumbRect.left = sbiVert->rcScrollBar.left;
vertThumbRect.top = sbiVert->rcScrollBar.top + sbiVert->xyThumbTop;
vertThumbRect.right = sbiVert->rcScrollBar.right;
vertThumbRect.bottom = sbiVert->rcScrollBar.top + sbiVert->xyThumbBottom;
vertUpArrowRect.left = sbiVert->rcScrollBar.left;
vertUpArrowRect.top = sbiVert->rcScrollBar.top;
vertUpArrowRect.right = sbiVert->rcScrollBar.right;
vertUpArrowRect.bottom = sbiVert->rcScrollBar.top + sbiVert->dxyLineButton;
vertDownArrowRect.left = sbiVert->rcScrollBar.left;
vertDownArrowRect.top = sbiVert->rcScrollBar.bottom - sbiVert->dxyLineButton;
vertDownArrowRect.right = sbiVert->rcScrollBar.right;
vertDownArrowRect.bottom = sbiVert->rcScrollBar.bottom;
vertFreeScrollRect.left = sbiVert->rcScrollBar.left;
vertFreeScrollRect.top = sbiVert->rcScrollBar.top + sbiVert->dxyLineButton;
vertFreeScrollRect.right = sbiVert->rcScrollBar.right;
vertFreeScrollRect.bottom = sbiVert->rcScrollBar.bottom - sbiVert->dxyLineButton;
return;
}
void VerticalTrack(POINT pnt){
SCROLLBARINFO sbiVert;
if( PtInRect(&vertFreeScrollRect, pnt) == false ){
sbiVert.cbSize = sizeof(SCROLLBARINFO);
GetScrollBarInfo(hwndListView, OBJID_VSCROLL, &sbiVert);
vertThumbRect.left = sbiVert.rcScrollBar.left;
vertThumbRect.top = sbiVert.rcScrollBar.top + sbiVert.xyThumbTop;
vertThumbRect.right = sbiVert.rcScrollBar.right;
vertThumbRect.bottom = sbiVert.rcScrollBar.top + sbiVert.xyThumbBottom;
ReleaseCapture();
SetTimer(hwndWin, 1, 10, NULL);
scrolling = NO_SCROLLING;
}
return;
}
void VerticalUpLine(POINT pnt){
SCROLLBARINFO sbiVert;
if( PtInRect(&vertUpArrowRect, pnt) == false ){
sbiVert.cbSize = sizeof(SCROLLBARINFO);
GetScrollBarInfo(hwndListView, OBJID_VSCROLL, &sbiVert);
vertThumbRect.left = sbiVert.rcScrollBar.left;
vertThumbRect.top = sbiVert.rcScrollBar.top + sbiVert.xyThumbTop;
vertThumbRect.right = sbiVert.rcScrollBar.right;
vertThumbRect.bottom = sbiVert.rcScrollBar.top + sbiVert.xyThumbBottom;
KillTimer(hwndWin, 2);
scrolling = NO_SCROLLING;
}
return;
}
void VerticalDownLine(POINT pnt){
SCROLLBARINFO sbiVert;
if( PtInRect(&vertDownArrowRect, pnt) == false ){
sbiVert.cbSize = sizeof(SCROLLBARINFO);
GetScrollBarInfo(hwndListView, OBJID_VSCROLL, &sbiVert);
vertThumbRect.left = sbiVert.rcScrollBar.left;
vertThumbRect.top = sbiVert.rcScrollBar.top + sbiVert.xyThumbTop;
vertThumbRect.right = sbiVert.rcScrollBar.right;
vertThumbRect.bottom = sbiVert.rcScrollBar.top + sbiVert.xyThumbBottom;
KillTimer(hwndWin, 3);
scrolling = NO_SCROLLING;
}
return;
}
void VerticalPageUp(POINT pnt){
SCROLLBARINFO sbiVert;
sbiVert.cbSize = sizeof(SCROLLBARINFO);
GetScrollBarInfo(hwndListView, OBJID_VSCROLL, &sbiVert);
vertThumbRect.left = sbiVert.rcScrollBar.left;
vertThumbRect.top = sbiVert.rcScrollBar.top + sbiVert.xyThumbTop;
vertThumbRect.right = sbiVert.rcScrollBar.right;
vertThumbRect.bottom = sbiVert.rcScrollBar.top + sbiVert.xyThumbBottom;
if( PtInRect(&vertFreeScrollRect, pnt) == false || PtInRect(&vertThumbRect, pnt) != false ){
KillTimer(hwndWin, 5);
scrolling = NO_SCROLLING;
}
return;
}
void VerticalPageDown(POINT pnt){
SCROLLBARINFO sbiVert;
sbiVert.cbSize = sizeof(SCROLLBARINFO);
GetScrollBarInfo(hwndListView, OBJID_VSCROLL, &sbiVert);
vertThumbRect.left = sbiVert.rcScrollBar.left;
vertThumbRect.top = sbiVert.rcScrollBar.top + sbiVert.xyThumbTop;
vertThumbRect.right = sbiVert.rcScrollBar.right;
vertThumbRect.bottom = sbiVert.rcScrollBar.top + sbiVert.xyThumbBottom;
if( PtInRect(&vertFreeScrollRect, pnt) == false || PtInRect(&vertThumbRect, pnt) != false ){
KillTimer(hwndWin, 4);
scrolling = NO_SCROLLING;
}
return;
}
void HorizontalTrack(POINT pnt){
return;
}
void HorizontalLeftLine(POINT pnt){
return;
}
void HorizontalRightLine(POINT pnt){
return;
}
void HorizontalPageRight(POINT pnt){
return;
}
void HorizontalPageLeft(POINT pnt){
return;
}
void CheckMouseInVert(POINT pnt){
SCROLLBARINFO sbiVert;
sbiVert.cbSize = sizeof(SCROLLBARINFO);
GetScrollBarInfo(hwndListView, OBJID_VSCROLL, &sbiVert);
vertThumbRect.left = sbiVert.rcScrollBar.left;
vertThumbRect.top = sbiVert.rcScrollBar.top + sbiVert.xyThumbTop;
vertThumbRect.right = sbiVert.rcScrollBar.right;
vertThumbRect.bottom = sbiVert.rcScrollBar.top + sbiVert.xyThumbBottom;
if( scrolling == NO_SCROLLING ){
if( PtInRect(&vertScrollRect, pnt) == true ){
if( PtInRect(&vertUpArrowRect, pnt) == true ){
SetTimer(hwndWin, 2, 50, NULL);
scrolling = VERT_UP_LINE;
return;
}
if( PtInRect(&vertDownArrowRect, pnt) == true ){
SetTimer(hwndWin, 3, 50, NULL);
scrolling = VERT_DOWN_LINE;
return;
}
if( PtInRect(&vertThumbRect, pnt) == true ){
scrolling = VERT_TRACK;
SendMessage(hwndListView, WM_NCLBUTTONDOWN, (WPARAM)HTVSCROLL, (LPARAM)POINTTOPOINTS(pnt));
return;
}
if( pnt.y < vertThumbRect.top ){
SetTimer(hwndWin, 5, 50, NULL);
scrolling = VERT_PAGE_UP;
}
else{
SetTimer(hwndWin, 4, 50, NULL);
scrolling = VERT_PAGE_DOWN;
}
}
}
else{
(*scrollStatePointer[ scrolling - 1 ])( pnt );
}
return;
}
char CheckMouseInHorz(POINT pnt){
return;
}
void CheckMouseInBoth(POINT pnt){
return;
}