September 26, 2022, 06:35:23 AM

News:

IonicWind Snippit Manager 2.xx Released!  Install it on a memory stick and take it with you!  With or without IWBasic!


Drag-drop in report list view

Started by sapero, March 12, 2007, 06:10:34 AM

Previous topic - Next topic

0 Members and 1 Guest are viewing this topic.

sapero

March 12, 2007, 06:10:34 AM Last Edit: March 12, 2007, 06:14:09 AM by sapero
This is an example for custom implemented drag-drop for list view in report view. It implements custom drawn insertion mark, and allows auto scrolling the list view.
Currently only one item can be dragged.
The main class must detect beginning of drag-drop operation and notify the drag-class helper.
While the dragging is active, we call the helper to move the image and insertion mark in response to mouse events (mouse move + left button up)
CDlg::OnInitDialog()
{
m_lv1 = GetControl(IDC_LV1);
m_lv1->SetExtendedStyle(ALVS_EX_FULLROWSELECT|ALVS_EX_GRIDLINES|ALVS_EX_FLATSB|ALVS_EX_LABELTIP);
m_lv1->InsertColumn(0, "Window name",0,300);
m_lv1->InsertColumn(1, "Process",0,200);

// fill list view with some data
EnumWindows(&EnumWindowsProc, m_lv1);
}


CDlg::OnNotify(int code, int nID, NMHDR *pnmhdr), int
{
switch (nID)
{
case IDC_LV1:

if (code == LVN_BEGINDRAG)
{
// call CReportListviewDragHelper::StartDrag to start dragging
m_drag.StartDrag(this, m_lv1, pnmhdr);
}
else if (code == LVN_ENDDRAG) // custom notification
{
int newPos = m_drag.MoveItem(*(LVENDDRAG)pnmhdr.nItemFrom, *(LVENDDRAG)pnmhdr.nItemTo, 1);
m_lv1->SetSel(newPos);
}
}
}


CDlg::OnMouseMove(int x, int y, int flags)
{
m_drag.OnMouseMove(x,y);
}


CDlg::OnLButtonUp(int x, int y, int flags)
{
m_drag.OnLButtonUp();
}

The helper class uses a trick to draw the insertin mark. It makes first a backup of a portion of list view bitmap where the insertion mark will be drawn, then it draws the mark. While doing this, the dragged image is hidden. If the listview needs to scroll, we use the WM_VSCROLL instead one of the control specific messages, to be sure the listview is correctly repainted:
CReportListviewDragHelper::StartDrag(CWindow *parent, CListView *listview, NMLISTVIEW *nmlv)
{
POINT pt;
m_parent        = parent;
m_lv1           = listview;
m_hwndLock      = GetDesktopWindow();
m_InsertMarkItem = -1;
m_DragItemIndex = nmlv->iItem;
m_ItemCount     = SendMessage(m_lv1->m_hwnd, LVM_GETITEMCOUNT, 0, 0);
m_himl          = SendMessage(m_lv1->m_hwnd, LVM_CREATEDRAGIMAGE, m_DragItemIndex, &pt);
pt.x            = nmlv->ptAction.x;
pt.y            = nmlv->ptAction.y;

ImageList_BeginDrag(m_himl, 0, 0, 0);
ClientToScreen(m_lv1->m_hwnd, &pt);

GetCursorPos(&m_ptPos);
RECT rc;
rc.left = LVIR_BOUNDS;

SendMessage(m_lv1->m_hwnd, LVM_GETITEMRECT, m_DragItemIndex, &rc);
ClientToScreen(m_lv1->m_hwnd, &rc);

m_ptCursorOffset.x = pt.x - rc.left;
m_ptCursorOffset.y = pt.y - rc.top;

ImageList_DragEnter(m_hwndLock, m_ptPos.x - m_ptCursorOffset.x, m_ptPos.y - m_ptCursorOffset.y);

// change the cursor:
if (m_cursor) SetCursor(m_cursor);
SetCapture(m_parent->m_hwnd);

// create offscreen bitmap for insertion mark
GetClientRect(m_lv1->m_hwnd, &rc);
m_InsertMarkWidth = rc.right;
HDC dc = GetDC(m_lv1->m_hwnd);
m_dcCache = CreateCompatibleDC(dc);
m_bmCache = SelectObject(m_dcCache, CreateCompatibleBitmap(dc, m_InsertMarkWidth, 6));
ReleaseDC(m_lv1->m_hwnd, dc);
}

//---------------------------------------------
// ends the drag-drop operation

CReportListviewDragHelper::OnLButtonUp()
{
if (m_himl)
{
ImageList_DragShowNolock(false);
ImageList_EndDrag();
ImageList_Destroy(m_himl);
m_himl = 0;
ReleaseCapture();

// force OnNotify event
LVENDDRAG d;
d.hdr.hwndFrom = m_lv1->m_hwnd;
d.hdr.idFrom   = GetWindowLong(m_lv1->m_hwnd, GWL_ID);
d.hdr.code     = LVN_ENDDRAG;
d.nItemFrom    = m_DragItemIndex;
d.nItemTo      = m_InsertMarkItem;
SetInsertMark(-1, false, false);
SendMessage(m_parent->m_hwnd, WM_NOTIFY, d.hdr.idFrom, &d);

DeleteObject(SelectObject(m_dcCache, m_bmCache));
DeleteDC(m_dcCache);
}
}

//---------------------------------------------

CReportListviewDragHelper::OnMouseMove(word x,word y)
{
int item = -1;
if (m_himl)
{
POINT pt;
GetCursorPos(&m_ptPos);
pt.x = m_ptPos.x;
pt.y = m_ptPos.y;
ImageList_DragMove(m_ptPos.x - m_ptCursorOffset.x, m_ptPos.y - m_ptCursorOffset.y);

ScreenToClient(m_lv1->m_hwnd, pt);
LVHITTESTINFO ht;
ht.pt.x = pt.x;
ht.pt.y = pt.y;
SendMessage(m_lv1->m_hwnd, LVM_HITTEST, 0, &ht);
if (ht.flags == LVHT_ABOVE)
{
// scroll down
int topindex = SendMessage(m_lv1->m_hwnd, LVM_GETTOPINDEX, 0, 0);
if (topindex)
{
// hide drag-image
ImageList_DragShowNolock(false);
SetInsertMark(-1, false, false);
// scroll listview
SendMessage(m_lv1->m_hwnd, WM_VSCROLL, SB_LINEUP, 0);
// show drag-image
ImageList_DragShowNolock(true);
}
}
else if (ht.flags == LVHT_BELOW)
{
// scroll up
int lastitem = SendMessage(m_lv1->m_hwnd, LVM_GETTOPINDEX, 0, 0)
             + SendMessage(m_lv1->m_hwnd, LVM_GETCOUNTPERPAGE, 0, 0);

if (lastitem <= m_ItemCount)
{
// hide drag-image
ImageList_DragShowNolock(false);
SetInsertMark(-1, false, false);
// scroll listview
SendMessage(m_lv1->m_hwnd, WM_VSCROLL, SB_LINEDOWN, 0);
// show drag-image
ImageList_DragShowNolock(true);
}
}
else if ((ht.flags == LVHT_TOLEFT) || (ht.flags == LVHT_TORIGHT))
{
// scroll to left/right ?
}
else if (ht.flags & (LVHT_ONITEMICON | LVHT_ONITEMLABEL))
{
item = ht.iItem;
}
else // under the last item
{
RECT rc;
rc.left = LVIR_BOUNDS;
rc.top  = 2;
SendMessage(m_lv1->m_hwnd, LVM_GETSUBITEMRECT, m_ItemCount-1, &rc);
if ((y > rc.bottom) && (x < rc.right))
{
item = m_ItemCount-1;
}
}
if (item != -1)
{
RECT rcLv;
GetWindowRect(m_lv1->m_hwnd, &rcLv);
ScreenToClient(m_parent->m_hwnd, &rcLv);
// check if the mouse is before, over or after listview itema
rc.left = LVIR_BOUNDS;
rc.top  = 2;
SendMessage(m_lv1->m_hwnd, LVM_GETSUBITEMRECT, item, &rc);
y -= rcLv.top;
int pos = rc.top + ((rc.bottom - rc.top - 1) >> 1) + 1;
SetInsertMark(item, y > pos, true);
}
}
}

//---------------------------------------------

CReportListviewDragHelper::SetInsertMark(int item, BOOL After, BOOL hideImage)
{
int newitem = item + (After != false);

if (newitem != m_InsertMarkItem)
{
// hide drag-image
if (hideImage) ImageList_DragShowNolock(false);

if (m_InsertMarkItem != -1)
{
// remove previous selection mark
HDC dc = GetDC(m_lv1->m_hwnd);
BitBlt(dc, 0, m_InsertMarkY, m_InsertMarkWidth, 6, m_dcCache, 0,0, SRCCOPY);
ReleaseDC(m_lv1->m_hwnd, dc);
m_InsertMarkItem = -1;
}
if (item != -1)
{
m_InsertMarkItem = newitem;
m_InsertAfter    = (After != false);
// item position in listview coordinates
RECT rc;
rc.left = LVIR_BOUNDS;
SendMessage(m_lv1->m_hwnd, LVM_GETITEMRECT, item, &rc);

// default: over iteme
m_InsertMarkY = rc.top - 3;
// or under
if (After) m_InsertMarkY = rc.bottom - 3;

dc = GetDC(m_lv1->m_hwnd);
HPEN pen = SelectObject(dc, CreatePen(PS_SOLID, 1, rgb(1,2,91)));
BitBlt(m_dcCache, 0, 0, m_InsertMarkWidth, 6, dc, 0, m_InsertMarkY,SRCCOPY);
// line 1
MoveToEx(dc, 0, m_InsertMarkY+2, null);
LineTo(dc, m_InsertMarkWidth, m_InsertMarkY+2);
// line 2
MoveToEx(dc, 0, m_InsertMarkY+3, null);
LineTo(dc, m_InsertMarkWidth, m_InsertMarkY+3);
// triangle left
MoveToEx(dc, 0, m_InsertMarkY, null);
LineTo(dc, 0, m_InsertMarkY+6);
MoveToEx(dc, 1, m_InsertMarkY+1, null);
LineTo(dc, 1, m_InsertMarkY+5);
// triangle right
MoveToEx(dc, m_InsertMarkWidth-1, m_InsertMarkY, null);
LineTo(dc, m_InsertMarkWidth-1, m_InsertMarkY+6);
MoveToEx(dc, m_InsertMarkWidth-2, m_InsertMarkY+1, null);
LineTo(dc, m_InsertMarkWidth-2, m_InsertMarkY+5);

DeleteObject(SelectObject(dc, pen));
ReleaseDC(m_lv1->m_hwnd, dc);
}
// show drag-image
if (hideImage) ImageList_DragShowNolock(true);
}

}

ExMember001

Another great example !!
i was struggling trying to do this ;)

ExMember001

Hi Sapero,
Why are you including a lib file in the project and what's in it?
first time i see someone doing this, so i want to understand ;)