对话框中加入标签页的5种方法
標(biāo)簽頁(yè)是MFC控件里有一點(diǎn)難用的東西,今天看到了一篇相關(guān)文章,覺(jué)得寫得比較系統(tǒng),而且淺顯易懂,特地轉(zhuǎn)來(lái),與大家分享的同時(shí)也留備自己以后查用。
(以下轉(zhuǎn)自http://www.vckbase.com/document/viewdoc/?id=398,原作者:黃晨量)
當(dāng)一個(gè)基于對(duì)話框的程序中有相當(dāng)多的控件時(shí),你一定會(huì)想到使用屬性頁(yè)來(lái)將這些控件分類放置。本文針對(duì)這種方法來(lái)討論幾種可能實(shí)現(xiàn)的方案。
方案一
在對(duì)話框上放置一個(gè)Tab Control的控件,再在對(duì)話框上放置所需的控件(本例放置了2個(gè)按鈕,試圖在每個(gè)標(biāo)簽中顯示一個(gè))。然后利用Class Wizard來(lái)為Tab Control控件創(chuàng)建一個(gè)控件變量,該變量是CTabCtrl類的,再為其他控件也創(chuàng)建相應(yīng)的控件類。 在主對(duì)話框的初始函數(shù)中CProperty1Dlg::OnInitDialog()加入如下代碼: //本例插入兩個(gè)標(biāo)簽,實(shí)際運(yùn)用中可通過(guò)循環(huán)插入所需個(gè)數(shù)的標(biāo)簽,運(yùn)行后默認(rèn)第一個(gè)標(biāo)簽被選中m_tab.InsertItem( 0, _T("Tab1") );
m_tab.InsertItem( 1, _T("Tab2") );
//將不是第一個(gè)標(biāo)簽的控件隱藏掉,只留下你要的控件
m_button2.ShowWindow( SW_HIDE ); 再利用ClassWizard處理Tab Control的 TCN_SELCHANGE 的消息。在消息處理函數(shù)中,利用CWnd::ShowWindow來(lái)使相應(yīng)的控件顯示和隱藏。 void CProperty1Dlg::OnSelchangeTab1(NMHDR* pNMHDR, LRESULT* pResult)
{
//GetCurSel返回當(dāng)前被選中的標(biāo)簽的索引號(hào)(以0為基礎(chǔ)算起)
int sel = m_tab.GetCurSel();
switch(sel)
{
case 0:
m_button1.ShowWindow( SW_SHOW );
m_button2.ShowWindow( SW_HIDE );
break;
case 1:
m_button2.ShowWindow( SW_SHOW );
m_button1.ShowWindow( SW_HIDE );
break;
}
*pResult = 0;
} 這樣做以后就可以使界面上的控件在不同的標(biāo)簽中顯示了,但是這個(gè)方案也有很多弊病。
所有的控件仍然在一個(gè)對(duì)話框內(nèi),在使用對(duì)話框編輯器進(jìn)行編輯時(shí),操作很不方便。
為了能分類顯示控件,必須用ClassWizard為每一 個(gè)控件創(chuàng)建一個(gè)控件變量,以便利用各控件變量的CWnd基類的ShowWindow函數(shù)來(lái)顯示和 隱藏。有時(shí)為了使用DDX和DDV機(jī)制來(lái)進(jìn)行數(shù)據(jù)交換,還要?jiǎng)?chuàng)建一些存放值的變量,這樣就使得整個(gè)對(duì)話框類變得相當(dāng)龐大難以操作。
當(dāng)然你也可以使用數(shù)組來(lái)存放那些控件變量或值變量,但是這樣并不是最好,有時(shí)一些不相關(guān)的控件變量放入一個(gè)數(shù)組中,通過(guò)沒(méi)有實(shí)際意義的數(shù)組索引號(hào)來(lái)訪問(wèn)控 件,對(duì)程序的編寫會(huì)造成麻煩。 最好能將所有控件進(jìn)行分類,放入不通對(duì)話框類中,這些對(duì)話框作為子對(duì)話框出現(xiàn)在主對(duì)話框中。可以。現(xiàn)在看看方案二。
方案二
這個(gè)方案中,我將使用MFC中現(xiàn)成的CPropertySheet和CPropertyPage類來(lái)完成將控件分散到各個(gè)對(duì)話框類中。首先加入兩個(gè)(或數(shù)個(gè))對(duì)話框資源。修改各對(duì)話框資源的屬性,將對(duì)話框的Caption屬性改為你要在標(biāo)簽上所顯示的文字。將對(duì)話框的Style屬 性改為:Child, Border屬性改為:Thin, 只選中Title Bar復(fù)選框,去掉其他復(fù)選框。然后你可以在這些對(duì)話框中加入要分開(kāi)顯示的各個(gè)控件。
為上述對(duì)話框資源分別制作一個(gè)對(duì)話框類,該對(duì)話框類是從CPropertyPage繼承。這樣一來(lái)各子對(duì)話框類就好了,主對(duì)話框類可以直接使用CPropertySheet類。使用如下代碼即可:
CPropertySheet sheet("屬性頁(yè)對(duì)話框");CPage1 page1;
CPage2 page2;
//加入子對(duì)話框作為一個(gè)屬性頁(yè)
sheet.AddPage(&page1);
sheet.AddPage(&page2);
//產(chǎn)生一個(gè)模態(tài)對(duì)話框,也可以使用Create方法來(lái)產(chǎn)生一個(gè)非模態(tài)對(duì)話框(具體參見(jiàn)MSDN)
sheet.DoModal(); 這樣這個(gè)對(duì)話框效果如下:
但是會(huì)有人問(wèn),如何在主對(duì)話框中放置其他控件呢?如果直接使用CPropertySheet的話,是不可以的,但是別忘了我們可以從CPropertySheet類繼承自己的類啊!下面來(lái)看看方案三的做法。
方案三
首先還是要?jiǎng)?chuàng)建那些要在屬性頁(yè)中的顯示的子對(duì)話框類,創(chuàng)建步驟和方案二一樣,都是從CPropertyPage繼承。這次我們將從CPropertySheet類繼承自己的類(假設(shè)類名為CMySheet)。我們要在這里放上一個(gè)button控件。那么現(xiàn)在先在CMySheet中加入一個(gè)CButton類的成員變量m_button。
在CMySheet類中的OnInitDialog()函數(shù)里,這樣寫:
BOOL bResult = CPropertySheet::OnInitDialog();//取得屬性頁(yè)的大小
CRect rectWnd;
GetWindowRect(rectWnd);
//調(diào)整對(duì)話框的寬度
SetWindowPos(NULL, 0, 0,rectWnd.Width() + 100,rectWnd.Height(),SWP_NOMOVE | SWP_NOZORDER | SWP_NOACTIVATE);
CRect rectButton(rectWnd.Width() + 25, 25,rectWnd.Width()+75, 75);
//用程序創(chuàng)建一個(gè)按鈕
m_button.Create("Button", BS_PUSHBUTTON, CRect(rectWnd.Width(), 25,rectWnd.Width()+75, 50) , this, 1);
//顯示這個(gè)按鈕
m_button.ShowWindow( SW_SHOW );
CenterWindow();
return bResult; 效果如下:
使用方案三雖然能在主對(duì)話框中加入控件,但是也比較麻煩,首先所加的控件只能在屬性頁(yè)的右邊或下邊。并且用程序來(lái)產(chǎn)生控件比較煩瑣,位置與大小不易控制。那么還有其他方法,既能在對(duì)話框中加入屬性頁(yè),又能在主對(duì)話框隨意添加控件?還是有的,看看方案四。
方案四
這次我們不從CPropertySheet繼承自己的類,還是直接使用它。各屬性頁(yè)的子對(duì)話框類還是需要的,創(chuàng)建方法和上述兩個(gè)方案相同。首先我們新建一個(gè)基于對(duì)話框的工程。在編輯已有的一個(gè)主對(duì)話框中可以自由加一些所需的控件,但是得留出一定的空間用于放置屬性頁(yè)。
在主對(duì)話框類里加入一個(gè)CPropertySheet類的一個(gè)成員變量(m_sheet)代表整個(gè)屬性頁(yè)。再加入一些各子對(duì)話框類的實(shí)例作為成員變量(m_page1、m_page2……)。
在主對(duì)話框類的OnInitDialog()函數(shù)中加入:
//加入標(biāo)簽,標(biāo)簽名由各個(gè)子對(duì)話框的標(biāo)題欄決定m_sheet.AddPage(&m_page1);
m_sheet.AddPage(&m_page2);
//用Create來(lái)創(chuàng)建一個(gè)屬性頁(yè)
m_sheet.Create(this, WS_CHILD | WS_VISIBLE, WS_EX_CONTROLPARENT);
RECT rect;
m_sheet.GetWindowRect(&rect);
int width = rect.right - rect.left;
int height = rect.bottom - rect.top;
//調(diào)整屬性頁(yè)的大小和位置
m_sheet.SetWindowPos(NULL, 20, 50, 0, 0, SWP_NOSIZE | SWP_NOZORDER | SWP_NOACTIVATE); 效果如下:
這個(gè)方案可以自由在主對(duì)話框中加一些必要的控件,而且屬性頁(yè)中的控件也都分散在了各個(gè)子對(duì)話框類中,使用非常方便。
但是這樣也有一些缺陷:主對(duì)話框不能處理屬性頁(yè)上標(biāo)簽的消息,即點(diǎn)擊標(biāo)簽時(shí)無(wú)法通知主對(duì)話框。(可能筆者水平有限,理論上應(yīng)該可以,但筆者尚未解決這個(gè)問(wèn)題)
方案五
這次我們?nèi)匀灰褂肨ab Control,并且從CTabCtrl控件類繼承自己的類(CTabSheet)來(lái)處理。(此方法來(lái)自CodeGuru的一篇文章,本人稍做修改使其使用更簡(jiǎn)便)首先我先介紹一下如何使用CTabSheet。
先要制作子對(duì)話框類,這次的子對(duì)話框類不要從CPropertyPage繼承,而是直接從CDialog繼承。并且各個(gè)子對(duì)話框資源的屬性應(yīng)設(shè)置為:Style為Child, Border為None。
在主對(duì)話框資源中,加入一個(gè)Tab Control,并且適當(dāng)調(diào)整位置和大小。利用ClassWizard來(lái)為這個(gè)Tab Control創(chuàng)建一個(gè)CTabSheet的控件變量。
在主對(duì)話框的OnInitDialog()加入:
m_sheet.AddPage("tab1", &m_page1, IDD_DIALOG1);m_sheet.AddPage("tab2", &m_page2, IDD_DIALOG2);
m_sheet.Show(); 就這樣就可以在對(duì)話框上制作出一個(gè)完美的屬性頁(yè)了。效果和上圖完全一樣。
下面我就來(lái)講講CTabSheet類的細(xì)節(jié)內(nèi)容。
CTabSheet是從CTabCtrl繼承來(lái)的,用于Tab Control的控件類。在類中有一個(gè)成員變量用來(lái)記錄各子對(duì)話框的指針CDialog* m_pPages[MAXPAGE]; MAXPAGE是該類所能加載的標(biāo)簽的最大值。
類中有一個(gè)AddPage方法,用于記錄子對(duì)話框的指針和所使用對(duì)話框資源的ID號(hào)。
BOOL CTabSheet::AddPage(LPCTSTR title, CDialog *pDialog,UINT ID){
if( MAXPAGE == m_nNumOfPages )
return FALSE;
//保存目前總的子對(duì)話框數(shù)
m_nNumOfPages++;
//記錄子對(duì)話框的指針、資源ID、要在標(biāo)簽上顯示的文字
m_pPages[m_nNumOfPages-1] = pDialog;
m_IDD[m_nNumOfPages-1] = ID;
m_Title[m_nNumOfPages-1] = title;
return TRUE;
} 在使用AddPage加入了若干子對(duì)話框后,必須調(diào)用CTabSheet的Show方法來(lái)真正生成標(biāo)簽和子對(duì)話框。 void CTabSheet::Show()
{
//利用CDialog::Create來(lái)創(chuàng)建子對(duì)話框,并且使用CTabCtrl::InsertItem來(lái)加上相應(yīng)的標(biāo)簽
for( int i=0; i < m_nNumOfPages; i++ )
{
m_pPages[i]->Create( m_IDD[i], this );
InsertItem( i, m_Title[i] );
}
//由于對(duì)話框顯示時(shí)默認(rèn)的是第一個(gè)標(biāo)簽被選中,所以應(yīng)該讓第一個(gè)子對(duì)話框顯示,其他子對(duì)話框隱藏
m_pPages[0]->ShowWindow(SW_SHOW);
for( i=1; i < m_nNumOfPages; i++)
m_pPages[i]->ShowWindow(SW_HIDE);
SetRect();
} 生成好標(biāo)簽和子對(duì)話框后,調(diào)用CTabSheet::SetRect來(lái)計(jì)算并調(diào)整屬性頁(yè)的大小。 void CTabSheet::SetRect()
{
CRect tabRect, itemRect;
int nX, nY, nXc, nYc;
//得到Tab Control的大小
GetClientRect(&tabRect);
GetItemRect(0, &itemRect);
//計(jì)算出各子對(duì)話框的相對(duì)于Tab Control的位置和大小
nX=itemRect.left;
nY=itemRect.bottom+1;
nXc=tabRect.right-itemRect.left-2;
nYc=tabRect.bottom-nY-2;
//利用計(jì)算出的數(shù)據(jù)對(duì)各子對(duì)話框進(jìn)行調(diào)整
m_pPages[0]->SetWindowPos(&wndTop, nX, nY, nXc, nYc, SWP_SHOWWINDOW);
for( int nCount=1; nCount < m_nNumOfPages; nCount++ )
m_pPages[nCount]->SetWindowPos(&wndTop, nX, nY, nXc, nYc, SWP_HIDEWINDOW);
} 在單擊標(biāo)簽欄后,應(yīng)該是相應(yīng)的子對(duì)話框顯示,正在顯示的子對(duì)話框應(yīng)該隱藏。因此利用ClassWizard來(lái)處理WM_LBUTTONDOWN消息。 void CTabSheet::OnLButtonDown(UINT nFlags, CPoint point)
{
CTabCtrl::OnLButtonDown(nFlags, point);
//判斷是否單擊了其他標(biāo)簽
if(m_nCurrentPage != GetCurFocus())
{
//將原先的子對(duì)話框隱藏
m_pPages[m_nCurrentPage]->ShowWindow(SW_HIDE);
m_nCurrentPage=GetCurFocus();
//顯示當(dāng)前標(biāo)簽所對(duì)應(yīng)的子對(duì)話框
m_pPages[m_nCurrentPage]->ShowWindow(SW_SHOW);
}
} 這樣利用CTabSheet這個(gè)類就可以輕松地在對(duì)話框上放置自己的屬性頁(yè)了,并且控件都分散在各子對(duì)話框類中,符合對(duì)象封裝的思想。而且用這個(gè)方法來(lái)制 作屬性頁(yè)就可以利用ClassWizard來(lái)輕松地生成消息映射處理Tab Control的消息了。例如:可以處理TCN_SELCHANGE消息來(lái)對(duì)切換了標(biāo)簽時(shí)進(jìn)行一些動(dòng)作。
總結(jié)
以上是生活随笔為你收集整理的对话框中加入标签页的5种方法的全部?jī)?nèi)容,希望文章能夠幫你解決所遇到的問(wèn)題。
- 上一篇: 教你如何使用EXCEL中的lookup函
- 下一篇: 猪肉价格继续下降 部分地区跌破40元每