MFC单文档添加CTabCtrl(一)
前阵子给一个老师写一个东西,功能做的差不多了,他说希望把界面做的好看一下,然后想了想,那就在界面表面添加一个Tab控件来糊弄一下他呗。
好像大部分添加Tab的都是在对话框里面添加的,然后我已开始给他写好的东西是基于单文档结构的MFC的,查了一下添加的方法,这里留个笔记吧。
我查的过程中发现了两种方法,这里先说已经“破解”了的一种,另一种周末研究研究,还有一个别人写好的类似于Excel的Sheet切换效果的,有空再放上来。
正文:
这个方法的思路就是自己做一个对话框,令它继承于CFormView,我们假设这个对话框生成的类名叫CTab_View,这里有一个要注意,要把对话框的Style设置成child,System Menu和Title Bar设置成false。
然后这个CTab_View对话框中只有一个控件,就是一个CTabCtrl,所以说说白了这个思路就是用这个对话框继承自CFormView后取代C****View。当然为了好看,要把这个对话框的Boarder给设成None。
然后我们为要添加的每个标签页做一个对话框,假设这些对话框生成的类名叫CTab1,CTab2。。。(本例中就只用两个标签页吧。。),每个对话框的ID分别是IDD_DIALOG_TAB1,IDD_DIALOG_TAB2。。。同样,把这每个子对话框的Boarder给设成None,Style设置成Child。
然后在CTab_View类中添加两个成员变量,分别是:
CTab1 m_tab1; CTab2 m_tab2;
当然要记得添加头文件。。。
然后重写OnInitialUpdate和PreCreateWindow两个函数,另外再为CTabCtrl添加一个OnTcnSelchangeTab1的事件响应。代码如下:
// CTab_View 消息处理程序 void CTab_View::OnInitialUpdate() { CFormView::OnInitialUpdate(); ((CTabCtrl *)GetDlgItem(IDC_TAB1))->InsertItem(0,"标签名1"); ((CTabCtrl *)GetDlgItem(IDC_TAB1))->InsertItem(1,"标签名2"); m_tab1.Create(IDD_DIALOG_TAB1,GetDlgItem(IDC_TAB1)); m_tab2.Create(IDD_DIALOG_TAB2,GetDlgItem(IDC_TAB1)); m_tab1.ShowWindow(TRUE); } void CTab_View::OnTcnSelchangeTab1(NMHDR *pNMHDR, LRESULT *pResult) { // TODO:切换标签页时做的响应 int nCurSel = ((CTabCtrl *)GetDlgItem(IDC_TAB1))->GetCurSel(); switch (nCurSel) { case 0: m_tab1.ShowWindow(TRUE); m_tab2.ShowWindow(FALSE); break; case 1: m_tab1.ShowWindow(FALSE); m_tab2.ShowWindow(TRUE); break; } *pResult = 0; } BOOL CTab_View::PreCreateWindow(CREATESTRUCT& cs) { // TODO: cs.style &=~WS_BORDER; return CFormView::PreCreateWindow(cs); }
接下来把你的C****App里面的BOOL C****App::InitInstance()里面RUNTIME_CLASS(C***View))替换成RUNTIME_CLASS(CTab_View))。上面之所以要把System Menu和Title Bar设置成false因为这里下面的这个判断会跟OpenFile那个东西打交道,而你自己做的对话框现在已经是主要的View了,但是却没有菜单中的OpenFile的那个东西,所以程序就会崩溃,懂?
// 调度在命令行中指定的命令。如果 // 用 /RegServer、/Register、/Unregserver 或 /Unregister 启动应用程序,则返回 FALSE。 if (!ProcessShellCommand(cmdInfo)) return FALSE;
在接下来CMainFrame里面重写一个Onsize函数即可,如下面所示:
void CMainFrame::OnSize(UINT nType, int cx, int cy) { CFrameWnd::OnSize(nType, cx, cy); CRect rect; m_pViewActive->GetClientRect(&rect); m_pViewActive->GetDlgItem(IDC_TAB1)->MoveWindow(rect,TRUE); m_pViewActive->GetDlgItem(IDC_TAB1)->GetClientRect(&rect); rect.top+=30; rect.bottom-=4; rect.left+=4; rect.right-=4; ((CTab_View *)m_pViewActive)->m_tab1.MoveWindow(rect, TRUE); ((CTab_View *)m_pViewActive)->m_tab1.GetClientRect(&rect); ((CTab_View *)m_pViewActive)->m_tab1.GetDlgItem(IDC_STATIC)->MoveWindow(rect,TRUE); m_pViewActive->GetDlgItem(IDC_TAB1)->GetClientRect(&rect); rect.top+=30; rect.bottom-=4; rect.left+=4; rect.right-=4; ((CTab_View *)m_pViewActive)->m_tab2.MoveWindow(rect, TRUE); ((CTab_View *)m_pViewActive)->m_tab2.GetClientRect(&rect); ((CTab_View *)m_pViewActive)->m_tab2.GetDlgItem(IDC_STATIC)->MoveWindow(rect,TRUE); // TODO: 在此处添加消息处理程序代码 }
这个不多说,就是在打开程序的时候把对话框放对位置,不然就会改掉你的Tab控件的Tab标题什么的。虽然他本来还有另外一个作用就是让你的标签页的子对话框随着整个程序的界面的缩放变化而变化,但是如果你设置了子标签页为Child了,这个功能本身就无所谓了。
为了起到定位作用,也为了美化界面,每个子对话框都添加了一个Group控件,然后为了让Group控件也随着对话框大小改变,才会有了最后的那两行。
另外为什么一定要重写CTab_View中的PreCreateWindow呢?因为如果不重写的话,OnSize中的m_pViewActive会为NULL。
【完】
本文内容遵从CC版权协议,转载请注明出自http://www.kylen314.com
您好 BOOL CTab_View::PreCreateWindow(CREATESTRUCT& cs)这个函数已经重写 但是m_pViewActive为什么还是NULL呢?