這個問題很多朋友都問過我,當然流汗是必須的,但同時如果按照某種思路進行有計劃的學習就會起到更好的效果。萬事開頭難,為了幫助朋友們更快的掌握VC開發(fā),下面我將自己的一點體會講一下:
1、需要有好的C/C++基礎(chǔ)。正所謂“磨刀不誤砍柴工”,最開始接觸VC時不要急于開始Windows程序開發(fā),而是應(yīng)該進行一些字符界面程序的編寫。這樣做的目的主要是增加對語言的熟悉程度,同時也訓練自己的思維和熟悉一些在編程中常犯的錯誤。更重要的是理解并能運用C++的各種特性,這些在以后的開發(fā)中都會有很大的幫助,特別是利用MFC進行開發(fā)的朋友對C++一定要能熟練運用。
2、理解Windows的消息機制,窗口句柄和其他GUI句柄的含義和用途。了解和MFC各個類功能相近的API函數(shù)。
3、一定要理解MFC中消息映射的作用。
4、訓練自己在編寫代碼時不使用參考書而是使用Help Online。
5、記住一些常用的消息名稱和參數(shù)的意義。
6、學會看別人的代碼。
7、多看書,少買書,買書前一定要慎重。
8、閑下來的時候就看參考書。
9、多來我的主頁。^O^
后面幾條是我個人的一點意見,你可以根據(jù)需要和自身的情況選用適用于自己的方法。
此外我將一些我在選擇參考書時的原則:
對于初學者:應(yīng)該選擇一些內(nèi)容比較全面的書籍,并且書籍中的內(nèi)容應(yīng)該以合理的方式安排,在使用該書時可以達到循序漸進的效果,書中的代碼要有詳細的講解。盡量買翻譯的書,因為這些書一般都比較易懂,而且語言比較輕松。買書前一定要慎重如果買到不好用的書可能會對自己的學習積極性產(chǎn)生打擊。
對于已經(jīng)掌握了VC的朋友:這種程度的開發(fā)者應(yīng)該加深自己對系統(tǒng)原理,技術(shù)要點的認識。需要選擇一些對原理講解的比較透徹的書籍,這樣一來才會對新技術(shù)有更多的了解,最好書中對技術(shù)的應(yīng)用有一定的闡述。盡量選擇示范代碼必較精簡的書,可以節(jié)約銀子。
此外最好涉獵一些輔助性的書籍。
Windows系統(tǒng)是一個消息驅(qū)動的OS,什么是消息呢?我很難說得清楚,也很難下一個定義(誰在噓我),我下面從不同的幾個方面講解一下,希望大家看了后有一點了解。
1、消息的組成:一個消息由一個消息名稱(UINT),和兩個參數(shù)(WPARAM,LPARAM)。當用戶進行了輸入或是窗口的狀態(tài)發(fā)生改變時系統(tǒng)都會發(fā)送消息到某一個窗口。例如當菜單轉(zhuǎn)中之后會有WM_COMMAND消息發(fā)送,WPARAM的高字中(HIWORD(wParam))是命令的ID號,對菜單來講就是菜單ID。當然用戶也可以定義自己的消息名稱,也可以利用自定義消息來發(fā)送通知和傳送數(shù)據(jù)。
2、誰將收到消息:一個消息必須由一個窗口接收。在窗口的過程(WNDPROC)中可以對消息進行分析,對自己感興趣的消息進行處理。例如你希望對菜單選擇進行處理那么你可以定義對WM_COMMAND進行處理的代碼,如果希望在窗口中進行圖形輸出就必須對WM_PAINT進行處理。
3、未處理的消息到那里去了:M$為窗口編寫了默認的窗口過程,這個窗口過程將負責處理那些你不處理消息。正因為有了這個默認窗口過程我們才可以利用Windows的窗口進行開發(fā)而不必過多關(guān)注窗口各種消息的處理。例如窗口在被拖動時會有很多消息發(fā)送,而我們都可以不予理睬讓系統(tǒng)自己去處理。
4、窗口句柄:說到消息就不能不說窗口句柄,系統(tǒng)通過窗口句柄來在整個系統(tǒng)中唯一標識一個窗口,發(fā)送一個消息時必須指定一個窗口句柄表明該消息由那個窗口接收。而每個窗口都會有自己的窗口過程,所以用戶的輸入就會被正確的處理。例如有兩個窗口共用一個窗口過程代碼,你在窗口一上按下鼠標時消息就會通過窗口一的句柄被發(fā)送到窗口一而不是窗口二。
5、示例:下面有一段偽代碼演示如何在窗口過程中處理消息
LONG yourWndProc(HWND hWnd,UINT uMessageType,WPARAM wP,LPARAM)
{ switch(uMessageType) {//使用SWITCH語句將各種消息分開 case(WM_PAINT): doYourWindow(...);//在窗口需要重新繪制時進行輸出 break; case(WM_LBUTTONDOWN): doYourWork(...);//在鼠標左鍵被按下時進行處理 break; default: callDefaultWndProc(...);//對于其它情況就讓系統(tǒng)自己處理 break; } } 接下來談?wù)勈裁词窍C制:系統(tǒng)將會維護一個或多個消息隊列,所有產(chǎn)生的消息都回被放入或是插入隊列中。系統(tǒng)會在隊列中取出每一條消息,根據(jù)消息的接收句柄而將該消息發(fā)送給擁有該窗口的程序的消息循環(huán)。每一個運行的程序都有自己的消息循環(huán),在循環(huán)中得到屬于自己的消息并根據(jù)接收窗口的句柄調(diào)用相應(yīng)的窗口過程。而在沒有消息時消息循環(huán)就將控制權(quán)交給系統(tǒng)所以Windows可以同時進行多個任務(wù)。下面的偽代碼演示了消息循環(huán)的用法: while(1)
{ id=getMessage(...); if(id == quit) break; translateMessage(...); } 當該程序沒有消息通知時getMessage就不會返回,也就不會占用系統(tǒng)的CPU時間。 下圖為消息投遞模式
在16位的系統(tǒng)中系統(tǒng)中只有一個消息隊列,所以系統(tǒng)必須等待當前任務(wù)處理消息后才可以發(fā)送下一消息到相應(yīng)程序,如果一個程序陷如死循環(huán)或是耗時操作時系統(tǒng)就會得不到控制權(quán)。這種多任務(wù)系統(tǒng)也就稱為協(xié)同式的多任務(wù)系統(tǒng)。Windows3.X就是這種系統(tǒng)。 而32位的系統(tǒng)中每一運行的程序都會有一個消息隊列,所以系統(tǒng)可以在多個消息隊列中轉(zhuǎn)換而不必等待當前程序完成消息處理就可以得到控制權(quán)。這種多任務(wù)系統(tǒng)就稱為搶先式的多任務(wù)系統(tǒng)。Windows95/NT就是這種系統(tǒng)。
MFC借助C++的優(yōu)勢為Windows開發(fā)開辟了一片新天地,同時也借助ApplicationWizzard使開發(fā)者擺脫離了那些每次都必寫基本代碼,借助ClassWizard和消息映射使開發(fā)者擺脫了定義消息處理時那種混亂和冗長的代碼段。更令人興奮的是利用C++的封裝功能使開發(fā)者擺脫Windows中各種句柄的困擾,只需要面對C++中的對象,這樣一來使開發(fā)更接近開發(fā)語言而遠離系統(tǒng)。(但我個人認為了解系統(tǒng)原理對開發(fā)很有幫助)
正因為MFC是建立在C++的基礎(chǔ)上,所以我強調(diào)C/C++語言基礎(chǔ)對開發(fā)的重要性。利用C++的封裝性開發(fā)者可以更容易理解和操作各種窗口對象;利用C++的派生性開發(fā)者可以減少開發(fā)自定義窗口的時間和創(chuàng)造出可重用的代碼;利用虛擬性可以在必要時更好的控制窗口的活動。而且C++本身所具備的超越C語言的特性都可以使開發(fā)者編寫出更易用,更靈活的代碼。
在MFC中對消息的處理利用了消息映射的方法,該方法的基礎(chǔ)是宏定義實現(xiàn),通過宏定義將消息分派到不同的成員函數(shù)進行處理。下面簡單講述一下這種方法的實現(xiàn)方法:
代碼如下
BEGIN_MESSAGE_MAP(CMainFrame, CFrameWnd) //{{AFX_MSG_MAP(CMainFrame) ON_WM_CREATE() //}}AFX_MSG_MAP ON_COMMAND(ID_FONT_DROPDOWN, DoNothing) END_MESSAGE_MAP() 經(jīng)過編譯后,代碼被替換為如下形式(這只是作講解,實際情況比這復雜得多): //BEGIN_MESSAGE_MAP(CMainFrame, CFrameWnd) CMainFrame::newWndProc(...) { switch(...) { //{{AFX_MSG_MAP(CMainFrame) // ON_WM_CREATE() case(WM_CREATE): OnCreate(...); break; //}}AFX_MSG_MAP // ON_COMMAND(ID_FONT_DROPDOWN, DoNothing) case(WM_COMMAND): if(HIWORD(wP)==ID_FONT_DROPDOWN) { DoNothing(...); } break; //END_MESSAGE_MAP() } } newWndProc就是窗口過程只要是該類的實例生成的窗口都使用該窗口過程。
所以了解了Windows的消息機制在加上對消息映射的理解就很容易了解MFC開發(fā)的基本思路了。
以下是我在最初學習VC時所常用的開發(fā)思路和方法,希望能對初學VC的朋友有所幫助和啟發(fā)。 1、開發(fā)需要讀寫文件的應(yīng)用程序并且有簡單的輸入和輸出可以利用單文檔視結(jié)構(gòu)。
2、開發(fā)注重交互的簡單應(yīng)用程序可以使用對話框為基礎(chǔ)的窗口,如果文件讀寫簡單這可利用CFile進行。
3、開發(fā)注重交互并且文件讀寫復雜的的簡單應(yīng)用程序可以利用以CFormView為基礎(chǔ)視的單文檔視結(jié)構(gòu)。
4、利用對話框得到用戶輸入的數(shù)據(jù),在等級提高后可使用就地輸入。
5、在對多文檔要求不強烈時盡量避免多文檔視結(jié)構(gòu),可以利用分隔條產(chǎn)生單文檔多視結(jié)構(gòu)。
6、在要求在多個文檔間傳遞數(shù)據(jù)時使用多文檔視結(jié)構(gòu)。
7、學會利用子窗口,并在自定義的子窗口包含多個控件達到封裝功能的目的。
8、盡量避免使用多文檔多視結(jié)構(gòu)。
9、不要使用多重繼承并盡量減少一個類中封裝過多的功能。CRect:用來表示矩形的類,擁有四個成員變量:top left bottom right。分別表是左上角和右下角的坐標??梢酝ㄟ^以下的方法構(gòu)造:
CRect( int l, int t, int r, int b ); 指明四個坐標
CRect( const RECT& srcRect ); 由RECT結(jié)構(gòu)構(gòu)造
CRect( LPCRECT lpSrcRect ); 由RECT結(jié)構(gòu)構(gòu)造
CRect( POINT point, SIZE size ); 有左上角坐標和尺寸構(gòu)造
CRect( POINT topLeft, POINT bottomRight ); 有兩點坐標構(gòu)造
下面介紹幾個成員函數(shù):
int Width( ) const; 得到寬度
int Height( ) const; 得到高度 CSize Size( ) const; 得到尺寸 CPoint& TopLeft( ); 得到左上角坐標 CPoint& BottomRight( ); 得到右下角坐標 CPoint CenterPoint( ) const; 得當中心坐標 此外矩形可以和點(CPoint)相加進行位移,和另一個矩形相加得到“并”操作后的矩形。 CPoint:用來表示一個點的坐標,有兩個成員變量:x y。 可以和另一個點相加。
CString:用來表示可變長度的字符串。使用CString可不指明內(nèi)存大小,CString會根據(jù)需要自行分配。下面介紹幾個成員函數(shù):
GetLength 得到字符串長度
GetAt 得到指定位置處的字符 operator + 相當于strcat void Format( LPCTSTR lpszFormat, ... ); 相當于sprintf Find 查找指定字符,字符串 Compare 比較 CompareNoCase 不區(qū)分大小寫比較 MakeUpper 改為小寫 MakeLower 改為大寫 CStringArray:用來表示可變長度的字符串數(shù)組。數(shù)組中每一個元素為CString對象的實例。下面介紹幾個成員函數(shù):
Add 增加CString
RemoveAt 刪除指定位置CString對象 RemoveAll 刪除數(shù)組中所有CString對象 GetAt 得到指定位置的CString對象 SetAt 修改指定位置的CString對象 InsertAt 在某一位置插入CString對象 常用宏
RGB
TRACE
ASSERT
VERIFY
常用函數(shù) CWindApp* AfxGetApp();
HINSTANCE AfxGetInstanceHandle( );
HINSTANCE AfxGetResourceHandle( );
int AfxMessageBox( LPCTSTR lpszText, UINT nType = MB_OK, UINT nIDHelp = 0 );用于彈出一個消息框
2.1 和GUI有關(guān)的各種對象 在Windows中有各種GUI對象(不要和C++對象混淆),當你在進行繪圖就需要利用這些對象。而各種對象都擁有各種屬性,下面分別講述各種GUI對象和擁有的屬性。 字體對象CFont用于輸出文字時選用不同風格和大小的字體。可選擇的風格包括:是否為斜體,是否為粗體,字體名稱,是否有下劃線等。顏色和背景色不屬于字體的屬性。關(guān)于如何創(chuàng)建和使用字體在2.2 在窗口中輸出文字中會詳細講解。
刷子CBrush對象決定填充區(qū)域時所采用的顏色或模板。對于一個固定色的刷子來講它的屬性為顏色,是否采用網(wǎng)格和網(wǎng)格的類型如水平的,垂直的,交叉的等。你也可以利用8*8的位圖來創(chuàng)建一個自定義模板的刷子,在使用這種刷子填充時系統(tǒng)會利用位圖逐步填充區(qū)域。關(guān)于如何創(chuàng)建和使用刷子在2.3 使用刷子,筆進行繪圖中會詳細講解。
畫筆CPen對象在畫點和畫線時有用。它的屬性包括顏色,寬度,線的風格,如虛線,實線,點劃線等。關(guān)于如何創(chuàng)建和使用畫筆在2.3 使用刷子,筆進行繪圖中會詳細講解。
位圖CBitmap對象可以包含一幅圖像,可以保存在資源中。關(guān)于如何使用位圖在2.4 在窗口中繪制設(shè)備相關(guān)位圖,圖標,設(shè)備無關(guān)位圖中會詳細講解。
還有一種特殊的GUI對象是多邊形,利用多邊形可以很好的限制作圖區(qū)域或是改變窗口外型。關(guān)于如何創(chuàng)建和使用多邊形在2.6 多邊形和剪貼區(qū)域中會詳細講解。
在Windows中使用GUI對象必須遵守一定的規(guī)則。首先需要創(chuàng)建一個合法的對象,不同的對象創(chuàng)建方法不同。然后需要將該GUI對象選入DC中,同時保存DC中原來的GUI對象。如果選入一個非法的對象將會引起異常。在使用完后應(yīng)該恢復原來的對象,這一點特別重要,如果保存一個臨時對象在DC中,而在臨時對象被銷毀后可能引起異常。有一點必須注意,每一個對象在重新創(chuàng)建前必須銷毀,下面的代碼演示了這一種安全的使用方法:
OnDraw(CDC* pDC)
{ CPen pen1,pen2; pen1.CreatePen(PS_SOLID,2,RGB(128,128,128));//創(chuàng)建對象 pen2.CreatePen(PS_SOLID,2,RGB(128,128,0));//創(chuàng)建對象 CPen* pPenOld=(CPen*)pDC->SelectObject(&pen1);//選擇對象進DC drawWithPen1... (CPen*)pDC->SelectObject(&pen2);//選擇對象進DC drawWithPen2... pen1.DeleteObject();//再次創(chuàng)建前先銷毀 pen1.CreatePen(PS_SOLID,2,RGB(0,0,0));//再次創(chuàng)建對象 (CPen*)pDC->SelectObject(&pen1);//選擇對象進DC drawWithPen1... pDC->SelectObject(pOldPen);//恢復 } 此外系統(tǒng)中還擁有一些庫存GUI對象,你可以利用CDC::SelectStockObject(SelectStockObject( int nIndex )選入這些對象,它們包括一些固定顏色的刷子,畫筆和一些基本字體。 BLACK_BRUSH Black brush.
DKGRAY_BRUSH Dark gray brush. GRAY_BRUSH Gray brush. HOLLOW_BRUSH Hollow brush. LTGRAY_BRUSH Light gray brush. NULL_BRUSH Null brush. WHITE_BRUSH White brush. BLACK_PEN Black pen. NULL_PEN Null pen. WHITE_PEN White pen. ANSI_FIXED_FONT ANSI fixed system font. ANSI_VAR_FONT ANSI variable system font. DEVICE_DEFAULT_FONT Device-dependent font. OEM_FIXED_FONT OEM-dependent fixed font. SYSTEM_FONT The system font. By default, Windows uses the system font to draw menus, dialog-box controls, and other text. In Windows versions 3.0 and later, the system font is proportional width; earlier versions of Windows use a fixed-width system font. SYSTEM_FIXED_FONT The fixed-width system font used in Windows prior to version 3.0. This object is available for compatibility with earlier versions of Windows. DEFAULT_PALETTE Default color palette. This palette consists of the 20 static colors in the system palette. 這些對象留在DC中是安全的,所以你可以利用選入庫存對象來作為恢復DC中GUI對象。 大家可能都注意到了繪圖時都需要一個DC對象,DC(Device Context設(shè)備環(huán)境)對象是一個抽象的作圖環(huán)境,可能是對應(yīng)屏幕,也可能是對應(yīng)打印機或其它。這個環(huán)境是設(shè)備無關(guān)的,所以你在對不同的設(shè)備輸出時只需要使用不同的設(shè)備環(huán)境就行了,而作圖方式可以完全不變。這也就是Windows耀眼的一點設(shè)備無關(guān)性。如同你將對一幅畫使用照相機或復印機將會產(chǎn)生不同的輸出,而不需要對畫進行任何調(diào)整。DC的使用會穿插在本章中進行介紹。
2.2 在窗口中輸出文字
在這里我假定讀者已經(jīng)利用ApplicationWizard生成了一個SDI界面的程序代碼。接下來的你只需要在CView派生類的OnDraw成員函數(shù)中加入繪圖代碼就可以了。在這里我需要解釋一下OnDraw函數(shù)的作用,OnDraw函數(shù)會在窗口需要重繪時自動被調(diào)用,傳入的參數(shù)CDC* pDC對應(yīng)的就是DC環(huán)境。使用OnDraw的優(yōu)點就在于在你使用打印功能的時候傳入OnDraw的DC環(huán)境將會是打印機繪圖環(huán)境,使用打印預覽時傳入的是一個稱為CPreviewDC的繪圖環(huán)境,所以你只需要一份代碼就可以完成窗口/打印預覽/打印機繪圖三重功能。利用Windows的設(shè)備無關(guān)性和M$為打印預覽所編寫的上千行代碼你可以很容易的完成一個具有所見即所得的軟件。 輸出文字一般使用CDC::BOOL TextOut( int x, int y, const CString& str )和CDC::int DrawText( const CString& str, LPRECT lpRect, UINT nFormat )兩個函數(shù),對TextOut來講只能輸出單行的文字,而DrawText可以指定在一個矩形中輸出單行或多行文字,并且可以規(guī)定對齊方式和使用何種風格。nFormat可以是多種以下標記的組合(利用位或操作)以達到選擇輸出風格的目的。
DT_BOTTOM底部對齊 Specifies bottom-justified text. This value must be combined with DT_SINGLELINE.
DT_CALCRECT計算指定文字時所需要矩形尺寸 Determines the width and height of the rectangle. If there are multiple lines of text, DrawText will use the width of the rectangle pointed to by lpRect and extend the base of the rectangle to bound the last line of text. If there is only one line of text, DrawText will modify the right side of the rectangle so that it bounds the last character in the line. In either case, DrawText returns the height of the formatted text, but does not draw the text. DT_CENTER中部對齊 Centers text horizontally. DT_END_ELLIPSIS or DT_PATH_ELLIPSIS Replaces part of the given string with ellipses, if necessary, so that the result fits in the specified rectangle. The given string is not modified unless the DT_MODIFYSTRING flag is specified. You can specify DT_END_ELLIPSIS to replace characters at the end of the string, or DT_PATH_ELLIPSIS to replace characters in the middle of the string. If the string contains backslash (\) characters, DT_PATH_ELLIPSIS preserves as much as possible of the text after the last backslash. DT_EXPANDTABS Expands tab characters. The default number of characters per tab is eight.
DT_EXTERNALLEADING Includes the font抯 external leading in the line height. Normally, external leading is not included in the height of a line of text. DT_LEFT左對齊 Aligns text flush-left. DT_MODIFYSTRING Modifies the given string to match the displayed text. This flag has no effect unless the DT_END_ELLIPSIS or DT_PATH_ELLIPSIS flag is specified. Note Some uFormat flag combinations can cause the passed string to be modified. Using DT_MODIFYSTRING with either DT_END_ELLIPSIS or DT_PATH_ELLIPSIS may cause the string to be modified, causing an assertion in the CString override. DT_NOCLIP Draws without clipping. DrawText is somewhat faster when DT_NOCLIP is used. DT_NOPREFIX禁止使用&前綴 Turns off processing of prefix characters. Normally, DrawText interprets the ampersand (&) mnemonic-prefix character as a directive to underscore the character that follows, and the two-ampersand (&&) mnemonic-prefix characters as a directive to print a single ampersand. By specifying DT_NOPREFIX, this processing is turned off. DT_PATH_ELLIPSIS DT_RIGHT右對齊 Aligns text flush-right. DT_SINGLELINE單行輸出 Specifies single line only. Carriage returns and linefeeds do not break the line. DT_TABSTOP設(shè)置TAB字符所占寬度 Sets tab stops. The high-order byte of nFormat is the number of characters for each tab. The default number of characters per tab is eight. DT_TOP定部對齊 Specifies top-justified text (single line only). DT_VCENTER中部對齊 Specifies vertically centered text (single line only). DT_WORDBREAK每行只在單詞間被折行 Specifies word-breaking. Lines are automatically broken between words if a word would extend past the edge of the rectangle specified by lpRect. A carriage return杔inefeed sequence will also break the line. 在輸出文字時如果希望改變文字的顏色,你可以利用CDC::SetTextColor( COLORREF crColor )進行設(shè)置,如果你希望改變背景色就利用CDC::SetBkColor( COLORREF crColor ),很多時候你可能需要透明的背景色你可以利用CDC::SetBkMode( int nBkMode )設(shè)置,可接受的參數(shù)有
OPAQUE Background is filled with the current background color before the text, hatched brush, or pen is drawn. This is the default background mode.
TRANSPARENT Background is not changed before drawing. 接下來講講如何創(chuàng)建字體,你可以創(chuàng)建的字體有兩種:庫存字體CDC::CreateStockObject( int nIndex )和自定義字體。
在創(chuàng)建非庫存字體時需要填充一個LOGFONT結(jié)構(gòu)并使用CFont::CreateFontIndirect(const LOGFONT* lpLogFont ),或使用CFont::CreateFont( int nHeight, int nWidth, int nEscapement, int nOrientation, int nWeight, BYTE bItalic, BYTE bUnderline, BYTE cStrikeOut, BYTE nCharSet, BYTE nOutPrecision, BYTE nClipPrecision, BYTE nQuality, BYTE nPitchAndFamily, LPCTSTR lpszFacename )其中的參數(shù)和LOGFONT中的分量有一定的對應(yīng)關(guān)系。下面分別講解參數(shù)的意義: nHeight 字體高度(邏輯單位)等于零為缺省高度,否則取絕對值并和可用的字體高度進行匹配。
nWidth 寬度(邏輯單位)如果為零則使用可用的橫縱比進行匹配。 nEscapement 出口矢量與X軸間的角度 nOrientation 字體基線與X軸間的角度 nWeight 字體粗細,可取以下值 Constant Value FW_DONTCARE 0 FW_THIN 100 FW_EXTRALIGHT 200 FW_ULTRALIGHT 200 FW_LIGHT 300 FW_NORMAL 400 FW_REGULAR 400 FW_MEDIUM 500 FW_SEMIBOLD 600 FW_DEMIBOLD 600 FW_BOLD 700 FW_EXTRABOLD 800 FW_ULTRABOLD 800 FW_BLACK 900 FW_HEAVY 900 bItalic 是否為斜體
bUnderline 是否有下劃線 cStrikeOut 是否帶刪除線 nCharSet 指定字符集合,可取以下值 Constant Value ANSI_CHARSET 0 DEFAULT_CHARSET 1 SYMBOL_CHARSET 2 SHIFTJIS_CHARSET 128 OEM_CHARSET 255 nOutPrecision 輸出精度
OUT_CHARACTER_PRECIS OUT_STRING_PRECIS OUT_DEFAULT_PRECIS OUT_STROKE_PRECIS OUT_DEVICE_PRECIS OUT_TT_PRECIS OUT_RASTER_PRECIS nClipPrecision 剪輯精度,可取以下值
CLIP_CHARACTER_PRECIS CLIP_MASK CLIP_DEFAULT_PRECIS CLIP_STROKE_PRECIS CLIP_ENCAPSULATE CLIP_TT_ALWAYS CLIP_LH_ANGLES nQuality 輸出質(zhì)量,可取以下值
DEFAULT_QUALITY Appearance of the font does not matter. DRAFT_QUALITY Appearance of the font is less important than when PROOF_QUALITY is used. For GDI raster fonts, scaling is enabled. Bold, italic, underline, and strikeout fonts are synthesized if necessary. PROOF_QUALITY Character quality of the font is more important than exact matching of the logical-font attributes. For GDI raster fonts, scaling is disabled and the font closest in size is chosen. Bold, italic, underline, and strikeout fonts are synthesized if necessary. nPitchAndFamily 字體間的間距 lpszFacename 指定字體名稱,為了得到系統(tǒng)所擁有的字體可以利用EmunFontFamiliesEx。 此外可以利用CFontDialog來得到用戶選擇的字體的LOGFONT數(shù)據(jù)。 最后我講一下文本坐標的計算,利用CDC::GetTextExtent( const CString& str )可以得到字符串的在輸出時所占用的寬度和高度,這樣就可以在手工輸出多行文字時使用正確的行距。另外如果需要更精確的對字體高度和寬度進行計算就需要使用CDC::GetTextMetrics( LPTEXTMETRIC lpMetrics ) 該函數(shù)將會填充TEXTMETRIC結(jié)構(gòu),該結(jié)構(gòu)中的分量可以非常精確的描述字體的各種屬性。
2.3 使用點,刷子,筆進行繪圖
在Windows中畫點的方法很簡單,只需要調(diào)用COLORREF CDC::SetPixel( int x, int y, COLORREF crColor )就可以在指定點畫上指定顏色,同時返回原來的顏色。COLORREF CDC::GetPixel( int x, int y)可以得到指定點的顏色。在Windows中應(yīng)該少使用畫點的函數(shù),因為這樣做的執(zhí)行效率比較低。 刷子和畫筆在Windows作圖中是使用最多的GUI對象,本節(jié)在講解刷子和畫筆使用方法的同時也講述一寫基本作圖函數(shù)。
在畫點或畫線時系統(tǒng)使用當前DC中的畫筆,所以在創(chuàng)建畫筆后必須將其選入DC才會在繪圖時產(chǎn)生效果。畫筆可以通過CPen對象來產(chǎn)生,通過調(diào)用CPen::CreatePen( int nPenStyle, int nWidth, COLORREF crColor )來創(chuàng)建。其中nPenStyle指名畫筆的風格,可取如下值:
PS_SOLID 實線 Creates a solid pen.
PS_DASH 虛線,寬度必須為一 Creates a dashed pen. Valid only when the pen width is 1 or less, in device units. PS_DOT 點線,寬度必須為一 Creates a dotted pen. Valid only when the pen width is 1 or less, in device units. PS_DASHDOT 點劃線,寬度必須為一 Creates a pen with alternating dashes and dots. Valid only when the pen width is 1 or less, in device units. PS_DASHDOTDOT 雙點劃線,寬度必須為一 Creates a pen with alternating dashes and double dots. Valid only when the pen width is 1 or less, in device units. PS_NULL 空線,使用時什么也不會產(chǎn)生 Creates a null pen. PS_ENDCAP_ROUND 結(jié)束處為圓形 End caps are round. PS_ENDCAP_SQUARE 結(jié)束處為方形 End caps are square. nWidth和crColor為線的寬度和顏色。 刷子是在畫封閉曲線時用來填充的顏色,例如當你畫圓形或方形時系統(tǒng)會用當前的刷子對內(nèi)部進行填充。刷子可利用CBrush對象產(chǎn)生。通過以下幾種函數(shù)創(chuàng)建刷子:
BOOL CreateSolidBrush( COLORREF crColor ); 創(chuàng)建一種固定顏色的刷子
BOOL CreateHatchBrush( int nIndex, COLORREF crColor ); 創(chuàng)建指定顏色和網(wǎng)格的刷子,nIndex可取以下值: HS_BDIAGONAL Downward hatch (left to right) at 45 degrees HS_CROSS Horizontal and vertical crosshatch HS_DIAGCROSS Crosshatch at 45 degrees HS_FDIAGONAL Upward hatch (left to right) at 45 degrees HS_HORIZONTAL Horizontal hatch HS_VERTICAL Vertical hatch BOOL CreatePatternBrush( CBitmap* pBitmap ); 創(chuàng)建以8*8位圖為模板的刷子 在選擇了畫筆和刷子后就可以利用Windows的作圖函數(shù)進行作圖了,基本的畫線函數(shù)有以下幾種
CDC::MoveTo( int x, int y ); 改變當前點的位置
CDC::LineTo( int x, int y ); 畫一條由當前點到參數(shù)指定點的線 CDC::BOOL Arc( LPCRECT lpRect, POINT ptStart, POINT ptEnd ); 畫弧線 CDC::BOOL Polyline( LPPOINT lpPoints, int nCount ); 將多條線依次序連接 基本的作圖函數(shù)有以下幾種: CDC::BOOL Rectangle( LPCRECT lpRect ); 矩形 CDC::RoundRect( LPCRECT lpRect, POINT point ); 圓角矩形 CDC::Draw3dRect( int x, int y, int cx, int cy, COLORREF clrTopLeft, COLORREF clrBottomRight ); 3D邊框 CDC::Chord( LPCRECT lpRect, POINT ptStart, POINT ptEnd ); 扇形 CDC::Ellipse( LPCRECT lpRect ); 橢圓形 CDC::Pie( LPCRECT lpRect, POINT ptStart, POINT ptEnd ); CDC::Polygon( LPPOINT lpPoints, int nCount ); 多邊形 對于矩形,圓形或類似的封閉曲線,系統(tǒng)會使用畫筆繪制邊緣,使用刷子填充內(nèi)部。如果你不希望填充或是畫出邊緣,你可以選入空刷子(NULL_PEN)或是(NULL_BRUSH)空筆。 下面的代碼創(chuàng)建一條兩象素寬的實線并選入DC。并進行簡單的作圖:
{ ... CPen pen; pen.CreatePen(PS_SOLID,2,RGB(128,128,128)); CPen* pOldPen=(CPen*)dc.SelectObject(&pen); dc.SelectStockObject(NULL_BRUSH);//選入空刷子 dc.Rectangle(CRect(0,0,20,20));//畫矩形 ... }2.4 在窗口中繪制設(shè)備相關(guān)位圖,圖標,設(shè)備無關(guān)位圖 在Windows中可以將預先準備好的圖像復制到顯示區(qū)域中,這種內(nèi)存拷貝執(zhí)行起來是非??斓?。在Windows中提供了兩種使用圖形拷貝的方法:通過設(shè)備相關(guān)位圖(DDB)和設(shè)備無關(guān)位圖(DIB)。 DDB可以用MFC中的CBitmap來表示,而DDB一般是存儲在資源文件中,在加載時只需要通過資源ID號就可以將圖形裝入。BOOL CBitmap::LoadBitmap( UINT nIDResource )可以裝入指定DDB,但是在繪制時必須借助另一個和當前繪圖DC兼容的內(nèi)存DC來進行。通過CDC::BitBlt( int x, int y, int nWidth, int nHeight, CDC* pSrcDC, int xSrc, int ySrc, DWORD dwRop )繪制圖形,同時指定光柵操作的類型。BitBlt可以將源DC中位圖復制到目的DC中,其中前四個參數(shù)為目的區(qū)域的坐標,接下來是源DC指針,然后是源DC中的起始坐標,由于BitBlt為等比例復制,所以不需要再次指定長寬,(StretchBlt可以進行縮放)最后一個參數(shù)為光柵操作的類型,可取以下值:
BLACKNESS 輸出區(qū)域為黑色 Turns all output black.
DSTINVERT 反色輸出區(qū)域 Inverts the destination bitmap. MERGECOPY 在源和目的間使用AND操作 Combines the pattern and the source bitmap using the Boolean AND operator. MERGEPAINT 在反色后的目的和源間使用OR操作 Combines the inverted source bitmap with the destination bitmap using the Boolean OR operator. NOTSRCCOPY 將反色后的源拷貝到目的區(qū) Copies the inverted source bitmap to the destination. PATINVERT 源和目的間進行XOR操作 Combines the destination bitmap with the pattern using the Boolean XOR operator. SRCAND 源和目的間進行AND操作 Combines pixels of the destination and source bitmaps using the Boolean AND operator. SRCCOPY 復制源到目的區(qū) Copies the source bitmap to the destination bitmap. SRCINVERT 源和目的間進行XOR操作 Combines pixels of the destination and source bitmaps using the Boolean XOR operator. SRCPAINT 源和目的間進行OR操作 Combines pixels of the destination and source bitmaps using the Boolean OR operator. WHITENESS 輸出區(qū)域為白色 Turns all output white. 下面用代碼演示這種方法: CYourView::OnDraw(CDC* pDC) { CDC memDC;//定義一個兼容DC memDC.CreateCompatibleDC(pDC);//創(chuàng)建DC CBitmap bmpDraw; bmpDraw.LoadBitmap(ID_BMP) ;//裝入DDB CBitmap* pbmpOld=memDC.SelectObject(&bmpDraw) ; //保存原有DDB,并選入新DDB入DC pDC->BitBlt(0,0,20,20,&memDC,0,0,SRCCOPY) ; //將源DC中(0,0,20,20)復制到目的DC(0,0,20,20) pDC->BitBlt(20,20,40,40,&memDC,0,0,SRCAND); //將源DC中(0,0,20,20)和目的DC(20,20,40,40)中區(qū)域進行AND操作 memDC.SelectObject(pbmpOld) ;//選入原DDB } (圖標并不是一個GDI對象,所以不需要選入DC)在MFC中沒有一個專門的圖標類,因為圖標的操作比較簡單,使用HICON CWinApp::LoadIcon( UINT nIDResource )或是HICON CWinApp::LoadStandardIcon( LPCTSTR lpszIconName ) 裝入后就可以利用BOOL CDC::DrawIcon( int x, int y, HICON hIcon )繪制。由于在圖標中可以指定透明區(qū)域,所以在某些需要使用非規(guī)則圖形而且面積不大的時候使用圖標會比較簡單。下面給出簡單的代碼: OnDraw(CDC* pDC)
{ HICON hIcon1=AfxGetApp()->LoadIcon(IDI_I1); HICON hIcon2=AfxGetApp()->LoadIcon(IDI_I2); pDC->DrawIcon(0,0,hIcon1); pDC->DrawIcon(0,40,hIcon2); DestroyIcon(hIcon1); DestroyIcon(hIcon2); } 同樣在MFC也沒有提供一個DIB的類,所以在使用DIB位圖時我們需要自己讀取位圖文件中的頭信息,并讀入數(shù)據(jù),并利用API函數(shù)StretchDIBits繪制。位圖文件以BITMAPFILEHEADER結(jié)構(gòu)開始,然后是BITMAPINFOHEADER結(jié)構(gòu)和調(diào)色版信息和數(shù)據(jù),其實位圖格式是圖形格式中最簡單的一種,而且也是Windows可以理解的一種。我不詳細講解DIB位圖的結(jié)構(gòu),提供一個CDib類供大家使用,這個類包含了基本的功能如:Load,Save,Draw。DownLoad CDib 4K 2.5 使用各種映射方式
所謂的映射方式簡單點講就是坐標的安排方式,系統(tǒng)默認的映射方式為MM_TEXT即X坐標向右增加,Y坐標向下增加,(0,0)在屏幕左上方,DC中的每一點就是屏幕上的一個象素。也許你會認為這種方式下是最好理解的,但是一個點和象素對應(yīng)的關(guān)系在屏幕上看來是正常的,但到了打印機上就會很不正常。因為我們作圖是以點為單位并且打印機的分辨率遠遠比顯示器高(800DPI 800點每英寸)所以在打印機上圖形看起來就會很小。這樣就需要為打印另做一套代碼而加大了工作量。如果每個點對應(yīng)0.1毫米那么在屏幕上的圖形就會和打印出來的圖形一樣大小。 通過int CDC::SetMapMode( int nMapMode )可以指定映射方式,可用的有以下幾種:
MM_HIENGLISH 每點對應(yīng)0.001英寸 Each logical unit is converted to 0.001 inch. Positive x is to the right; positive y is up.
MM_HIMETRIC 每點對應(yīng)0.001毫米 Each logical unit is converted to 0.01 millimeter. Positive x is to the right; positive y is up. MM_LOENGLISH 每點對應(yīng)0.01英寸 Each logical unit is converted to 0.01 inch. Positive x is to the right; positive y is up. MM_LOMETRIC 每點對應(yīng)0.001毫米 Each logical unit is converted to 0.1 millimeter. Positive x is to the right; positive y is up. MM_TEXT 象素對應(yīng) Each logical unit is converted to 1 device pixel. Positive x is to the right; positive y is down. 以上幾種映射默認的原點在屏幕左上方。除MM_TEXT外都為X坐標向右增加,Y坐標向上增加,和自然坐標是一致的。所以在作圖是要注意什么時候應(yīng)該使用負坐標。而且以上的映射都是X-Y等比例的,即相同的長度在X,Y軸上顯示的長度都是相同的。 DownLoad Sample
另外的一種映射方式為MM_ANISOTROPIC,這種方式可以規(guī)定不同的長寬比例。在設(shè)置這中映射方式后必須調(diào)用CSize CDC::SetWindowExt( SIZE size )和CSize CDC::SetViewportExt( SIZE size )來設(shè)定長寬比例。系統(tǒng)會根據(jù)兩次設(shè)定的長寬的比值來確定長寬比例。下面給出一段代碼比較映射前后的長寬比例: OnDraw(CDC* pDC)
{ CRect rcC1(200,0,400,200); pDC->FillSolidRect(rcC1,RGB(0,0,255)); pDC->SetMapMode(MM_ANISOTROPIC ); CSize sizeO; sizeO=pDC->SetWindowExt(5,5); TRACE("winExt %d %d\n",sizeO.cx,sizeO.cy); sizeO=pDC->SetViewportExt(5,10); TRACE("ViewExt %d %d\n",sizeO.cx,sizeO.cy); CRect rcC(0,0,200,200); pDC->FillSolidRect(rcC,RGB(0,128,0)); } 上面代碼在映射后畫出的圖形將是一個長方形。
DownLoad Sample 最后講講視原點(viewport origin),你可以通過調(diào)用CPoint CDC::SetViewportOrg( POINT point )重新設(shè)置原點的位置,這就相對于對坐標進行了位移。例如你將原點設(shè)置在(20,20)那么原來的(0,0)就變成了(-20,-20)。 多邊形也是一個GDI對象,同樣遵守其他GDI對象的規(guī)則,只是通常都不將其選入DC中。在MFC中多邊形有CRgn表示。多邊形用來表示一個不同與矩形的區(qū)域,和矩形具有相似的操作。如:檢測某點是否在內(nèi)部,并操作等。此外還得到一個包含此多邊形的最小矩形。下面介紹一下多邊形類的成員函數(shù): CreateRectRgn 由矩形創(chuàng)建一個多邊形
CreateEllipticRgn 由橢圓創(chuàng)建一個多邊形 CreatePolygonRgn 創(chuàng)建一個有多個點圍成的多邊形 PtInRegion 某點是否在內(nèi)部 CombineRgn 兩個多邊形相并 EqualRgn 兩個多邊形是否相等 在本節(jié)中講演多邊形的意義在于重新在窗口中作圖時提高效率。因為引發(fā)窗口重繪的原因是某個區(qū)域失效,而失效的區(qū)域用多邊形來表示。假設(shè)窗口大小為500*400當上方的另一個窗口從(0,0,10,10)移動到(20,20,30,30)這時(0,0,10,10)區(qū)域就失效了,而你只需要重繪這部分區(qū)域而不是所有區(qū)域,這樣你程序的執(zhí)行效率就會提高。
通過調(diào)用API函數(shù)int GetClipRgn( HDC hdc, HRGN hrgn)就可以得到失效區(qū)域,但是一般用不著那么精確而只需得到包含該區(qū)域的最小矩形就可以了,所以可以利用int CDC::GetClipBox( LPRECT lpRect )完成這一功能。
|
|