Выполнить прокрутку по умолчанию, когда пользователь перетаскивает элемент списка на свою полосу прокрутки

ВСТУПЛЕНИЕ:

Я не являюсь носителем английского языка, и я не очень опытный программист.

Я столкнулся с проблемой, которую мне трудно описать, поэтому, пожалуйста, помните об этом, читая этот вопрос.

РЕЛЕВАНТНАЯ ИНФОРМАЦИЯ:

Я работаю над реализацией функции перетаскивания в списке. Я просто хочу, чтобы можно было переставить строки внутри списка, не будет перетаскивания элементов в другие окна.

Я не хочу использовать OLE для этого, и меня не устраивает реализация по умолчанию, которую я нашел по многочисленным ссылкам.

У меня есть своя собственная идея о том, как бы я хотел это сделать, но моя неопытность мешает мне реализовать свои мысли.

Я занимаюсь разработкой в ​​Visual Studio, на C ++ и сырым WinAPI. Я не использую никаких библиотек, и я не хотел бы начать использовать их сейчас.

ПРОБЛЕМА:

Я хочу реализовать следующее поведение:

Пользователь нажимает левую кнопку мыши и начинает перетаскивать элемент -> пользователь перемещает мышь по вертикальной полосе прокрутки -> происходит прокрутка по умолчанию.

Поскольку полоса прокрутки считается неклиентской областью, это означает, что я должен как-то выполнить поведение по умолчанию для WM_NCMOUSEMOVE а также WM_NCLBUTTONDOWN но я не знаю как это сделать.

Позвольте мне попытаться объяснить лучше, что я имею в виду:

При перетаскивании элемента логично, что приложение указывает, куда он будет отброшен, когда указатель мыши находится над элементом (в клиентской области списка).

Когда вы перетаскиваете элемент на полосу прокрутки, очевидно, что пользователь не может сбросить элемент там. Вместо указания недопустимой точки удаления (например, путем изменения курсора, как это делает OLE), я хочу выполнить следующее:

Я хочу выполнить поведение полосы прокрутки по умолчанию (как если бы пользователь вообще не перетаскивал элемент). Это было бы так, как если бы пользователь наводил курсор на полосу прокрутки, нажимал и удерживал левую кнопку мыши и, опционально, перемещал мышь вверх или вниз.

Когда пользователь перемещает мышь из полосы прокрутки обратно в клиентскую область списка, перетаскивание продолжается.

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

Теперь начните перетаскивать элемент, а затем переместите указатель мыши выше / ниже / выше большого пальца полосы прокрутки -> я наблюдаю поведение, которое вы наблюдаете.

Эта программа имеет недостаток:

Когда я пытаюсь перетащить элемент обратно в клиентскую область списка, вместо моего кода перетаскивания полоса прокрутки все еще контролируется. Это поведение по умолчанию, но мне нужно изменить его таким образом, чтобы вместо этого выполнялся мой перетаскиваемый код.

Это лучшее, что я смог сделать сам. Теперь вы можете видеть, что я пытаюсь сделать.

Если потребуется дополнительная информация, я обновлю свой пост. Тем временем я буду продолжать пытаться самостоятельно и обновлять этот пост, если я сделаю успехи.

Спасибо за ваше время и помощь. С наилучшими пожеланиями.

7

Решение

Единственный способ применения этого подхода — найти способ получить mouse move сообщения во время прокрутки. Мышь есть "lost" но захват все еще остается в виде списка (полоса прокрутки). Таким образом, когда мышь покидает область прокрутки, нам нужно освободить захват (с полосы прокрутки) и снова установить его на просмотр списка. Для этого мы применим 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);
}

В подклассе списка:

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

РЕДАКТИРОВАТЬ (прокрутка по умолчанию)

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

Другие решения

Я добавляю еще один ответ, потому что код немного длинный, но не сложный. Следующий пример для простоты предназначен для 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;
}
0

По вопросам рекламы [email protected]