SDI是單文檔接口應用程序的簡稱。本文要實現的是在視圖區域顯示一張圖片,而後在圖片表層顯示文字,而且文字跟隨鼠標移動。思考一下,能夠判斷這個問題一共分爲如下幾個部分:一、顯示圖片;二、找到鼠標的位置;三、插入文字;四、自動移動文字。如下分步驟說明。函數
一、首先是使用「打開」方式打開一張圖片並顯示,出於方便這裏僅僅針對bmp格式的圖片,具體是在CYourView類內部的OnDraw函數內添加以下代碼:this
if(GetDocument()->GetPathName() != "")//載入圖片 { HBITMAP bitmap; bitmap = (HBITMAP)LoadImage(AfxGetInstanceHandle(), GetDocument()->GetPathName(), IMAGE_BITMAP, 0, 0, LR_LOADFROMFILE|LR_CREATEDIBSECTION); HBITMAP OldBitmap; CBitmap cbitmap; cbitmap.Attach(bitmap); cbitmap.GetBitmap(&m_bitmap); CDC MemDC; MemDC.CreateCompatibleDC(pDC); CRect rect; GetClientRect(&rect); OldBitmap = (HBITMAP)MemDC.SelectObject(bitmap); pDC->BitBlt(0, 0, m_bitmap.bmWidth, m_bitmap.bmHeight, &MemDC, 0, 0, SRCCOPY); MemDC.SelectObject(OldBitmap); MemDC.DeleteDC(); }
其中m_bitmap是一個BITMAP對象。顯示圖片的方法是比較常見的,這裏再也不說明。spa
二、接下來須要知道有個消息處理函數OnMouseMove用來處理鼠標移動消息。因此首先要添加這個函數,這個能夠在Class Wizard中找到。先添加這個函數。code
OnMouseMove(UINT nFlags, CPoint point);
這個函數的第二個參數CPoint對象就是鼠標所在位置,可使用以下方法找到對應座標:orm
Xcoords = point.x;
Ycoords = point.y;
三、而後就要插入文字了,在SDI中特定位置插入文字其實很簡單,使用CDC->DrawText方法就能夠實現。這個函數能夠實如今指定的矩形框內添加格式化文本,有點像word中的插入對話框。具體語法以下:對象
int DrawText(const CString& str, LPRECT lpRect, UINT nFormat);
第一個參數是要輸出的CString對象,第二個參數是顯示文本的矩形區域,最後一個參數是文本的格式,好比居左居中等,一個使用例子以下:blog
CClientDC cdc(this); cdc.DrawText("text", CRect(left, top, reight, bottom), DT_LEFT);
四、插入文字實現以後,就要進行文字的移動了。首先我想到的是SDI中圖形的移動。好比畫一個矩形CDC->Rectangle方法,而後移動這個矩形框。有一個經常使用的圖形移動方式是進行異或畫圖,即便用CDC->SetROP2(R2_XORPEN)這個方法畫圖。使用與原來畫筆異或屏幕顏色再畫一遍,能夠實現清除已畫圖形的效果,所以圖形的移動也就是以下過程:接口
(1)異或畫圖圖片
(2)在原位置再次異或畫圖內存
(3)在新位置異或畫圖
(4)...
把畫圖與OnMouseMove結合就能容易實現圖形跟隨鼠標移動。可是一樣的方法能夠用於文本的移動嗎?答案是否認的。由於DrawText與其餘畫圖函數有本質的區別,由於其沒有用到畫筆,因此SetROP2這個方法整個對文本沒有效果。不過,有了移動圖形的方法,能夠容易想到所謂移動(圖形或者文本)的本質其實就是首先把原位置的內容清除,而後在新位置從新畫(好像是廢話)。
所以,移動文字的關鍵就在於如何把原位置的文字清除。(用DrawText插入一個空文本的方法實行不一樣的,由於不會影響已經插入的文本。)一個容易想到的方法是將原位置(DrawText對應的矩形)整個複製下來,而後在插入文字後從新繪製上去,就用覆蓋的方式清除了文字。因而有了以下的方法。
首先添加一個截圖函數,叫作ScreenShot。
CBitmap* CYourView::ScreenShot(CDC* pDC, CBitmap *memBitmap, int Xcoords, int Ycoords, int Width, int Height) { CDC memDC; memDC.CreateCompatibleDC(pDC); memBitmap->CreateCompatibleBitmap(pDC, Width, Height); memDC.SelectObject(memBitmap); memDC.BitBlt(0, 0, Width, Height, pDC, Xcoords, Ycoords, SRCCOPY); memDC.DeleteDC(); return memBitmap; }
這個函數的處理過程很簡單,將指定位置和大小的矩形內部的圖片內容保存起來。
具體的實現文本跟隨鼠標移動的代碼在OnMouseMove函數中:
void CYourView::OnMouseMove(UINT nFlags, CPoint point) { // TODO: 在此添加消息處理程序代碼和/或調用默認值 CClientDC cdc(this); Xcoords = point.x; Ycoords = point.y; if(m_loaded_flag) { if(m_first_show_flag == false) { OldX = Xcoords+10; OldY = Ycoords-10; memBitmap.DeleteObject(); memBmp = ScreenShot(&cdc, &memBitmap, OldX, OldY, 100, 50); cdc.SetBkMode(TRANSPARENT); cdc.DrawText("text",CRect(OldX,OldY, OldX+100, OldY+50),DT_LEFT); m_first_show_flag = true; } else { cdc.SetBkMode(TRANSPARENT);//去除原來位置的文字 BITMAP bm; memBmp->GetObject (sizeof(BITMAP),&bm); CDC MemDC; MemDC.CreateCompatibleDC(&cdc); CBitmap *pOldBitmap=MemDC.SelectObject(memBmp); cdc.BitBlt(OldX,OldY,bm.bmWidth ,bm.bmHeight,&MemDC,0,0,SRCCOPY); MemDC.SelectObject (pOldBitmap); MemDC.DeleteDC(); //新座標做爲下次使用的前一次座標 OldX = Xcoords+10; OldY = Ycoords-10; memBitmap.DeleteObject(); memBmp = ScreenShot(&cdc, &memBitmap, OldX, OldY, 100, 50); cdc.DrawText("text",CRect(OldX, OldY, OldX+100, OldY+50),DT_LEFT);//在新位置添加文字 } } CScrollView::OnMouseMove(nFlags, point); }
因爲第一次鼠標放上去的狀態跟後面的移動狀態有點區別,說以代碼中分開處理,所以還須要在OnDraw函數中if(GetDocument()->GetPathName() != "")語句內添加以下內容:
m_loaded_flag = true; if(m_first_show_flag == true) { Invalidate(); m_first_show_flag = false; }
當載入圖片後才顯示跟隨鼠標的文字。而且當移動圖片時刷新以清除文字。
總的來講整個過程是比較簡單的,曾經看到過一個採用OnTimer實現的文字跟隨鼠標移動的例子,其實就是不斷地刷新屏幕來顯示不一樣的文字,當有背景圖時會出現嚴重的閃爍,須要採用其餘方法來避免閃爍,可是使用個人方法就沒有這個問題。一小部分的重繪在視覺上是沒有影響的。
另外要注意使用到CDC對象的回收,若是使用CDC *pDC = this->DetDC()這樣的方法建立一個設備上下文則須要在不用的時候使用pDC->DeleteDC()刪除DC,否則就會致使嚴重的內存泄露。可是使用CClientDC cdc(this)建立的CDC對象系統可以自動回收,因此以上代碼中採用的是這種方法。