`

对话框中加入标签页的5种方法

MFC 
阅读更多
(2008-07-08 15:25 博客搬家拾遗)

标签页是MFC控件里有一点难用的东西,今天看到了一篇相关文章,觉得写得比较系统,而且浅显易懂,特地转来,与大家分享的同时也留备自己以后查用。
(以下转自http://www.vckbase.com/document/viewdoc/?id=398,原作者:黄晨量

当一个基于对话框的程序中有相当多的控件时,你一定会想到使用属性页来将这些控件分类放置。本文针对这种方法来讨论几种可能实现的方案。

方案一

在对话框上放置一个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;
}
这样做以后就可以使界面上的控件在不同的标签中显示了,但是这个方案也有很多弊病。

所有的控件仍然在一个对话框内,在使用对话框编辑器进行编辑时,操作很不方便。
为了能分类显示控件,必须用ClassWizard为每一 个控件创建一个控件变量,以便利用各控件变量的CWnd基类的ShowWindow函数来显示和 隐藏。有时为了使用DDX和DDV机制来进行数据交换,还要创建一些存放值的变量,这样就使得整个对话框类变得相当庞大难以操作。
当然你也可以使用数组来存放那些控件变量或值变量,但是这样并不是最好,有时一些不相关的控件变量放入一个数组中,通过没有实际意义的数组索引号来访问控 件,对程序的编写会造成麻烦。 最好能将所有控件进行分类,放入不通对话框类中,这些对话框作为子对话框出现在主对话框中。可以。现在看看方案二。

方案二

这个方案中,我将使用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类继承自己的类啊!下面来看看方案三的做法。

方案三

首先还是要创建那些要在属性页中的显示的子对话框类,创建步骤和方案二一样,都是从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;
效果如下:

使用方案三虽然能在主对话框中加入控件,但是也比较麻烦,首先所加的控件只能在属性页的右边或下边。并且用程序来产生控件比较烦琐,位置与大小不易控制。那么还有其他方法,既能在对话框中加入属性页,又能在主对话框随意添加控件?还是有的,看看方案四。

方案四

这次我们不从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);
效果如下:

这个方案可以自由在主对话框中加一些必要的控件,而且属性页中的控件也都分散在了各个子对话框类中,使用非常方便。

但是这样也有一些缺陷:主对话框不能处理属性页上标签的消息,即点击标签时无法通知主对话框。(可能笔者水平有限,理论上应该可以,但笔者尚未解决这个问题)

方案五

这次我们仍然要使用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消息来对切换了标签时进行一些动作。
分享到:
评论

相关推荐

    多用户聊天对话框

    标题中的“多用户聊天对话框”指的是一个支持多个用户同时在线交流的应用程序界面设计,它通常包含文字输入、发送、接收、以及用户列表等功能。在本项目中,它基于MFC(Microsoft Foundation Classes)框架构建,这...

    把一个tab控件加入到一个form或对话框中.rar_Tabú_tab控件

    在Windows应用程序开发中,Tab控件是一种常用的界面元素,它允许用户通过点击不同的标签页来切换显示不同的内容区域。在本文中,我们将深入探讨如何将一个Tab控件添加到一个Form或对话框中,以及与之相关的编程知识...

    js确认删除对话框适用于a标签及submit

    从提供的文件信息中,我们可以提炼出关于JavaScript(js)确认删除对话框的关键知识点,这些知识点包括如何将确认删除对话框应用于超链接(a标签)以及表单提交(submit)。以下是详细的知识点: ### JavaScript ...

    jQuery模态对话框提示代码.zip

    模态对话框是网页设计中常见的一种交互元素,用于向用户显示重要的信息、提示或者获取用户的确认。在这个压缩包中,我们可以期待找到一个能够直接运行并且可进行二次开发的jQuery模态对话框代码示例。 模态对话框...

    JS实现浏览器打印、打印预览示例

    为了防止这种情况,可以使用一种方法临时移除不需要打印的页面内容,并在打印完成后恢复,或者在打印内容中加入脚本以便在打印后重新加载原始页面。 9. 用户体验:在提供打印和打印预览功能时,还需考虑用户的操作...

    接收弹出页面的回传值

    首先,让我们明确“弹出页面”通常指的是使用`window.open()`方法打开的新窗口或标签页。这个方法允许我们创建一个新的浏览器窗口,并指定要加载的URL。例如: ```javascript var newWindow = window.open('b.html'...

    如何在PPT2021中嵌入视频_1.docx

    - 打开PowerPoint 2021,进入“插入”标签页。 - 点击“影片”下的“文件中的影片”选项,打开“插入影片”对话框。 - 在对话框中选择视频文件并点击“确定”,视频的第一帧将出现在幻灯片中。 - 可以调整视频的播放...

    element ui 对话框el-dialog关闭事件详解

    在标签中加入@close=’closeDialog’ mothods中加入 //关闭弹框的事件 closeDialog(){ this.xxx = '';//清空数据 }, 以上这篇element ui 对话框el-dialog关闭事件详解就是小编分享给大家的全部内容了,希望能给...

    Dreamweaver CS6在网页中插入背景音乐.pdf

    首先,我们需要知道在HTML中插入背景音乐有两种常见方法:一种是通过`&lt;bgsound&gt;`标签,另一种是使用插件,如Flash或JavaScript。在Dreamweaver CS6中,我们将重点介绍使用`&lt;bgsound&gt;`标签的步骤。 步骤一,打开...

    在网站文章中加入锚文本或者超链接的方法.pdf

    锚文本是链接的一种表现形式,它是指在文字中嵌入链接,当用户点击这些特定的文字时,会被导向另一个网页。锚文本不仅能够方便用户快速获取更多信息,还对搜索引擎理解页面内容有着重要作用。搜索引擎会分析锚文本的...

    设为首页加入收藏

    以上介绍了两种常见的网页功能——“设为首页”和“加入收藏”的实现方法及其背后的技术原理。需要注意的是,由于这些方法依赖于特定浏览器的支持,因此在实际开发中需要考虑到浏览器兼容性的问题。为了提高用户体验...

    打开超链需要“确认”对话框的方法

    - `window.open("", "测试", "width=340,height=163,toolbar=0,status=0,menubar=0,resize=0")`:如果用户确认,则打开一个新的窗口或标签页,这里使用了`window.open`方法,并设置了一些参数来控制新窗口的外观和...

    windows 树形控件

    在描述中提到的“开发属性控件、加入页、应用编程”可能是指利用CTabCtrl控件创建类似Windows资源管理器中的属性对话框,这种对话框包含多个选项卡,每个选项卡代表一个特定的属性或功能区域。CTabCtrl控件使得用户...

    如何在VS2005 工具箱中加入 DotNetBar 控件

    1. **查看工具箱设置**:再次打开“工具箱”对话框,确认是否已正确选择“.NET Framework 组件”标签页,并且已经勾选了“自动更新工具箱”选项。 2. **重启Visual Studio**:关闭并重新启动Visual Studio 2005,...

    vc++6.0调试方法

    在`C/C++`标签页的`Category`选项中选择`General`,将`Optimizations`设为`Disable (Debug)`,`Debug info`设为`Program Database`。在`Link`标签页中勾选`Generate debug info`复选框。这样的设置使得程序具有调试...

    IR PSpice 器件库在ORCAD中的使用方法.pdf

    2. **配置库路径**:在新打开的配置文件窗口中,转至“Configuration Files”标签页,在“Category”下拉菜单中选择“Library”项。 3. **添加IR2302.lib文件**:点击“Browse”按钮,找到并选中IR2302.lib文件,此...

    jQuery_EasyUI

    - **实现方法**:调用`.tabs('add')`方法添加新的标签页。 #### 四、DataGrid 数据格 ##### 知识点1:转换HTML表格到DataGrid - **概念**:将静态的HTML表格转换为具有更多交互功能的数据表格。 - **实现方法**:...

    离开页面时提醒你加入书签.rar

    标题“离开页面时提醒你加入书签.rar”所涉及的核心技术是JavaScript的事件处理和书签功能。在网页设计中,书签是一个重要的用户交互元素,允许用户方便地保存当前页面以便日后访问。这个压缩包可能包含了一个...

    如何在Myeclipse中的网页工程中加入 MySQL 驱动

    在右侧的详细信息面板中,你会看到有四个标签页,分别是"Libraries"(库)、"Order and Export"(顺序与导出)、"Source"(源)和"Javadoc Location"(Javadoc位置)。这里我们关注的是"Libraries"。 点击...

    vc++6.0调试方法.pdf

    接着,在"Link"标签页中勾选Generate debug info复选框。这样创建的版本既有调试信息,又接近Release性能。 3. **Release与Debug的区别**:Release版本是用于发布的,其代码优化程度高,运行速度快,但不包含调试...

Global site tag (gtag.js) - Google Analytics