VC++學習之簡單繪圖

一、關於兩個窗口的消息處理

要在一個MFC的項目中畫圖,我們知道任何windows程序都是靠消息來進行處理的。所以在新建一個單文檔窗口的時候,我們會得到兩個窗口,一個框架窗口,一個視圖窗口,視圖窗口會覆蓋在框架窗口上面。這也就是說,我們一切對框架窗口的操作都只能夠被視圖窗口捕獲。


二、MFC消息映射機制

在任何一個類中如果要添加一個消息響應函數之後,都會在三個地方產生代碼。首先會在相應類的頭文件中添加函數原型,函數原型前面有afx_msg關鍵字修飾,表明這是一個消息處理函數。

其次,會在該類的源文件中BEGIN_MESSAGE_MAP宏之間定義的消息映射表中出現wm_lbuttondown,該類中的這個消息映射表,就是把一些消息與一些相應的消息處理函數關聯起來,通過這種機制,一旦有消息映射表中的消息出現,那麼就立刻調用相應的消息處理函數來進行處理。第三處,就是消息函數在該類的源文件中的定義了。具體的實現也在那裏。


MFC的消息映射機制和WIN32程序的消息映射機制有所不同,win32程序中,產生一個消息時,首先這個消息被操作系統放置到程序的消息隊列中,之後程序通過getmessage這個函數在消息隊列中依次取出消息,然後通過dispatchmessage函數交給操作系統處理,操作系統會調用相應的wndproc窗口過程函數對消息進行分類處理。




每一個類都有一個消息處理函數映射表,裏面有該類處理的消息已經對應的消息處理函數的指針。比如視圖類,與視圖類對象相關的,肯定有一個窗口(視圖類窗口),這個窗口的句柄與這個對象的指針一一對應。MFC在後臺會維護一個C++對象指針和窗口句柄的對照表。當我們收到某一個消息的時候,消息的第一個參數爲我們提供了消息產生的窗口句柄,然後通過這個窗口句柄可以找到相應的C++指針,然後這個指針會被傳遞到窗口類的基類Cwnd裏面,調用一個WindowPorc函數,這個函數裏面有一個叫做onwndmsg的函數,他會現在對應的子類的頭文件中找,是否有消息處理函數的原型,然後在源文件的消息映射表中找看是否有相應的處理函數,找到之後就會接着調用該消息處理函數。


三、繪圖

想要在MFC程序中進行繪圖,就必須要用到DC也就是設備描述表。它是一個包含設備信息的結構體。DC也是一種資源,爲了屏蔽底層物理繪圖設備的不同,給我們一個繪圖的標準。


繪圖的操作應該在我們擡起鼠標按鍵的那個消息的相應函數中,首先要得到一個HDC dc句柄dc用於畫圖,之後要將當前位置移動到起點的位置,之後用lineto函數畫圖,完成之後記得釋放掉hdc的資源。


void CDrawView::OnLButtonUp(UINT nFlags, CPoint point)
{
	// TODO: 在此添加消息處理程序代碼和/或調用默認值
	HDC hdc;
	hdc = ::GetDC(m_hWnd);
	MoveToEx(hdc,m_ptOrigin.x, m_ptOrigin.y,NULL);
	LineTo(hdc, point.x, point.y);
	::ReleaseDC(m_hWnd,hdc);
	CView::OnLButtonUp(nFlags, point);
}

處理利用SDK的函數之外,MFC封裝了一個類CDC專門用來進行繪圖工作。

	CDC *pDC = GetDC();
	pDC->MoveTo(m_ptOrigin);
	pDC->LineTo(point);
	ReleaseDC(pDC);
	CView::OnLButtonUp(nFlags, point);

CDC有一個子類叫做CClientDC,他也可以用於畫圖功能。比較方面的是,它在構造函數和析構函數裏面會分別的自動調用getdc和releasedc這兩個函數,所以利用這個對象進行繪圖,不用管獲得dc設備表還有最後釋放dc的事情。CClient這個類還有一個好處就是,想在那個窗口畫圖,那麼在創建對象的時候就要相應的給構造函數傳遞哪一個窗口所在類的類對象的指針。

所以,這時,如果我想在框架窗口上劃線的時候,只需要改變構造函數傳遞進去的類對象的指針,就能夠實現在框架窗口上劃線。因爲框架窗口是視圖窗口的父類,所以利用getparent函數就能夠得到父類對象的指針。然後獲得父類窗口上面的dc資源,這樣就可以吧線劃到父類的框架窗口中。所以說,在哪繪圖主要是看得到了哪個窗口的dc資源。一般來說我們繪圖都是在客戶區中進行,非客戶區是訪問不到的。


畫線還有一個類叫做CWindowDC類,他也是CDC的派生類,他有一個好處就是,畫圖的功能不但能夠訪問到客戶區還能夠訪問到非客戶區。


四、獲取畫筆

設備描述表裏面都有一個默認的黑色畫筆,如果想要用其他顏色或者其他種類的畫筆來進行繪圖的話uart,那麼要重新創建一個畫筆。

創建畫筆的類是CPen。裏面有三種構造函數可以供我們使用。

在進行畫圖的時候,即使創建了新的畫筆,也不能立刻進行使用需要將新的畫筆加入到設備描述表中纔可以。

所以,會利用selectobject函數,來把畫筆加入到設備描述表中。因爲有的時候,我們在局部和全局所需要的畫筆是不同的,當局部的繪圖任務完成之後,我們需要將畫筆還原到更改之前的樣式,這就需要在一開始 更改畫筆之前保存上一次的畫筆信息,恰好selectobject這個函數的返回值就是返回更改之前的畫筆信息,所以要先利用一個變量將這個老的畫筆信息保存起來,等到局部的繪圖人物完成之後,再利用selectobject函數將畫筆復原即可。


	CWindowDC dc(this);
	CPen pen(PS_SOLID, 10, RGB(255,0,0));
	CPen *ptr = dc.SelectObject(&pen);
	dc.MoveTo(m_ptOrigin);
	dc.LineTo(point);
	dc.SelectObject(ptr);
	CView::OnLButtonUp(nFlags, point);

五、畫刷繪圖


畫刷通常是用來填充一定的區域。畫刷的類是CBrush,然後利用一些成員函數可以完成填充工作。

	CBrush brsh(RGB(255,8,9));
	CWindowDC dc(this);
	dc.FillRect(CRect(m_ptOrigin, point), &brsh);

	CView::OnLButtonUp(nFlags, point);

創建了畫刷和設備描述表之後,通過調用fillrect函數可以知道,這是利用畫刷來填充矩形區域。利用CRect類來生成一個矩形,這個構造函數取矩形的兩個對角線的點進行繪圖,並給填充函數穿進去相應的畫刷即可。



六、位圖畫刷

對於位圖來說,有它專用的畫刷。

剛纔說畫刷的構造函數有三種形式,其中一種就是創建位圖畫刷的。畫刷只是一個工具,那麼實際畫的時候需要一個位圖。

位圖的創建有一個CBit類,創建這個對象之後還不能夠使用這個位圖,需要在資源中創建一個位圖資源,然後將這個資源與位圖對象關聯起來,才能夠利用這個位圖對象操作真正的位圖資源。


七、透明畫刷

Rectangle可以利用設備描述表中的這個函數來畫一個矩形,但是在畫兩個矩形重疊的時候,新的矩形會覆蓋住一部分老的矩形,這時因爲設備描述表中還有一個默認的 白色的畫刷用於填充我們所畫矩形的內部。如果我們希望矩形內部是透明的,也就是能夠看到遮擋的部分,這就需要透明畫刷來實現。

透明畫刷是的獲取是靠一個返回空畫刷句柄的函數叫做GetStockObject.繪圖的時候用的是畫刷對象,所以說要把這個函數返回的畫刷句柄轉化爲畫刷對象,需要藉助的是CBrush的FromHandle函數來實現。


八、連續線條

畫直線的方法我們都知道了,只要找到起點和終點就可以利用lineto方法來畫。如果要是畫連續的線條,就要在鼠標拖動的過程中不斷獲取到終點然後每移動到一個點就劃線。這樣就需要來響應鼠標的移動消息。並且,鼠標按下的消息開始視作開始畫圖,鼠標按鍵擡起的消息響應爲停止畫圖,爲了在相應鼠標移動消息的函數中得知是否進行畫圖操作,可以在全局設置一個bool變量,在其他兩個消息函數中通過改動這個變量來和畫圖的函數進行交互即可。


void CDrawView::OnMouseMove(UINT nFlags, CPoint point)
{
	// TODO: 在此添加消息處理程序代碼和/或調用默認值
	CPen pens(PS_SOLID,1,RGB(255,7,100));
	CClientDC dc(this);
	CPen *ptr = dc.SelectObject(&pens);
	if(m_bDraw == true)
	{
		dc.MoveTo(m_ptOrigin);
		dc.LineTo(point);
		m_ptOrigin = point;
	}
	dc.SelectObject(pens);
	CView::OnMouseMove(nFlags, point);
}



發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章