Microsoft Visual C++是一種可視化編程語言,因功能強大而受到廣大程序設計人 員的青睐。但是,由於VC++的應用程序框架結構非常復雜,使得許多初學者望而卻步。 本文通過對設置視圖背景顏色和改變對話框標題的幾種實現方法的分析研究,揭示了VC+ +程序代碼執行時的一些本質特征和有關的程序設計技巧,對理解MFC庫的結構和Windows 操作系統的內部工作方式提供了一定的幫助。
設置視圖背景顏色
對於VC+ +文檔、視結構中的視圖,從用戶的角度來看,只是可以改變大小、位置的普通窗口,同 其他基於Windows應用程序的窗口是一樣的;從程序員的角度來看,視圖並不是普通的窗 口,而是從MFC庫中CView類派生的類對象。像任何VC++對象一樣,視圖對象的行為由類 的成員函數(數據成員)決定,包括派生類中應用程序定義的函數和從基類繼承來的函數 。
提出問題
視圖的背景一般來說是白色的,在缺省情況下,它和系統定義 的顏色COLOR_WINDOW是一致的。設計者一般會希望自己的程序可以讓用戶輕松地改變窗口 背景顏色,或是用漂亮的圖片來充填背景。我們可以用Windows函數SetSysColors來重新 指定COLOR_WINDOW所對應的實際顏色,來達到改變視圖背景顏色的目的。但這樣會同時改 變其他應用程序的視圖窗口背景,使得整個Windows系統的顏色設置產生混亂。另外,我 們可能會用以下方法來設置視圖的背景顏色,即在CView的OnDraw函數中添寫如下一段程 序代碼:
void CTestView::OnDraw(CDC* pDC)
{
CTestDoc* pDoc = GetDocument();
ASSERT_VALID(pDoc);
CRect rectClient;
CBrush brushBkColor;
GetClientRect(rectClient);
brushBkColor.CreateSolidBrush(RGB(255,0,0));
pDC->DPtoLP (rectClient);
pDC->FillRect(rectClient,&brushBkColor);
…
}
這樣可以達到改變當前應用程序的視圖背景的目的, 但同時也產生了一些不良影響,使得程序運行效果不盡如人意。
分析問題
我們知道,在VC++的文檔、視結構中,CView的OnDraw函數用於實現絕大部分圖形繪制 的工作。如果用戶改變窗口尺寸,或者顯示隱藏的區域,OnDraw函數都將被調用來重畫窗 口。並且,當程序文檔中的數據發生改變時,一般必須通過調用視圖的Invalidate(或 InvalidateRect)成員函數來通知Windows所發生的改變,對Invalidate的調用也會觸發 對OnDraw函數的調用。正因為OnDraw函數被頻繁調用,所以在其執行時,每次都刷新填充 一次視圖客戶區域,便會使屏幕不穩定,產生閃爍現象。
筆者通過對VC++應用 程序框架結構和Windows消息映射系統的仔細研究,找到另外一種改變視圖背景的方法, 其執行效果比上述兩種方法都好。其實在程序調用OnDraw函數之前,會觸發一個Windows 消息:WM_ERASEBKGND,以擦除視圖刷新區域。在缺省情況下,Windows系統使用視圖窗口 注冊時窗口類中的成員hbrBackground所描述的畫刷來擦除屏幕,這一般會將屏幕刷新成 COLOR_WINDOW所對應的顏色。因此,在OnDraw函數中設置背景顏色的執行過程是這樣的: 先將屏幕刷新成COLOR_WINDOW所對應的顏色,接著又在OnDraw函數中填充其他顏色,這正 是產生屏幕閃爍的根本原因。
解決問題
通過上述分析,我們應將視圖背景 顏色填充移到Windows消息:WM_ERASEBKGND所對應的消息映射函數中,而不是在OnDraw函 數中。我們可以通過下列步驟實現這一過程:在文檔類中增加一成員變量m_viewBkColor 保存當前背景顏色,同時增加兩個成員函數GetViewBkColor和SetViewBkColor對其進行讀 寫操作。這樣做的好處是可以對m_viewBkColor成員進行序列化,將其和文檔聯系在一起 ,打開某一文檔時,其背景將和上一次程序操作該文檔時的背景保持一致。在視圖類中為 視圖的Windows消息WM_ERASEBKGND增加消息映射函數OnEraseBkgnd,代碼如下:
BOOL CTestView::OnEraseBkgnd(CDC* pDC)
{
CRect rect;
CBrush brush;
brush.CreateSolidBrush(GetDocument()->GetViewBkColor ());
pDC->GetClipBox(rect);
pDC->FillRect(rect,&brush);
return true;
}
在該函數中不需要對客戶區域矩形進行設備坐標 到邏輯坐標的轉換,並且Windows在調用該函數時會自動進行裁剪區域的計算,使得需要 刷新的屏幕面積達到最小。這樣我們可以在程序中通過設計下列菜單函數輕松地改變視圖 背景的顏色,而且運行效果相當令人滿意。
void CTestView::OnChangeViewBkcolor()
{
CColorDialog cdlg;
if (cdlg.DoModal()==IDOK)
{
GetDocument()->SetViewBkColor
(cdlg.GetColor());
InvalidateRect(NULL);
}
}
改變 對話框標題
提出問題
在VC++程序設計過程中經常會遇到這樣的情況:執 行程序的多個地方需要調用同一個對話框,但在不同的情況下希望給對話框加上不同的標 題。開始我們可能會用下面的一段程序以達到這一目的:
CTestDialog dlg;
dlg.SetWindowText(“標題-1");
dlg.DoModal ();
利用上述辦法,我們本希望在程序不同的地方,通過設置函數 SetWindowText不同的參數,以達到使同一對話框具有不同標題的目的,但這樣做是行不 通的。
分析問題
利用這種方法,當執行該段程序時,在一個可以忽略的錯 誤之後,對話框的標題不會發生任何改變。這是因為,VC++程序設計中,大部分窗體是 動態創建的。比如上述對話框,在對dlg.DoModal的調用之前,雖然已構造了對話框的VC ++對象,但窗體對象還沒有被創建,顯然對一個沒有創建窗體對象的對話框設置標題是 行不通的。另外,dlg.DoModal的調用結束時,對話框窗體對象將立即被釋放,因此在該 函數之後設置對話框標題也是不行的。
解決問題
通過對VC++框架結構中 函數的調用順序的分析,我們發現在dlg.DoModal執行的開始時,程序會自動調用對話框 的一系列初始化函數,其中包括對對話框成員函數OnInitDialog的調用,從這裡入手,將 找到改變對話框標題的辦法。為此,首先為對話框引進一個類型為CString的公有成員變 量m_strCaption,並將上述程序段改為:
CTestDialog dlg;
dlg.m_strCaption = “標題-1";
dlg.DoModal();
然 後重載對話框的虛成員函數OnInitDialog如下:
BOOL CTestDialog::OnInitDialog()
{
CDialog::OnInitDialog();
SetWindowText(m_strCaption);
…
return TRUE;
}
通過這種辦法,每次在打開對話框之前,只要將對話框公有成員變量 m_strCaption設置為一個不同的值,就可使得對話框有不同的標題。
小 結
本文介紹的2個技巧有一個相似之處,就是用於解決問題的程序實現代碼基本上是 相同的,只是被放在了程序流程的不同地方。這正是學習和熟練掌握VC++的一個非常重 要的方面,是影響其程序執行效率和性能的關鍵。