Ovo mi je prvi post, pa Vas molim da imate razumevanja.
Radim u MS Visual Studiju 2013, koristeći C++ i čist WinAPI.
Treba da napravim listview u report modu koji dozvoljava preuredjivanje itema.
Korisnici traže da se ova funkcionalnost izvodi poput uobičajenog drag / drop-a.
Ja sam ovaj zadatak u velikoj meri uspešno rešio, ali mi treba pomoc oko implementacije autoscroll-ovanja.
Vidite, autoscrol-ovanje treba da se izvrši kada korisnik pomeri miš van listview kontrole.
Na taj način se prikazuje item koji je do tada bio nevidljiv.
Dole Vam je demo aplikacija koja ilustruje o čemu govorim. Priložen je minimalan kod.
Iako je moja implementacija autoscroll-ovanja neispravna, primer dobro ilustruje o čemu govorim.
Izvinjavam se za komentare na Engleskom, na brzinu sam sastavljao ovaj post pa nisam stigao da ih prevedem na srpski.
Još jedna stvar, glavni deo čini subclass procedura DragAndDrop, i WM_NOTIFY hendler u proceduri glavnog prozora.
Sve ostalo se može ignorisati.
UPUTSTVO: Napravite prazan C++ projekat, kopirajte ovaj kod i to je to. Ne zaboravite da podesite projekat na UNICODE.
Code:
#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; // did user start drag and drop?
POINT ptOld; // needed to calculate to scroll up or down
// 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;
// do other cleanup, omitted for brevity
}
return DefSubclassProc(hwnd, message, wParam, lParam);
case WM_LBUTTONUP:
{
if (g_bDrag)
{
// we release the capture now and update relevant variables
g_bDrag = FALSE;
ReleaseCapture();
// perform item rearrangement, omitted for brevity
}
}
return DefSubclassProc(hwnd, message, wParam, lParam);
case WM_MOUSEMOVE:
{
if (g_bDrag)
{
// extract the coordinate from the LPARAM
POINT pt = { GET_X_LPARAM(lParam), GET_Y_LPARAM(lParam) };
// check if we are under item
LVHITTESTINFO lvhti = { 0 };
lvhti.pt = pt;
ListView_HitTest(hwnd, &lvhti);
// if outside listview, autoscroll
if (lvhti.iItem == -1)
{
// index of the top fully visible item
int topIndex = ListView_GetTopIndex(hwnd);
// we need item rectangle for scrolling calculations
RECT rcTopIndex = { 0 };
ListView_GetItemRect(hwnd, topIndex, &rcTopIndex, LVIR_BOUNDS);
// currentPoint - oldPoint determines scroll direction
// we just need to multiply it with item's rectangle height
// to get proper scrolling amount
ListView_Scroll(hwnd, 0, (pt.y - ptOld.y) * (rcTopIndex.bottom - rcTopIndex.top));
// update previous point
ptOld = pt;
}
else // we are inside listview, reset old point
ptOld = { 0 };
}
}
return DefSubclassProc(hwnd, message, wParam, lParam);
case WM_NCDESTROY:
::RemoveWindowSubclass(hwnd, DragAndDrop, uIdSubclass);
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:
{
ptOld = { 0 }; // user is not dragging listview item
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;
// listview must capture the mouse
SetCapture(((LPNMHDR)lParam)->hwndFrom);
}
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;
}
#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; // did user start drag and drop?
POINT ptOld; // needed to calculate to scroll up or down
// 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;
// do other cleanup, omitted for brevity
}
return DefSubclassProc(hwnd, message, wParam, lParam);
case WM_LBUTTONUP:
{
if (g_bDrag)
{
// we release the capture now and update relevant variables
g_bDrag = FALSE;
ReleaseCapture();
// perform item rearrangement, omitted for brevity
}
}
return DefSubclassProc(hwnd, message, wParam, lParam);
case WM_MOUSEMOVE:
{
if (g_bDrag)
{
// extract the coordinate from the LPARAM
POINT pt = { GET_X_LPARAM(lParam), GET_Y_LPARAM(lParam) };
// check if we are under item
LVHITTESTINFO lvhti = { 0 };
lvhti.pt = pt;
ListView_HitTest(hwnd, &lvhti);
// if outside listview, autoscroll
if (lvhti.iItem == -1)
{
// index of the top fully visible item
int topIndex = ListView_GetTopIndex(hwnd);
// we need item rectangle for scrolling calculations
RECT rcTopIndex = { 0 };
ListView_GetItemRect(hwnd, topIndex, &rcTopIndex, LVIR_BOUNDS);
// currentPoint - oldPoint determines scroll direction
// we just need to multiply it with item's rectangle height
// to get proper scrolling amount
ListView_Scroll(hwnd, 0, (pt.y - ptOld.y) * (rcTopIndex.bottom - rcTopIndex.top));
// update previous point
ptOld = pt;
}
else // we are inside listview, reset old point
ptOld = { 0 };
}
}
return DefSubclassProc(hwnd, message, wParam, lParam);
case WM_NCDESTROY:
::RemoveWindowSubclass(hwnd, DragAndDrop, uIdSubclass);
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:
{
ptOld = { 0 }; // user is not dragging listview item
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;
// listview must capture the mouse
SetCapture(((LPNMHDR)lParam)->hwndFrom);
}
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;
}
Ukoliko imate dodatna pitanja vrlo rado ću odgovoriti. Ako trebam da dodam još neke informacije nije problem.
Hvala na pažnji, iskreno se nadam da ćete mi pomoći.
Srdačan pozdrav.