CListCtrl实现列表项拖拽

本文介绍了一种在Windows应用程序中实现列表视图控件拖放功能的方法,通过定义特定的数据成员并响应LVN_BEGINDRAG等消息来创建和移动拖动图像,最终在父窗口中完成拖动项的更新。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

#define MAX_DRAG_SIZE 128
#define MAX_DRAG_SIZE_2 (MAX_DRAG_SIZE/2)


添加数据成员 

CImageList* m_pDragImage = NULL;//拖动时的图片
int m_nDragDrop, m_nDrag[MAX_DRAG_SIZE];//用于记录被拖动条目的index以及拖动到的位置


注:本文中仅以拖动1条为例,重在介绍方法,如果需要可作相应的扩展

响应消息LVN_BEGINDRAG, WM_MOUSEMOVE,WM_LBUTTONUP
ON_NOTIFY_REFLECT(LVN_BEGINDRAG, OnBegindrag)


void CMyList::OnBegindrag(NMHDR *pNMHDR, LRESULT *pResult)
{
   LPNMHEADER phdr = reinterpret_cast(pNMHDR);
   // TODO: 在此添加控件通知处理程序代码
   NM_LISTVIEW* pNMListView = (NM_LISTVIEW*)pNMHDR;
   *pResult = 0;

   CPoint ptAction( pNMListView->ptAction );

   for ( int i = 0; i < MAX_DRAG_SIZE; i++ ) 
{
   m_nDrag[i] = -1;
}

   m_pDragImage = CreateDragImage( ptAction );//绘制图像

   if ( m_pDragImage == NULL ) return;
   m_nDragDrop = -1;

   UpdateWindow();

   CRect rcClient;
   GetClientRect( &rcClient );
   ClientToScreen( &rcClient );
   ClipCursor( &rcClient );
   SetCapture();

   SetFocus();
   UpdateWindow();

   m_pDragImage->DragEnter( this, ptAction );

   *pResult = 0;
}

CImageList* CMyList::CreateDragImage(const CPoint& ptMouse)
{
CRect rcClient, rcOne, rcAll( 32000, 32000, -32000, -32000 );
int nIndex;

if ( GetSelectedCount() == 0 ) return NULL;

SetFocus();
GetClientRect( &rcClient );

int nCount = 0;
for ( nIndex = -1 ; ( nIndex = GetNextItem( nIndex, LVNI_SELECTED ) ) >= 0 && MAX_DRAG_SIZE >= nCount; )
{
   m_nDrag[ nCount++ ] = nIndex;
   GetItemRect( nIndex, rcOne, LVIR_BOUNDS );

   if ( rcOne.IntersectRect( &rcClient, &rcOne ) )
   {
    rcAll.left   = min( rcAll.left, rcOne.left );
    rcAll.top   = min( rcAll.top, rcOne.top );
    rcAll.right   = max( rcAll.right, rcOne.right );
    rcAll.bottom = max( rcAll.bottom, rcOne.bottom );
   }

   SetItemState( nIndex, 0, LVIS_FOCUSED );
}

BOOL bClipped = rcAll.Height() > MAX_DRAG_SIZE;

if ( bClipped )
{
   rcAll.left   = max( rcAll.left, ptMouse.x - MAX_DRAG_SIZE_2 );
   rcAll.right   = max( rcAll.right, ptMouse.x + MAX_DRAG_SIZE_2 );
   rcAll.top   = max( rcAll.top, ptMouse.y - MAX_DRAG_SIZE_2 );
   rcAll.bottom = max( rcAll.bottom, ptMouse.y + MAX_DRAG_SIZE_2 );
}

CClientDC dcClient( this );
CBitmap bmAll, bmDrag;
CDC dcAll, dcDrag;

if ( ! dcAll.CreateCompatibleDC( &dcClient ) )
   return NULL;
if ( ! bmAll.CreateCompatibleBitmap( &dcClient, rcClient.Width(), rcClient.Height() ) )
   return NULL;

if ( ! dcDrag.CreateCompatibleDC( &dcClient ) )
   return NULL;
if ( ! bmDrag.CreateCompatibleBitmap( &dcClient, rcAll.Width(), rcAll.Height() ) )
   return NULL;

CBitmap *pOldAll = dcAll.SelectObject( &bmAll );

COLORREF crDrag = RGB( 250, 255, 250 );
dcAll.FillSolidRect( &rcClient, crDrag );

COLORREF crBack = GetBkColor();
SetBkColor( crDrag );
SendMessage( WM_PAINT, (WPARAM)dcAll.GetSafeHdc() );
SetBkColor( crBack );

CBitmap *pOldDrag = dcDrag.SelectObject( &bmDrag );

dcDrag.FillSolidRect( 0, 0, rcAll.Width(), rcAll.Height(), crDrag );

CRgn pRgn;

if ( bClipped )
{
   CPoint ptMiddle( ptMouse.x - rcAll.left, ptMouse.y - rcAll.top );
   pRgn.CreateEllipticRgn( ptMiddle.x - MAX_DRAG_SIZE_2, ptMiddle.y - MAX_DRAG_SIZE_2,
    ptMiddle.x + MAX_DRAG_SIZE_2, ptMiddle.y + MAX_DRAG_SIZE_2 );
   dcDrag.SelectClipRgn( &pRgn );
}

for ( nIndex = -1 ; ( nIndex = GetNextItem( nIndex, LVNI_SELECTED ) ) >= 0 ; )
{
   GetItemRect( nIndex, rcOne, LVIR_BOUNDS );

   if ( rcOne.IntersectRect( &rcAll, &rcOne ) )
   {
    dcDrag.BitBlt( rcOne.left - rcAll.left, rcOne.top - rcAll.top,
     rcOne.Width(), rcOne.Height(), &dcAll, rcOne.left, rcOne.top, SRCCOPY );
   }
}

dcDrag.SelectObject( pOldDrag );
dcAll.SelectObject( pOldAll );

dcDrag.DeleteDC();
bmAll.DeleteObject();
dcAll.DeleteDC();

CImageList* pAll = new CImageList();
pAll->Create( rcAll.Width(), rcAll.Height(), ILC_COLOR16|ILC_MASK, 1, 1 );
pAll->Add( &bmDrag, crDrag );

bmDrag.DeleteObject();

pAll->BeginDrag( 0, ptMouse - rcAll.TopLeft() );

return pAll;
}

void CMyList::OnMouseMove(UINT nFlags, CPoint point)//移动的视觉效果
{
// TODO: 在此添加消息处理程序代码和/或调用默认值
int nHit = HitTest( point );

if ( m_pDragImage != NULL )
{
   m_pDragImage->DragMove( point );

   if ( nHit != m_nDragDrop )
   {
    CImageList::DragShowNolock( FALSE );
    if ( m_nDragDrop >= 0 ) SetItemState( m_nDragDrop, 0, LVIS_DROPHILITED );
    m_nDragDrop = nHit;
    if ( m_nDragDrop >= 0 ) SetItemState( m_nDragDrop, LVIS_DROPHILITED, LVIS_DROPHILITED );
    UpdateWindow();
    CImageList::DragShowNolock( TRUE );
   }
}

CListCtrl::OnMouseMove(nFlags, point);
}

void CMyList::OnLButtonUp(UINT nFlags, CPoint point)
{
// TODO: 在此添加消息处理程序代码和/或调用默认值
if ( m_pDragImage == NULL )
{
   CListCtrl::OnLButtonUp( nFlags, point );
   return;

ClipCursor( NULL );
ReleaseCapture();

m_pDragImage->DragLeave( this );
m_pDragImage->EndDrag();
delete m_pDragImage;
m_pDragImage = NULL;

if ( m_nDragDrop >= 0 )
{
   SetItemState( m_nDragDrop, 0, LVIS_DROPHILITED );
   /*由于本类中只是加了拖动的效果和记录拖动的相关信息,当拖动结束以后并没有实际效果。故需要传递消息 在其父窗口中进行拖动的实际处理*/
   NM_LISTVIEW pNotify;
   pNotify.hdr.hwndFrom = GetSafeHwnd();
   pNotify.hdr.idFrom   = GetDlgCtrlID();
   pNotify.hdr.code   = HDN_ENDDRAG;
   pNotify.iItem    = m_nDragDrop;//拖动到的index
   pNotify.lParam    = (LPARAM)m_nDrag;//被拖动的Item
   GetOwner()->SendMessage( WM_NOTIFY, pNotify.hdr.idFrom, (LPARAM)&pNotify );
}
}

在其listbox的父窗口中进行以下处理
ON_NOTIFY(HDN_ENDDRAG, IDC_LIST, OnEnddragList)//IDC_LIST为list控件的ID
void CDlgXX::OnEnddragList(NMHDR *pNMHDR, LRESULT *pResult)
{

NM_LISTVIEW* pNotify = (NM_LISTVIEW *)pNMHDR;
int* pBegin = (int*)pNotify->lParam;
int nEnd = pNotify->iItem;
m_bChange = TRUE;

CString strTmp;
BOOL bAhead = pBegin[0] > nEnd ? TRUE : FALSE;
for ( int i = 0; i < MAX_DRAG_SIZE && pBegin[i] >= 0; i++ )
{
   if ( pBegin[i] == nEnd )
   {
    nEnd++;
    continue;
   }
   strTmp = m_ctrlList.GetItemText( pBegin[i], 0 );
   m_ctrlList.DeleteItem( pBegin[i] );
  
   /*向上托第一条插在nEnd之前,向下托第一条插在nEnd之后,所拖动的条目保持原来的顺序*/
   if ( bAhead )
   {
    if ( pBegin[i] < nEnd )
     nEnd--;
    for ( int j = i + 1; j < MAX_DRAG_SIZE && pBegin[j] >= 0; j++)
    {
     if ( pBegin[j] <= nEnd )
      pBegin[j]--;
    }
    m_ctrlList.InsertItem( nEnd, strTmp );
    nEnd++;
   }
   else
   {
    if ( pBegin[i] >= nEnd )
     nEnd++;

    for ( int j = i + 1; j < MAX_DRAG_SIZE && pBegin[j] >= 0; j++)
     if ( pBegin[j] <= nEnd )
      pBegin[j]--;
    m_ctrlList.InsertItem( nEnd, strTmp );
    }
}
  
  
*pResult = 0;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值