您的位置:首页 > 其它

拖动效果的一个实现方案

2013-12-03 10:16 225 查看
转自http://blog.csdn.net/xianglitian/article/details/6023656

 

 

 拖动是界面编程频繁使用的一个效果,在windows系统下可谓大行其道。纵观时下的应用软件几乎各个都支持各种各样拖动的效果,windows7更是把拖动做到了极致。其实说起来拖动的实现也很简单,对于有句柄的对象都可以通过MoveWindow或SetWindowPos实现位置变动,而没有句柄的对象实现拖动无非就是做些参数修改,说到底实现拖动就是在OnLButtonDown、OnMouseMove和OnLButtonUp中处理数据,当然你可以使用鼠标右键甚至中建消息来实现,基本原理是一样的。

      基本原理是不难,不过要想做到效果二字就要动一番脑筋了。让我们来看看win7下的图标拖放,鼠标会拖起一个半透明的图标副本到你想要的位置,透过这个透明的图标你可以看到其下面的情况,这样的效果其实在windows的早期版本就已经实现了,它有着很好的用户体验。那么我们能不能轻松的实现类似的拖动效果呢?答案当然是肯定的!最近看到论坛里几个讨论拖动的帖子,正巧前一段时间自己也做了一些相关的工作,小研究了一下,于是就想把研究成果拿出来和大家分享,这样才有利于交流和进步嘛。以前我写博客没贴过效果图,以至于很多网友下载示例代码之后发现不是自己想要的东西,这个确实不好,在此我向大家表示歉意。这次把效果图贴上,如果觉得这个效果很一般或者不是你所需要的那就不要浪费你宝贵的时间阅读文章和下载代码了。




      从图中可以看出,我的小猪头像是可以被拖动的,半透明的那个就是拖动的副本,截图的时候鼠标没有截到,呵呵。为了让半透明效果能够明显的看出来我特意为对话框贴了张背景图。被拖动的其实是一个picture ctrl,也就是一个静态控件,当然通过后面的介绍大家会发现这个方法的扩展性比较强,可以应用于很多场合,甚至可以应用于非控件的拖动对象的情况。好了,效果就是这样了,下面切入正题开始介绍实现方法。

      对于熟悉拖动效果制作的朋友们都应该知道,实现拖动有一个很简单的方法就是通过CImageList。CImageList提供了BeginDrag、DragEnter、DragMove、DragLeave、EndDrag系列函数,分别在OnLButtonDown、OnMouseMove和OnLButtonUp等消息中合理调用这些函数就可以轻松实现对CImageList的元素的拖动效果。那么我们要做的就是构造一个CImageList,使它的元素是我们想要拖动的图片,这样就大功告成了。那怎样获取图像呢?答案也很简单,就是到被拖动的对象的DC中将所要拖动的区域拷贝到一个内存位图中即可。具体到我的这个例子,我是这样实现的:

      在OnLButtonDown中判断鼠标是否在控件范围内,如果在就将控件范围内的DC内容拷贝到内存位图中,然后创建CImageList将包含有控件内容的位图添加进CImageList作为其元素,接着通过这个ImageList实现拖动。具体代码如下

[cpp]
view plaincopyprint?

void CDragDemoDlg::OnLButtonDown(UINT nFlags, CPoint point)  
  
{  
  
    CRect   rectPic;  
  
    POINT   ptPut   = point;  
  
    GetDlgItem(IDC_STATIC_DEMO)->GetWindowRect(rectPic);  
  
    ClientToScreen(&ptPut);  
  
    if(rectPic.PtInRect(ptPut))  
  
    {  
  
        CBitmap     bitmapTemp, *pOldBitmap;  
  
        CDC         *pDC    = GetDlgItem(IDC_STATIC_DEMO)->GetDC(),  
  
                    *pMemDC = new CDC;  
  
        //创建位图内存   
  
        bitmapTemp.CreateCompatibleBitmap(pDC, rectPic.Width(), rectPic.Height());  
  
        pMemDC->CreateCompatibleDC(pDC);  
  
        pOldBitmap  = pMemDC->SelectObject(&bitmapTemp);  
  
        pMemDC->BitBlt(0, 0, rectPic.Width(), rectPic.Height(), pDC, 0, 0, SRCCOPY);  
  
        pMemDC->SelectObject(pOldBitmap);  
  
        delete  pMemDC;  
  
        ReleaseDC(pDC);  
  
        m_bIsLButtonDown    = TRUE;  
  
        m_ptOffset.x    = ptPut.x-rectPic.left;  
  
        m_ptOffset.y    = ptPut.y-rectPic.top;  
  
        m_imgDrag.DeleteImageList();  
  
        m_imgDrag.Create(rectPic.Width(), rectPic.Height(), ILC_COLOR32|ILC_MASK, 0, 1);  
  
        m_imgDrag.Add(&bitmapTemp, RGB(0, 0, 0));  
  
        m_imgDrag.BeginDrag(0, m_ptOffset);  
  
        m_imgDrag.DragEnter(NULL, ptPut);  
  
        SetCapture();  
  
    }  
  
    CDialog::OnLButtonDown(nFlags, point);  
  
}  

[cpp]
view plaincopyprint?

void CDragDemoDlg::OnMouseMove(UINT nFlags, CPoint point)  
  
{  
  
    if(m_bIsLButtonDown)  
  
    {  
  
        CRect       rtClient, rtPicture;  
  
        m_ptMove    = point;  
  
        GetDlgItem(IDC_STATIC_DEMO)->GetWindowRect(rtPicture);  
  
        GetClientRect(rtClient);  
  
        ClientToScreen(&rtClient);  
  
        ClientToScreen(&m_ptMove);  
  
        if(rtClient.left>m_ptMove.x-m_ptOffset.x)  
  
            m_ptMove.x  = rtClient.left+m_ptOffset.x;  
  
        if(rtClient.top>m_ptMove.y-m_ptOffset.y)  
  
            m_ptMove.y  = rtClient.top+m_ptOffset.y;  
  
        if(rtClient.right-rtPicture.Width()         m_ptMove.x  = rtClient.right-rtPicture.Width()+m_ptOffset.x;  
  
        if(rtClient.bottom-rtPicture.Height()           m_ptMove.y  = rtClient.bottom-rtPicture.Height()+m_ptOffset.y;  
  
        CImageList::DragMove(m_ptMove);  
  
    }  
  
    CDialog::OnMouseMove(nFlags, point);  
  
}  

void CDragDemoDlg::OnMouseMove(UINT nFlags, CPoint point)

{

if(m_bIsLButtonDown)

{

CRect		rtClient, rtPicture;

m_ptMove	= point;

GetDlgItem(IDC_STATIC_DEMO)->GetWindowRect(rtPicture);

GetClientRect(rtClient);

ClientToScreen(&rtClient);

ClientToScreen(&m_ptMove);

if(rtClient.left>m_ptMove.x-m_ptOffset.x)

m_ptMove.x	= rtClient.left+m_ptOffset.x;

if(rtClient.top>m_ptMove.y-m_ptOffset.y)

m_ptMove.y	= rtClient.top+m_ptOffset.y;

if(rtClient.right-rtPicture.Width()			m_ptMove.x	= rtClient.right-rtPicture.Width()+m_ptOffset.x;

if(rtClient.bottom-rtPicture.Height()			m_ptMove.y	= rtClient.bottom-rtPicture.Height()+m_ptOffset.y;

CImageList::DragMove(m_ptMove);

}

CDialog::OnMouseMove(nFlags, point);

}


      好了,现在就剩结束拖动状态的相关操作了,这部分就比较简单了,我代码中还加了一些容错判断和移动控件的操作,大家注意提取有效信息。

[cpp]
view plaincopyprint?

void CDragDemoDlg::OnLButtonUp(UINT nFlags, CPoint point)  
  
{  
  
    if(m_bIsLButtonDown)  
  
    {  
  
        CRect   rectPic;  
  
        CWnd*   pPic    = GetDlgItem(IDC_STATIC_DEMO);  
  
        ScreenToClient(&m_ptMove);  
  
        pPic->GetWindowRect(rectPic);  
  
        pPic->MoveWindow(m_ptMove.x-m_ptOffset.x, m_ptMove.y-m_ptOffset.y, rectPic.Width(), rectPic.Height());  
  
        m_bIsLButtonDown    = FALSE;  
  
        CImageList::DragLeave(this);  
  
        CImageList::EndDrag();  
  
        ReleaseCapture();  
  
        pPic->Invalidate();  
  
    }  
  
    CDialog::OnLButtonUp(nFlags, point);  
  
}  

void CDragDemoDlg::OnLButtonUp(UINT nFlags, CPoint point)

{

if(m_bIsLButtonDown)

{

CRect rectPic;

CWnd* pPic = GetDlgItem(IDC_STATIC_DEMO);

ScreenToClient(&m_ptMove);

pPic->GetWindowRect(rectPic);

pPic->MoveWindow(m_ptMove.x-m_ptOffset.x, m_ptMove.y-m_ptOffset.y, rectPic.Width(), rectPic.Height());

m_bIsLButtonDown = FALSE;

CImageList::DragLeave(this);

CImageList::EndDrag();

ReleaseCapture();

pPic->Invalidate();

}

CDialog::OnLButtonUp(nFlags, point);

}

       到此拖动效果就实现了,最后再说一点,例子中有关于鼠标捕获和释放的操作,目的是为了当鼠标离开窗口范围仍然可以响应,而且针对窗口有可能被其它程序抢夺焦点的情况,例程中专门处理了OnActivate消息,具体实现可以参考示例源码,就不在这里赘述了。应该说通过CImageList实现拖动操作是十分方便的,而且效果也很好。据说VS2008下的CImageList还支持PNG,那样就可以做出更炫的拖动效果了。

       再说一个题外话,本文是通过Windows Live Writer编辑并发布的,这个工具是论坛里的muzizongheng推荐的,确实很好用,在此也对muzizongheng表达一下谢意。本文只是对拖动效果的一个简单实现,如果有更好的方法还望大家赐教,在此谢过。

 
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: