CTabCtrlSSL - 一个易于使用、灵活的扩展标签控件






4.90/5 (79投票s)
一个扩展标签控件,允许从对话框资源添加标签页
引言
本文已更新,详情请参见“更新”部分。
一点点病痛在家也能激发生产力,这真是太神奇了!我病了好几天,结果又一个扩展控件诞生了。这次我盯上了标签控件。在我看来,现如今人们更喜欢属性表和页面,所以标签控件似乎有点过时了。嗯,我尝试过使用它们,但没有得到我想要的结果。我需要在标签区域上方放置一些控件(有点像演示一样,但要多放几个控件),而使用属性表和页面我做不到。
标准标签控件的问题在于,要将其中的页面插入到控件中需要大量的精力。查阅MSDN时,我找到一个Jeff Prosise的例子,他实现了一个标签视图,CTabView,允许将标签指定为对话框资源。好吧,说到代码,我就像一个十岁的孩子玩手表一样:我必须弄弄清楚它是如何工作的,并思考如何能做得更好。
第一个问题是Jeff的解决方案是针对视图类的,而我需要一个控件。这之间的转换很容易,因为视图只是封装了控件。我遇到的第二个问题是,它在标签页的灵活性方面做得不够。我问自己:“我如何处理页面中的事件?”答案是允许客户端继承标签页类,并允许将标签页对象添加到控件中,而不仅仅是对话框资源。
为什么解决方案总是带来新问题?通过允许这种方法,我引入了一个棘手的释放标签页的场景。内部的标签控件在堆上创建标签页,并维护一个已添加页面的列表。当控件被销毁时,`delete
`会依次对每个页面进行调用。如果页面是在标签控件外部创建并传入的,这显然会导致问题。在这种情况下,标签控件会销毁外部创建的页面,然后父对话框会尝试做同样的事情!!
经过在Visual C++论坛上与Tomasz Sowinski的长时间讨论,我们找到了答案。标签控件有一个受保护的函数用于向控件添加页面,而公共函数调用它。实际上传递给受保护函数的是一个包含指向标签页的指针和一个指示标签控件是否应销毁它的标志的结构。这样,如果传入标签页,则标志设置为false,但如果标签控件创建标签页,则标志设置为true。在销毁时,只有标志设置为true的页面才会被标签控件销毁。其他页面就是别人的问题了。
如何使用
- 创建一个对话框资源和用于该对话框的类,以便在该对话框上添加标签控件。
- 从工具箱中向对话框资源添加一个标签控件。
- 为标签控件创建一个成员变量,并将其类型更改为
CTabCtrlSSL
。 - 在对话框类的
OnInitDialog
处理程序中,创建并添加您想在标签控件上显示的任何页面。
BOOL CCTabCtrlSSL_demoDlg::OnInitDialog () { CDialog::OnInitDialog (); // Setup the tab control int nPageID = 0; m_tabDemo.AddSSLPage (_T("Basic Tab"), nPageID++, IDD_TAB_BASIC); m_advancedTab.Create (IDD_TAB_ADVANCED, this); m_tabDemo.AddSSLPage (_T("Advanced Page"), nPageID++, &m_advancedTab); m_aboutTab.Create (IDD_TAB_ABOUT, this); m_tabDemo.AddSSLPage (_T("About"), nPageID++, &m_aboutTab); return TRUE; // return TRUE unless you set the focus to a control }
从上面的代码示例(取自演示)可以看出,要添加一个基本的标签,请调用AddSSLPage
并仅传入对话框资源的资源ID。对于高级标签,创建对话框资源,设置窗口样式为Child,边框样式为None,然后调用AddSSLPage
并传入指向您派生的CTabPageSSL
对象的指针。
为了延续我与CButtonSSL开始的风格,这里是类的文档。
CTabCtrlSSL
“标签控件”类似于笔记本中的分隔符或文件柜中的标签。通过使用标签控件,应用程序可以在同一窗口或对话框区域定义多个页面。每个页面包含一组信息或一组控件,应用程序会在用户选择相应标签时显示它们。CTabCtrlSSL
通过提供一个接口来扩展标准标签控件,该接口用于向控件添加以对话框资源定义的标签。这些标签在内部被定义为CTabPageSSL对象。还有一个用于将此类对象添加到标签控件的接口,允许通过继承CTabPageSSL
来定义自定义对象。
由CTabCtrlSSL
创建的任何标签页(通过传入对话框资源ID添加的标签页)都会被CTabCtrlSSL
销毁。客户端有责任销毁任何传递给CTabCtrlSSL
的CTabPageSSL
派生对象。
#include "TabCtrlSSL.h"
类成员
然后,当你开始迭代 2(这是构建迭代的开始)时,你可能想要复制测试用例并将它们重新分类到迭代 2。这还允许对测试用例进行粒度跟踪,并允许你说某个测试用例在一个迭代中是准备好的,但在另一个迭代中不是。同样,如何做到这一点取决于你以及你希望如何报告。 “场景”部分提供了更多细节。
CTabCtrlSSL | 构造一个CTabCtrlSSL 对象。 |
页面函数
AddSSLPage | 向控件添加一个标签。 |
RemoveSSLPage | 从控件中移除一个标签。 |
GetSSLPageCount | 返回添加到控件中的标签数量。 |
GetSSLPageTitle | 返回特定标签页的标签。 |
SetSSLPageTitle | 设置特定标签页的标签。 |
GetSSLPageID | 返回指定标签页的页面ID。 |
SetSSLPageID | 设置指定标签页的页面ID。 |
ActivateSSLPage | 使指定的标签页成为活动的。 |
GetSSLActivePage | 获取当前活动的标签页。 |
GetSSLPageIndex | 返回指定标签页的页面索引。 |
可重写函数
OnInitPage | 在派生类中重写以初始化页面。 |
OnActivatePage | 在派生类中重写以响应页面激活。 |
OnDeactivatePage | 在派生类中重写以响应页面停用。 |
OnDestroyPage | 在派生类中重写以释放资源。 |
CTabCtrlSSL::CTabCtrlSSL
CTabCtrlSSL ();
备注
构造一个CTabCtrlSSL
对象。
CTabCtrlSSL::AddSSLPage
int AddSSLPage (LPCTSTR pszTitle, int nPageID, int nTemplateID);
int AddSSLPage (LPCTSTR pszTitle, int nPageID, LPCTSTR pszTemplateName);
int AddSSLPage (LPCTSTR pszTitle, int nPageID, CTabPageSSL* pTabPage);
返回值
成功时返回新标签的零基索引,否则返回–1。
参数
pszTitle
指向包含标签文本的以null结尾的字符串的地址。
nPageID
用于引用要添加的页面的零基数字ID。
nTemplateID
指定标签页的资源ID。
pszTemplateName
指向包含要加载的对话框资源名称的以null结尾的字符串。
pTabPage
指向要添加的CTabPageSSL
对象的指针。
备注
调用此函数以在现有标签控件中插入新标签。
参见 CTabCtrlSSL::RemoveSSLPage, CTabCtrlSSL::GetSSLPage
CTabCtrlSSL::RemoveSSLPage
BOOL RemoveSSLPage (int nIndex);
返回值
成功则为非零,否则为 0。
参数
nIndex
要删除的项目的零基值。
备注
调用此函数以从标签控件中移除指定的项目。
参见 CTabCtrlSSL::AddSSLPage, CTabCtrlSSL::GetSSLPage
CTabCtrlSSL::GetSSLPageCount
int GetSSLPageCount ();
返回值
标签控件中的项目数。
备注
调用此函数以检索标签控件中的标签数。
CTabCtrlSSL::GetSSLPageTitle
BOOL GetSSLPageTitle (int nIndex, CString& strTitle);
返回值
成功则为非零,否则为 0。
参数
nIndex
相关标签页的零基值。
strTitle
用于接收标签页标题的CString
引用。
备注
调用此函数以检索标签控件中指定项目的标题。
参见 CTabCtrlSSL::SetSSLPageTitle
CTabCtrlSSL::SetSSLPageTitle
BOOL SetSSLPageTitle (int nIndex, LPCTSTR pszTitle);
返回值
成功则为非零,否则为 0。
参数
nIndex
相关标签页的零基值。
pszTitle
指向包含要为指定标签页设置的标题的以null结尾的字符串。
备注
调用此函数以设置标签控件中指定项目的标题。
参见 CTabCtrlSSL::GetSSLPageTitle
CTabCtrlSSL::GetSSLPageID
int GetSSLPageID (int nIndex);
返回值
成功时返回零基页面标识符,否则返回-1。
参数
nIndex
相关标签页的零基索引。
备注
调用此函数以检索标签控件中指定项目的页面标识符(在添加标签时指定)。页面标识符也可以使用SetSSLPageID函数进行修改。
CTabCtrlSSL::SetSSLPageID
int SetSSLPageID (int nIndex, int nPageID);
返回值
成功时返回之前的零基页面标识符,否则返回-1。
参数
nIndex
相关标签页的零基索引。
nPageID
要设置的新零基页面标识符。
备注
调用此函数以更改指定标签页的页面标识符。
CTabCtrlSSL::ActivateSSLPage
BOOL ActivateSSLPage (int nIndex);
返回值
成功则为非零,否则为 0。
参数
nIndex
要激活的标签页的零基索引。
备注
调用此函数以更改活动标签。
参见 CTabCtrlSSL::GetSSLActivePage
CTabCtrlSSL::GetSSLActivePage
int GetSSLACtivePage ();
返回值
成功时返回选定标签的零基索引,如果没有选定标签则返回–1。
备注
调用此函数以检索标签控件中当前选定的标签。
参见 CTabCtrlSSL::ActivateSSLPage
CTabCtrlSSL::GetSSLPage
CWnd* GetSSLPage (int nIndex);
返回值
成功时返回请求的标签页的指针,否则返回NULL。
参数
nIndex
要获取的标签页的零基索引。
备注
调用此函数以获取标签控件中的特定标签。
CTabCtrlSSL::GetSSLPageIndex
int GetSSLPageIndex (int nPageID);
返回值
成功时返回指定标签的零基索引,否则返回-1。
参数
nPageID
相关标签页的零基页面标识符。
备注
调用此函数以检索标签控件中标签页的零基索引。
CTabCtrlSSL::OnInitPage
BOOL OnInitPage (int nIndex, int nPageID);
返回值
成功则为非零,否则为 0。
参数
nIndex
正在初始化的标签页的零基索引。
nPageID
正在初始化的页面的零基页面标识符。
备注
在派生类中重写以初始化页面。
CTabCtrlSSL::OnActivatePage
void OnActivatePage (int nIndex, int nPageID);
参数
nIndex
正在激活的标签页的零基索引。
nPageID
正在激活的页面的零基页面标识符。
备注
在派生类中重写以响应页面激活。
CTabCtrlSSL::OnDeactivatePage
void OnDeactivatePage (int nIndex, int nPageID);
参数
nIndex
正在激活的标签页的零基索引。
nPageID
正在激活的页面的零基页面标识符。
备注
在派生类中重写以响应页面停用。
CTabCtrlSSL::OnDestroyPage
void OnDestroyPage (int nIndex, int nPageID);
参数
nIndex
正在激活的标签页的零基索引。
nPageID
正在激活的页面的零基页面标识符。
备注
在派生类中重写以释放资源。
已知问题
据我所知,没有,但是高级标签部分还没有经过太多测试。如果你发现了任何问题,请告诉我。
更新
- 2003年10月1日
关于
CTabCtrlSSL
我收到的最常见的问题是关于某个x、y或z控件无法接收消息。我已经向许多人反复解释过这个问题和解决方案,现在,多亏了John A Johnson,我有一个真正有效的解决方案,可以应用于所有人的源代码。
问题在于
WM_COMMAND
、WM_NOTIFY
和WM_CMDMSG
被传递给了标签页的父窗口,即标签控件,然后标签控件又将它们传递给它的父窗口(通常是对话框),以便父对话框可以处理标签页的通知等。不幸的是,这意味着任何派生的标签页都不会有机会处理自己的控件通知,除非您在CTabPageSSL
中移除OnCommand
、OnNotify
和OnCmdMsg
处理程序。真正的解决方案是默认关闭此命令路由,因为大多数人希望直接在拥有它们的类中处理控件通知,但允许人们选择重新打开命令路由,如果他们希望父对话框来处理它们。这导致在
CTabPageSSL
中增加了一些方法。但是,如果您只想在派生的标签页中处理命令/通知,您根本不需要更改代码,只需将新的*TabPageSSL.h*和*TabPageSSL.cpp*文件插入即可。