Perform default scrolling behavior when a user drags a list item over his scroller

Introduction:

I am not a native speaker of English, and I am also not a very experienced programmer.

I ran into a problem that is difficult for me to describe, so please keep this in mind when reading this question.

RELATED INFORMATION:

I am working on implementing drag and drop functions in listview. I just want to be able to reorder the lines inside the listview, there will be no drag and drop of items into other windows.

I do not want to use OLE for this, and I am not satisfied with the default implementation that I found on numerous links.

I have my own idea of ​​how I would like to do this, but my inexperience prevents me from realizing my thoughts.

I am developing in Visual Studio, in C ++ and raw WinAPI. I do not use any libraries, and I would not start using them now.

Problem:

I want to implement the following behavior:

The user presses the left mouse button and starts dragging an element → the user moves the mouse along the vertical scroll bar → the default scroll occurs.

Since scrollbar counts as a non-client area, this means that I have to somehow execute the default behavior for WM_NCMOUSEMOVE and WM_NCLBUTTONDOWN , but I don't know how to do it.

Let me better explain what I mean:

When you drag an item, it’s logical that the application indicates where it will be deleted when the mouse is over the item (in the client area of ​​the list).

When you drag an item along the scroll bar, it is obvious that the user cannot delete the item. Instead of specifying an invalid drop point (for example, by changing the cursor, for example, OLE), I want to do the following:

I want to execute the default scroll behavior (as if the user is not dragging the item at all). It would be as if the user was hovering over the scrollbar, pressing and holding the left mouse button and, optionally, moving the mouse up or down.

When the user moves the mouse from the scrollbar back to the client area of ​​the list, drag and drop continues.

Sscce

My English was not good enough to do the right research (as I usually do before posting here), and I don’t know any application that has this type of behavior, so it was very difficult for me to try to solve this on my own.

However, while strolling through the Raymond Chen blog, I came up with the idea.

The sample code below perfectly demonstrates the behavior that I talked about above. This is not ideal, but it is closest to the implementation of the behavior that I want.

Create an empty C ++ project and just copy / paste the code below.

Then try dragging the item along the scroll bar.

IMPORTANT: I ​​did not implement reordering of elements and did not change the shape of the cursor to keep the code minimal. The purpose of this SSCCE is to demonstrate the behavior that I want.

 #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; } 

Now start dragging the item and then move the mouse pointer above / below / above the finger of the scroll bar -> the behavior you are observing is the one I'm looking for.

This program has the disadvantage of:

When I try to drag an item back to the client area of ​​the list, instead of dragging the code, the scrollbar is still controlled. This is the default behavior, but I need to change it so that the drag code is executed instead.

This is the best I could do myself. Now you can see what I'm trying to do.

If you need more information, I will update the message. In the meantime, I will continue on my own and update this post if I make progress.

Thanks for your time and help. Best wishes.

+7
c ++ listview winapi scroll drag
source share
2 answers

The only way for this approach is to find a way to receive mouse move messages while scrolling. The mouse is "lost" , but the capture still remains in the listview (scrollbar). So, when the mouse leaves the scroll area, we need to free the capture (from the scroll bar) and set it again in listview. To do this, we will use the WH_MOUSE_LL hook when we receive the notification message LVN_BEGINDRAG and untie it when we finish dragging (this is for the vertical scrollbar. The idea is exactly the same for the horizontal):

 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); } 

In a subclass of 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 (default scroll)

 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); } 
+2
source share

I am adding another answer because the code is a bit longgggg, but not complicated. The following example for simplicity is a vertical scrollbar only . Check it out, and if it works correctly, I will also add for horizontal scrollbar :

The variables you will need are:

 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; 

Main window:

 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 Window:

 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); } 

Hook function:

 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); } 

Various functions:

 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; } 
0
source share

All Articles