资料来源
http://www.vckbase.com/document/viewdoc/?id=398
在对话框中加入属性页 下载本文全部源代码 方案一 本方案的例子请见源代码打包文件中的Property1部分在对话框上放置一个Tab Control的控件,再在对话框上放置所需的控件(本例放置了2个按钮,试图在每个标签中显示一个)。然后利用Class Wizard来为Tab Control控件创建一个控件变量,该变量是CTabCtrl类的,再为其他控件也创建相应的控件类。 在主对话框的初始函数中CProperty1Dlg::OnInitDialog()加入如下代码: //本例插入两个标签,实际运用中可通过循环插入所需个数的标签,运行后默认第一个标签被选中 m_tab.InsertItem( 0, _T("Tab1") ); m_tab.InsertItem( 1, _T("Tab2") ); //将不是第一个标签的控件隐藏掉,只留下你要的控件 m_button2.ShowWindow( SW_HIDE );再利用ClassWizard处理Tab Control的 TCN_SELCHANGE 的消息。在消息处理函数中,利用CWnd::ShowWindow来使相应的控件显示和隐藏。 void CProperty1Dlg::OnSelchangeTab1(NMHDR* pNMHDR, LRESULT* pResult) { //GetCurSel返回当前被选中的标签的索引号(以0为基础算起) 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; }这样做以后就可以使界面上的控件在不同的标签中显示了,但是这个方案也有很多弊病。 所有的控件仍然在一个对话框内,在使用对话框编辑器进行编辑时,操作很不方便。 方案二 本方案的例子请见源代码打包文件中的Property2部分这个方案中,我将使用MFC中现成的CPropertySheet和CPropertyPage类来完成将控件分散到各个对话框类中。 首先加入两个(或数个)对话框资源。修改各对话框资源的属性,将对话框的Caption属性改为你要在标签上所显示的文字。将对话框的Style属性改为:Child, Border属性改为:Thin, 只选中Title Bar复选框,去掉其他复选框。然后你可以在这些对话框中加入要分开显示的各个控件。 为上述对话框资源分别制作一个对话框类,该对话框类是从CPropertyPage继承。这样一来各子对话框类就好了,主对话框类可以直接使用CPropertySheet类。使用如下代码即可: CPropertySheet sheet("属性页对话框"); CPage1 page1; CPage2 page2; //加入子对话框作为一个属性页 sheet.AddPage(&page1); sheet.AddPage(&page2); //产生一个模态对话框,也可以使用Create方法来产生一个非模态对话框(具体参见MSDN) sheet.DoModal();这样这个对话框效果如下: 但是会有人问,如何在主对话框中放置其他控件呢?如果直接使用CPropertySheet的话,是不可以的,但是别忘了我们可以从CPropertySheet类继承自己的类啊!下面来看看方案三的做法。 方案三 本方案的例子请见源代码打包文件中的Property3部分首先还是要创建那些要在属性页中的显示的子对话框类,创建步骤和方案二一样,都是从CPropertyPage继承。 这次我们将从CPropertySheet类继承自己的类(假设类名为CMySheet)。我们要在这里放上一个button控件。那么现在先在CMySheet中加入一个CButton类的成员变量m_button。 在CMySheet类中的OnInitDialog()函数里,这样写: BOOL bResult = CPropertySheet::OnInitDialog(); //取得属性页的大小 CRect rectWnd; GetWindowRect(rectWnd); //调整对话框的宽度 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); //用程序创建一个按钮 m_button.Create("Button", BS_PUSHBUTTON, CRect(rectWnd.Width(), 25,rectWnd.Width()+75, 50) , this, 1); //显示这个按钮 m_button.ShowWindow( SW_SHOW ); CenterWindow(); return bResult;效果如下: 使用方案三虽然能在主对话框中加入控件,但是也比较麻烦,首先所加的控件只能在属性页的右边或下边。并且用程序来产生控件比较烦琐,位置与大小不易控制。那么还有其他方法,既能在对话框中加入属性页,又能在主对话框随意添加控件?还是有的,看看方案四。 方案四 本方案的例子请见源代码打包文件中的Property4部分这次我们不从CPropertySheet继承自己的类,还是直接使用它。各属性页的子对话框类还是需要的,创建方法和上述两个方案相同。 首先我们新建一个基于对话框的工程。在编辑已有的一个主对话框中可以自由加一些所需的控件,但是得留出一定的空间用于放置属性页。 在主对话框类里加入一个CPropertySheet类的一个成员变量(m_sheet)代表整个属性页。再加入一些各子对话框类的实例作为成员变量(m_page1、m_page2……)。 在主对话框类的OnInitDialog()函数中加入: //加入标签,标签名由各个子对话框的标题栏决定 m_sheet.AddPage(&m_page1); m_sheet.AddPage(&m_page2); //用Create来创建一个属性页 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; //调整属性页的大小和位置 m_sheet.SetWindowPos(NULL, 20, 50, 0, 0, SWP_NOSIZE | SWP_NOZORDER | SWP_NOACTIVATE);效果如下: 这个方案可以自由在主对话框中加一些必要的控件,而且属性页中的控件也都分散在了各个子对话框类中,使用非常方便。 但是这样也有一些缺陷:主对话框不能处理属性页上标签的消息,即点击标签时无法通知主对话框。(可能笔者水平有限,理论上应该可以,但笔者尚未解决这个问题) 方案五 本方案的例子请见源代码打包文件中的Property5部分这次我们仍然要使用Tab Control,并且从CTabCtrl控件类继承自己的类(CTabSheet)来处理。(此方法来自CodeGuru的一篇文章,本人稍做修改使其使用更简便) 首先我先介绍一下如何使用CTabSheet。 先要制作子对话框类,这次的子对话框类不要从CPropertyPage继承,而是直接从CDialog继承。并且各个子对话框资源的属性应设置为:Style为Child, Border为None。 在主对话框资源中,加入一个Tab Control,并且适当调整位置和大小。利用ClassWizard来为这个Tab Control创建一个CTabSheet的控件变量。 在主对话框的OnInitDialog()加入: m_sheet.AddPage("tab1", &m_page1, IDD_DIALOG1); m_sheet.AddPage("tab2", &m_page2, IDD_DIALOG2); m_sheet.Show();就这样就可以在对话框上制作出一个完美的属性页了。效果和上图完全一样。 下面我就来讲讲CTabSheet类的细节内容。 CTabSheet是从CTabCtrl继承来的,用于Tab Control的控件类。在类中有一个成员变量用来记录各子对话框的指针CDialog* m_pPages[MAXPAGE]; MAXPAGE是该类所能加载的标签的最大值。 类中有一个AddPage方法,用于记录子对话框的指针和所使用对话框资源的ID号。 BOOL CTabSheet::AddPage(LPCTSTR title, CDialog *pDialog,UINT ID) { if( MAXPAGE == m_nNumOfPages ) return FALSE; //保存目前总的子对话框数 m_nNumOfPages++; //记录子对话框的指针、资源ID、要在标签上显示的文字 m_pPages[m_nNumOfPages-1] = pDialog; m_IDD[m_nNumOfPages-1] = ID; m_Title[m_nNumOfPages-1] = title; return TRUE; }在使用AddPage加入了若干子对话框后,必须调用CTabSheet的Show方法来真正生成标签和子对话框。 void CTabSheet::Show() { //利用CDialog::Create来创建子对话框,并且使用CTabCtrl::InsertItem来加上相应的标签 for( int i=0; i < m_nNumOfPages; i++ ) { m_pPages[i]->Create( m_IDD[i], this ); InsertItem( i, m_Title[i] ); } //由于对话框显示时默认的是第一个标签被选中,所以应该让第一个子对话框显示,其他子对话框隐藏 m_pPages[0]->ShowWindow(SW_SHOW); for( i=1; i < m_nNumOfPages; i++) m_pPages[i]->ShowWindow(SW_HIDE); SetRect(); }生成好标签和子对话框后,调用CTabSheet::SetRect来计算并调整属性页的大小。 void CTabSheet::SetRect() { CRect tabRect, itemRect; int nX, nY, nXc, nYc; //得到Tab Control的大小 GetClientRect(&tabRect); GetItemRect(0, &itemRect); //计算出各子对话框的相对于Tab Control的位置和大小 nX=itemRect.left; nY=itemRect.bottom+1; nXc=tabRect.right-itemRect.left-2; nYc=tabRect.bottom-nY-2; //利用计算出的数据对各子对话框进行调整 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); }在单击标签栏后,应该是相应的子对话框显示,正在显示的子对话框应该隐藏。因此利用ClassWizard来处理WM_LBUTTONDOWN消息。 void CTabSheet::OnLButtonDown(UINT nFlags, CPoint point) { CTabCtrl::OnLButtonDown(nFlags, point); //判断是否单击了其他标签 if(m_nCurrentPage != GetCurFocus()) { //将原先的子对话框隐藏 m_pPages[m_nCurrentPage]->ShowWindow(SW_HIDE); m_nCurrentPage=GetCurFocus(); //显示当前标签所对应的子对话框 m_pPages[m_nCurrentPage]->ShowWindow(SW_SHOW); } }这样利用CTabSheet这个类就可以轻松地在对话框上放置自己的属性页了,并且控件都分散在各子对话框类中,符合对象封装的思想。而且用这个方法来制作属性页就可以利用ClassWizard来轻松地生成消息映射处理Tab Control的消息了。例如:可以处理TCN_SELCHANGE消息来对切换了标签时进行一些动作。 本文共使用了5种方法在对话框中加属性页,当然应该还有其他方法,当本人水平有限,如果各位读者有更好的方法,可不要忘了告诉我。 |
最新评论 [发表评论] [文章投稿] | 查看所有评论 推荐给好友 打印 |
采用方案四,无法调整属性页大小,导致加载的属性页被缩小的,能有解决的办法吗?高手们,可以加我QQ:77359140,E-Mail:isc_117@163.com ( isabc 发表于 2007-7-3 14:52:00) 试了下第方案五,效果不错,比用 CPropertyPage 简便多了 但该方案中的代码有些小 BUG,就是在 SetRect() 方法中设置子对话框的位置时没有 TabControl 控件的属性,当标签位置不同时,子对话框显示位置会出错。 解决方法,通过 GetStyle() 获取 TabControl 的风格设置,判断 TCS_VERTICAL、TCS_BOTTOM、TCS_RIGHT 是否置位来分别计算子对话框的位置。 ( tuxw 发表于 2007-6-3 1:17:00) 请教楼主一个问题. 我在工程里运用了最后一种重载TabCtrl的方法,可是出现了一个问题. 在第一个属性页里(CDialog)包含一个listCtrl控件. list数据大于可视范围后没有出现滚动条. 我在猜想应该是list的父容器出现了问题.就是说最后一种重载TabCtrl的方法出现了问题. 现在很是困扰我,使我已经想重建工程,丢弃这种方法. 大家有好的办法,可以找我探讨. QQ: 5752108 eMail: danggang_82@163.com ( Winter_dg 发表于 2007-5-21 11:14:00) 这样的不爽,个人还是喜欢用按钮响应自定义消息,把多个对话框放到Static或者Group控件上面这样构成那样的效果, ( shellhy 发表于 2007-2-3 16:18:00) 我想问一下 怎么才能使方案四支持上下文的帮助呢? ( pixelor 发表于 2007-1-12 9:50:00) 那位大哥大姐帮个忙!我是刚开始学习VC!我的属性页一闪而过!(非模态)什么原因!十分感谢! ( kingbluesea 发表于 2006-11-16 19:13:00) 方案4:可以接收消息,可是无法初始化第二个窗口 m_sheet.AddPage(&m_page1); m_sheet.AddPage(&m_page2); m_sheet.Create(this, WS_CHILD | WS_VISIBLE, WS_EX_CONTROLPARENT); m_page2 加不进去,它的窗口句柄 hWnd 为空。 方案五:可以初始化,但是无法接收消息。郁闷!! 哪位大哥大姐帮帮忙,解决一下啊 ( bsnail 发表于 2006-10-13 11:36:00) 采用方案四,对第二个子窗口的列表控件无法进行处理。 当我在主对话框中的OnInitDialog() 对 第二个列表控件进行 m_page2.m_List2.InsertColumn(0, _T("文件路径"), LVCFMT_LEFT, nCol1Width); 处理的时候报错: Debug Assertion Failed! …… 如何解决啊,谢谢! ( bsnail 发表于 2006-10-13 10:16:00) 第四中方法 我的解决方法是在 每个子dialog里响应onshow消息,如果是显示那就是这个子窗口被显示了,就可以通过n中方法告诉主对话框了 ( lanhai 发表于 2006-10-12 17:40:00) 采用方案五 在属性页设置 listctrl 如何能让listctrl 支持拖拽 接收到 WM_DROPFILES 消息 谢谢! ( bsnail 发表于 2006-10-12 15:20:00) |