vc++ mfc中拖動效果的實現 藉助於CImageList

拖動是界面編程頻繁使用的一個效果,在windows系統下可謂大行其道。縱觀時下的應用軟件幾乎各個都支持各類各樣拖動的效果,windows7更是把拖動作到了極致。其實提及來拖動的實現也很簡單,對於有句柄的對象均可以經過MoveWindow或SetWindowPos實現位置變更,而沒有句柄的對象實現拖動無非就是作些參數修改,說到底實現拖動就是在OnLButtonDown、OnMouseMove和OnLButtonUp中處理數據,固然你可使用鼠標右鍵甚至中建消息來實現,基本原理是同樣的。
      基本原理是不難,不過要想作到效果二字就要動一番腦筋了。讓咱們來看看win7下的圖標拖放,鼠標會拖起一個半透明的圖標副本到你想要的位置,透過這個透明的圖標你能夠看到其下面的狀況,這樣的效果其實在windows的早期版本就已經實現了,它有着很好的用戶體驗。那麼咱們能不能輕鬆的實現相似的拖動效果呢?答案固然是確定的!最近看到論壇裏幾個討論拖動的帖子,正巧前一段時間本身也作了一些相關的工做,小研究了一下,因而就想把研究成果拿出來和你們分享,這樣纔有利於交流和進步嘛。之前我寫博客沒貼過效果圖,以致於不少網友下載示例代碼以後發現不是本身想要的東西,這個確實很差,在此我向你們表示歉意。此次把效果圖貼上,若是以爲這個效果很通常或者不是你所須要的那就不要浪費你寶貴的時間閱讀文章和下載代碼了。編程

18852_12901570587qaW

      從圖中能夠看出,個人小豬頭像是能夠被拖動的,半透明的那個就是拖動的副本,截圖的時候鼠標沒有截到,呵呵。爲了讓半透明效果可以明顯的看出來我特地爲對話框貼了張背景圖。被拖動的實際上是一個picture ctrl,也就是一個靜態控件,固然經過後面的介紹你們會發現這個方法的擴展性比較強,能夠應用於不少場合,甚至能夠應用於非控件的拖動對象的狀況。好了,效果就是這樣了,下面切入正題開始介紹實現方法。
      對於熟悉拖動效果製做的朋友們都應該知道,實現拖動有一個很簡單的方法就是經過CImageList。CImageList提供了BeginDrag、DragEnter、DragMove、DragLeave、EndDrag系列函數,分別在OnLButtonDown、OnMouseMove和OnLButtonUp等消息中合理調用這些函數就能夠輕鬆實現對CImageList的元素的拖動效果。那麼咱們要作的就是構造一個CImageList,使它的元素是咱們想要拖動的圖片,這樣就大功告成了。那怎樣獲取圖像呢?答案也很簡單,就是到被拖動的對象的DC中將所要拖動的區域拷貝到一個內存位圖中便可。具體到個人這個例子,我是這樣實現的:
      在OnLButtonDown中判斷鼠標是否在控件範圍內,若是在就將控件範圍內的DC內容拷貝到內存位圖中,而後建立CImageList將包含有控件內容的位圖添加進CImageList做爲其元素,接着經過這個ImageList實現拖動。具體代碼以下
windows

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);  
  
}
      這裏我說明兩個問題: 一是BeginDrag(0, m_ptOffset);的m_ptOffset參數,BeginDrag函數很容易理解了,就是進入拖動狀態,而m_ptOffset參數是拖動時鼠標相對於拖動圖標的偏移,注意是相對偏移。你們能夠本身改一下這個參數,好比改爲CPoint(0, 0)來感覺一下這個設置的做用。二是DragEnter(NULL, ptPut);的ptPut這個參數指定了初始拖動時圖標出現的位置,這裏注意這個位置不是圖標左上角的位置,而是左上角加上偏移後的位置。這個位置應用的也不是相對座標或客戶區座標,而是屏幕座標。
      接下來是移動的處理,其實很簡單就是一個DragMove函數。他有一個參數,也是一個點,意義和DragEnter的ptPut參數類似。例子中我限制了圖標不能超出窗口範圍,也是經過修改這個參數實現的。理論上咱們能夠拖着圖標在屏幕範圍內任意移動,不過結合這個例子若是在窗口範圍之外釋放鼠標那控件就找不到了,因此我作了限制,同時也能夠更好的理解m_ptMove參數的意義。具體實現能夠參考如下代碼。
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::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表達一下謝意。本文只是對拖動效果的一個簡單實現,若是有更好的方法還望你們賜教,在此謝過

void CDragDemoDlg::OnActivate(UINT nState, CWnd* pWndOther, BOOL bMinimized)
{
    CDialog::OnActivate(nState, pWndOther, bMinimized);

    if(nState==WA_INACTIVE)//當失去焦點後,
    {
        m_bIsLButtonDown    = FALSE;

        CImageList::DragLeave(this);
        CImageList::EndDrag();
        ReleaseCapture();
    }
}
相關文章
相關標籤/搜索