用VC完成對屬性表按鈕區的位圖繪制
發表時間:2024-02-19 來源:明輝站整理相關軟件相關文章人氣:
[摘要]作者: 徐毅 許潤濤 王宇 摘 要:本文通過詳細討論如何用VC實現對屬性表按鈕區的操作以改變屬性頁的外觀,從而提供一種對Windows應用程序的非窗口客戶區進行繪制的方法,并給出了一個簡單的示例程序。 關鍵字:屬性表類、非窗口區、位圖 屬性表類(CPropertySheet Class)在編寫Wi...
作者: 徐毅 許潤濤 王宇
摘 要:本文通過詳細討論如何用VC實現對屬性表按鈕區的操作以改變屬性頁的外觀,從而提供一種對Windows應用程序的非窗口客戶區進行繪制的方法,并給出了一個簡單的示例程序。
關鍵字:屬性表類、非窗口區、位圖
屬性表類(CPropertySheet Class)在編寫Windows應用程序時使用非常廣泛,如編寫安裝向導程序、應用程序配置等很多應用程序都必須使用屬性表類,但是如何實現用VC對屬性表類的按鈕區進行繪制卻是一個比較困難的問題。因為VC的MFC類庫封裝了屬性表類,使得其外觀表現一般不容易改變。而在編寫應用程序的過程中卻常常遇到要在屬性表的按鈕區域進行繪制的問題,如在屬性表按鈕區加入公司的標識等等。屬性表按鈕區是非窗口客戶區,因此要對其直接進行繪制需要采用一些特殊的處理。我們在實際編程開發過程中,對此問題進行了一些探索。下面我們通過示例說明在VC5.0環境下實現對屬性頁按鈕區域位圖繪制的方法。
1、 實現非窗口區域繪制的基本思想:
要完成對屬性表按鈕區域(即非窗口客戶區)的操作,必須得到相關的繪圖設備環境(CPaint DC),找出按鈕區域的具體位置,才能夠對其進行操作。為此,需要對MFC的CPropertySheet類進行繼承,對其繼承類的OnPaint消息處理函數進行重載,在OnPaint消息處理函數中,直接以當前指針為變量定義一個設備環境對象,這就是我們所需的繪圖設備環境,再找出屬性表類的制表控件(table control)客戶區位置和屬性表類的缺省按鈕位置,就能夠計算出按鈕區域的具體位置。只要完成上述兩步,對屬性表按鈕區的操作也就不難實現了。
2.示例程序具體實現
首先,用VC的Wizard代碼生成器生成一個MFC應用程序框架,在自動生成的過程中,選擇應用程序是基于對話框的程序。當生成完畢后,在將自動生成的對話框類全部刪除。再手動添加一個從CPropertySheet類繼承的子類CPropertySheetWithLogoDlg類和一個基于CDialog類的CFirstPropertyPage類,同時在程序App類的InitInstance方法中刪除關于自動生成的對話框類的代碼。并加入如下代碼:
CPropertySheetWithLogoDlg dlg("屬性表按鈕區繪制");
CFirstPropertyPage FirstPage; //進行類的實例化
dlg.SetLogoText("Example Vision"); //對要在按鈕區域繪制的字符串進行賦值
dlg.AddPage(&FirstPage); //向屬性表中添加屬性頁
int nResponse = dlg.DoModal();
if (nResponse == IDOK){}
else if (nResponse == IDCANCEL){}
return FALSE;
這段代碼使由Wizard代碼生成器生成的應用程序的主框架(mainframe)成為一個屬性表。其中SetLogoText是CPropertySheetWithLogoDlg類的用戶自定義方法,它是給寫在屬性表按鈕區的字符串賦值。
下面就是如何對按鈕區域進行操作。屬性表按鈕區是非窗口客戶區,因此我們不能通過重載CPropertySheetWithLogoDlg類的OnDraw方法來直接對屬性表按鈕區進行操作。而必須重載CPropertySheetWithLogoDlg類的OnPaint方法。其具體實現代碼如下:
void CPropertySheetWithLogoDlg::OnPaint()
{
CPaintDC dc(this); //獲得繪制的設備環境。
if(m_LogoText.IsEmpty())//判斷字符串是否為空。
return;
CRect rectTabCtrl;
GetTabControl()->GetWindowRect(rectTabCtrl);//獲得屬性表的制表控件的客戶區屏幕坐標。
ScreenToClient(rectTabCtrl);//屏幕坐標轉換為窗口邏輯坐標。
CRect rectOk;
GetDlgItem(IDOK)->GetWindowRect(rectOk);//獲得客戶區最左按鈕屏幕坐標。
ScreenToClient(rectOk); //屏幕坐標轉換為窗口邏輯坐標。
dc.SetBkMode(TRANSPARENT);//背景模式設為透明。
CRect rectText;
rectText.left = rectTabCtrl.left;
rectText.top = rectOk.top;
rectText.bottom = rectOk.bottom;
rectText.right = rectOk.left;//獲得所需繪制按鈕區窗口邏輯坐標。
CFont * OldFont = dc.SelectObject(&m_fontLogo);//選擇所需字體。
COLORREF OldColor = dc.SetTextColor( ::GetSysColor( COLOR_3DHILIGHT));//設置文本顏色。
dc.DrawText( m_LogoText, rectText + CPoint(1,1), DT_SINGLELINE DT_LEFT DT_VCENTER);//顯示字符串。
dc.SetTextColor( ::GetSysColor( COLOR_3DSHADOW));
dc.DrawText( m_LogoText, rectText, DT_SINGLELINE DT_LEFT DT_VCENTER);//顯示字符串3D陰影。
dc.SetTextColor( OldColor);//恢復原文本顏色。
dc.SelectObject(OldFont);//恢復原字體。
}
在這段代碼中,首先通過定義一個以thhis指針為變量的CPaintDC變量dc得到當前繪圖設備環境。然后是要找出按鈕區的具體位置。按鈕區操作的位置實際就是屬性表的制表控件客戶區的最左端直到第一個按鈕最左端為此的區域。也就是說,先需要得到屬性表制表控件的指針,這可用CPropertySheet類的方法GetTabCtrl()得到。再通過WIN32API函數GetWindowRect()得到控件客戶區的屏幕坐標。然后得到用GetDlgItem(IDOK)->GetWindowRect()得到客戶區最左OK按鈕的屏幕坐標。將它們都轉換為窗口邏輯坐標,以控件客戶區的左坐標作為操作按鈕區的左坐標,以最左OK按鈕的上、下坐標作為操作按鈕區的上、下坐標,以最左OK按鈕的左坐標作為操作按鈕區的右坐標。就得到了所需按鈕區的具體位置。最后只需再選擇字體和文本顏色,用DrawText() 進行顯示即可在屬性表按鈕區繪出字符串。
如果我們要在按鈕區顯示一幅位圖,只須對以上代碼作出很少修改,具體代碼如下:
CBitmap bmp, *poldbmp;
CDC memdc;
CRect rect;
bmp.LoadBitmap(IDB_BITMAPLOGO); //載入位圖資源。
memdc.CreateCompatibleDC(&dc);//生成一個與當前設備環境兼容的內存設備環境。
poldbmp = memdc.SelectObject(&bmp);//將位圖寫入內存設備環境。
GetClientRect(&rect);//獲得屬性表客戶區的大小
//從內存設備環境向屏幕挎貝位圖。
dc.BitBlt(left, rect.bottom - lower, w, h, &memdc, 0, 0, SRCCOPY);
//w,h為位圖的寬度和高度。Left為位圖距屬性頁左邊框的距離,lower 為位圖距下邊框的距離。
memdc.SelectObject(poldbmp);
通過引入位圖資源,再將其選入內存設備環境,最后用BitBlt函數顯示到實際設備環境,我們就在按鈕區繪出了所選位圖。這一步驟與其它在正常窗口顯示位圖的方法是基本一致的。
3.實現所需注意問題
需要注意的是,屬性表有兩種模式,一種是正常屬性表模式,另一種是向導模式(Wizard)。我們可以通過加入如下代碼檢測屬性表的模式:
BOOL bWizMode;
//從PROPSHEETHEADER 結構中得到當前屬性表的模式。
if( m_psh.dwFlags & PSH_WIZARD )
bWizMode = TRUE; //是向導模式
else
bWizMode = FALSE; // 是正常屬性表模式
其中,m_psh是CPropertySheet類的公有成員變量,它是PROPSHEETHEADER結構,可以通過訪問該成員變量獲取屬性表的基本屬性。
在正常屬性表模式下,上述獲取屬性表按鈕區位置的方法可以正確通過。而在向導模式下,屬性表沒有制表控件,無法得到制表控件客戶區位置。在這時,需要用屬性表對話框窖口代替控件客戶區。可在原代碼中加入如下代碼:
if( bWizMode ) {
GetWindowRect(rectTabCtrl); //得到對話框窗口屏幕坐標。
rectTabCtrl.OffsetRect(14,0); // 對窗口位置校正
}
else{
GetTabControl()->GetWindowRect(rectTabCtrl);
}
通過以上代碼,我們就能夠正確得到屬性表按鈕區域位置。
4.結論
由上述討論可知,對于屬性表按鈕區進行操作,關鍵是要了解對非窗口客戶區進行繪制是不同于在一般窗口繪制的過程時,在一般窗口繪制位圖或寫入字符時,只需重載窗口類的OnDraw方法,而對非窗口客戶區進行繪制時,必須重載OnPaint函數。這一法則適用于對任何非窗口客戶區的操作。
參考文獻
1.Microsoft Widnows 95 開發者必讀 翟炯 石秋云譯 電子工業出版社 1997年2月。
2.Visual C++開發指南 屠強等譯 電子工業出版社 1998年1月