VC++在对话框中加入属性页
当一个基于对话框的程序中有相当多的控件时,你一定会想到使用属性页来将这些控件分类放置。本文针对这种方法来讨论几种可能实现的方案。
方案一
本方案的例子请见源代码打包文件中的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;
}
这样做以后就可以使界面上的控件在不同的标签中显示了,但是这个方案也有很多弊病。
所有的控件仍然在一个对话框内,在使用对话框编辑器进行编辑时,操作很不方便。
为了能分类显示控件,必须用ClassWizard为每一个控件创建一个控件变量,以便利用各控件变量的CWnd基类的ShowWindow函数来显示和隐藏。有时为了使用DDX和DDV机制来进行数据交换,还要创建一些存放值的变量,这样就使得整个对话框类变得相当庞大难以操作。
当然你也可以使用数组来存放那些控件变量或值变量,但是这样并不是最好,有时一些不相关的控件变量放入一个数组中,通过没有实际意义的数组索引号来访问控件,对程序的编写会造成麻烦。 最好能将所有控件进行分类,放入不通对话框类中,这些对话框作为子对话框出现在主对话框中。可以。现在看看方案二。
方案二
本方案的例子请见源代码打包文件中的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消息来对切换了标签时进行一些动作。
VC++在对话框中加入属性页的更多相关文章
- MFC编程入门之十五(对话框:一般属性页对话框的创建及显示)
属性页对话框包括向导对话框和一般属性页对话框两类,上一节讲了如何创建并显示向导对话框,本节将继续介绍一般属性页对话框的创建和显示. 实际上,一般属性页对话框的创建和显示过程和向导对话框是很类似的.将上 ...
- VS2010/MFC对话框:一般属性页对话框的创建及显示
一般属性页对话框的创建及显示 本节将介绍一般属性页对话框的创建和显示. 实际上,一般属性页对话框的创建和显示过程和向导对话框是很类似的.鸡啄米将上一节中的向导对话框进行少量修改,使其成为一般属性页对话 ...
- VS2010/MFC编程入门之十五(对话框:一般属性页对话框的创建及显示)
属性页对话框包括向导对话框和一般属性页对话框两类,上一节鸡啄米讲了如何创建并显示向导对话框,本节将继续介绍一般属性页对话框的创建和显示. 实际上,一般属性页对话框的创建和显示过程和向导对话框是很类似的 ...
- VS2010-MFC(对话框:一般属性页对话框的创建及显示)
转自:http://www.jizhuomi.com/software/169.html 属性页对话框包括向导对话框和一般属性页对话框两类,上一节演示了如何创建并显示向导对话框,本节将继续介绍一般属性 ...
- 积累的VC编程小技巧之属性页
1.属性页的添加: 创建对话框的类,该类要从CpropertyPage继承:然后在要添加该对话框为属性页的类(头文件)里创建CpropertySheet类的一个对象m_tabsheet和新创建的对话框 ...
- directshow filter中添加属性页
directShow 属性页的制作,为CBall filter加了一个属性页 具体为分以下步骤: 1.在要显示属性的类中继承现ISpecifyPropertyPages类,并实现此类的GetPages ...
- vc++ 不同对话框中传递信息的方法(基于自定义消息SendMessage) (转载)
转载自:http://blog.csdn.net/myj0513/article/details/6827360 背景: 新建了一个基于对话框的MFC程序,在主对话框中添加tabcontrol控件,又 ...
- VS2010/MFC编程入门之十三(对话框:属性页对话框及相关类的介绍)
前面讲了模态对话框和非模态对话框,本节开始鸡啄米讲一种特殊的对话框--属性页对话框.另外,本套教程所讲大部分对VC++各个版本均可适用或者稍作修改即可,但考虑到终究还是基于VS2010版本的,所以将& ...
- MFC编程入门之十三(对话框:属性页对话框及相关类的介绍)
前面讲了模态对话框和非模态对话框,本节来将一种特殊的对话框--属性页对话框. 属性页对话框的分类 属性页对话框想必大家并不陌生,XP系统中桌面右键点属性,弹出的就是属性页对话框,它通过标签切换各个页面 ...
随机推荐
- Qt 设置背景图片3种方法(QPalette可以做无数的事情,一旦控件指定了调色板,就要乖乖听它的话;QPainter当场绘制当然也没有问题,还有就是QSS)
方法1. setStylSheet{"QDialog{background-image:url()"}} //使用styleSheet 这种方法的好处是继承它的dialog都会自 ...
- 盛希泰:办公室就像男人的春药——人的一生的精力是有限的,你把有限的时间分配给谁决定你的成败——你有N多选择,你人生的积累就是N多选择加起来的结果
欢迎关注“创事记”的微信订阅号:sinachuangshiji 创事记注:12月22日晚上,盛希泰在清华大学旧经管报告厅面对清华师生讲了一堂<创业引导课>.本文由洪泰帮根据课堂录音整理完成 ...
- 打包mysql、tomcat、jdk为一个软件
打包mysql.tomcat.jdk为一个软件 博客分类: 成长中的点滴 . 我们在本地开发web应用的时候,直接在IDE里面就可以完成jdk.容器.数据库的配置和集成. 但是如果当我们把应用程序交 ...
- 基于Visual C++2013拆解世界五百强面试题--题6-double类型逆序
请设计一个函数,不许用到字符串函数,用数学运算,将double类型数据转换,例如123.456转换成654.321 首先想到依次提取他的每一个位数,然后进行运算,移动每一位数到相应位置,结果相加就能逆 ...
- Android蓝牙开发深入解析
1. 使用蓝牙的响应权限 代码如下: <uses-permission android:name="android.permission.BLUETOOTH" /> ...
- 如何在macox下面配置集成ios和android游戏教程
教程截图: 1.准备工作,配置开发环境: 开发环境:mac ox 10.7.3 + xcode4.2 + ndk r7 + eclipse helios 部署环境:中兴v880 root过了 ...
- cpu有哪些架构
原文:http://blog.csdn.net/wyzxg/article/details/5027738 author:skatetime:2009/12/17 这几天在下载RPM包的时候,总会看见 ...
- matrix67:kmp算法详解
个人认为KMP是最没有必要讲的东西,因为这个东西网上能找到很多资料.但网上的讲法基本上都涉及到“移动(shift)”.“Next函数”等概念,这非常容易产生误解(至少一年半前我看这些资料学习KMP时就 ...
- BZOJ 1601 [Usaco2008 Oct]灌水
1601: [Usaco2008 Oct]灌水 Time Limit: 5 Sec Memory Limit: 162 MB Description Farmer John已经决定把水灌到他的n(1 ...
- java学习之二叉树的实现
二叉树是一种数据结构,每个节点都有两个子节点. 二叉树的遍历有三种方式, 先序遍历是 根节点,左子树,右子树: 中序遍历是 左子树,根节点,右子树: 后序遍历是 左子树,右子树,根节点: java实现 ...