CMyTabCtrl - MFC 的非常简单的动态选项卡控件






4.59/5 (27投票s)
2007 年 2 月 1 日
4分钟阅读

195600

14329
一个非常简单易用且可扩展的 CTabCtrl 类

引言
使用标签控件最令人恼火的事情之一是必须使用属性表或将子对话框嵌入标签控件。这两种方法都可以,但会导致程序代码比我认为的要多。因此,我决定创建一个非常易于使用的标签控件类,该类可以为您动态创建子控件,并为您清理内存。此类非常易于使用,只需很少的代码,并且应该为您节省大量时间!它开箱即用地支持您需要的任意数量的标签,并且还可以创建您需要的任意数量的控件!它允许您用一行代码将子控件插入标签!它允许轻松访问控件值,并能够非常轻松地修改控件。它还支持通过子控件进行键盘导航。
背景
该类是为我的一个项目“选项”对话框设计的。标签类负责所有对象内存清理,并为您动态创建和销毁所有子窗口对象。我在 Win2K 和 XP 上测试过此类,我不再使用 Win98,但我确定它在 98 上也能正常工作。它与 XP 主题兼容,非常感谢另一个 codeproject 文章中用于 XP 主题兼容性的想法;( https://codeproject.org.cn/wtl/ThemedDialog.asp )(我从我们需要的颜色像素创建了一个实心背景画笔,而不是使用位图的重新定位的 patternbrush
)
使用代码
使用此类很简单,只需添加...
#include "CMyTabClass.h"
到您的对话框的头文件中,如下所示...
#pragma once #include "afxcmn.h" #include "MyTabCtrl.h" //<-- Add here // COptionsDlg dialog class COptionsDlg : public CDialog {
接下来,在您的对话框中为您的 CTabCtrl
创建一个公共成员变量,并为您的 CTabCtrl
添加 WS_TABSTOP
样式,完成后,将声明的变量从“CTabCtrl”更改为“CMyTabCtrl”,如下所示...
public: //CTabCtrl m_cTab; //<-- comment this out CMyTabCtrl m_cTab; //<-- add this
您现在已准备好使用标签类!目前该类支持:GroupBoxes
、RadioBoxes
、Checkboxes
、Buttons
和 Statictext
。您可以非常轻松地添加对更多控件的支持,只需查看代码并稍作修改即可。
以下函数是您用于插入控件的函数,这里解释了它们的工作原理...
uID
是您正在创建的控件的 ID,当然必须是唯一的。sCaption
将是控件中的文本。iX
和iY
是您的 X 和 Y 偏移量,因此您可以在标签控件内移动控件。iTab
是您要插入控件的标签。iLen
将是控件的长度。bCheck
是创建后项目的选中状态(选中或未选中)
uLocation
是一个定位助手,它可以是一个或多个以下选项:P_TOP
、P_LEFT
、P_RIGHT
、P_BELOW
每次插入控件时,该类都会记住最后一个插入控件的位置,因此您可以使用 uLocation
将下一个控件定位在下方(P_BELOW
),并将其与前一个控件的左侧对齐(P_LEFT
)。您可以使用位或运算符包含多个...
uLocation = P_BELOW|P_LEFT;
P_BELOW
= 将要插入的控件的顶部与前一个插入控件的底部对齐P_TOP
= 将要插入的控件的顶部与前一个插入控件的顶部对齐P_LEFT
= 将要插入的控件的左侧与前一个插入控件的左侧对齐P_RIGHT
= 将要插入的控件的左侧与前一个插入控件的右侧对齐
BottomOf
、RightOf
、LeftOf
和 TopOf
函数也是用于定位的辅助函数。您可以使用它们来获取先前插入控件的位置,然后将该值放入您的 iX
或 iY
中以帮助定位新控件。
//control creation functions void CreateCheckBox(BOOL bCheck, LPCTSTR sCaption, int nID, int iTab, UINT uLocation = 0, int iX = 0, int iY = 0); void CreateGroupBox(LPCTSTR sCaption, int nID, int iTab, int width, int height, UINT uLocation = 0, int iX = 0, int iY = 0); void CreateRadioBox(BOOL bCheck, LPCTSTR sCaption, int nID, int iTab, UINT uLocation = 0, int iX = 0, int iY = 0, DWORD dwStyle = 0); void CreateButton(LPCTSTR sCaption, int nID, int iTab, UINT uLocation = 0, int iX = 0, int iY = 0, int iLen = 50); void CreateEdit(LPCTSTR sCaption, int nID, int iTab, UINT uLocation = 0, int iX = 0, int iY = 0, int iLen = 100); void CreateStatic(LPCTSTR sCaption, int nID, int iTab, UINT uLocation = 0, int iX = 0, int iY = 0); //placment helpers int BottomOf(int nID); int RightOf(int nID); int LeftOf(int nID); int TopOf(int nID); //get value helpers CString GetItemText(int nID); int GetItemTextLength(int nID); int GetItemTextAsInt(int nID); BOOL GetItemCheckState(int nID);
下面是一个代码使用示例,仅用 12 行代码在 2 个标签中创建了 9 个控件。
BOOL COptionsDlg::OnInitDialog() { CDialog::OnInitDialog(); //Must call this first to initialize the tab class m_cTab.Init(); m_cTab.InsertItem(0,"Connections"); m_cTab.InsertItem(1,"Notifications"); //CONNECTIONS m_cTab.CreateGroupBox("Startup",1,0,310,52); m_cTab.CreateCheckBox(FALSE,"Start Program with Windows", 2, 0, 0, 10, 15); m_cTab.CreateCheckBox(FALSE,"Connect to All Contacts on Startup", 3, 0, P_BELOW | P_LEFT); m_cTab.CreateGroupBox("New Contact Acceptance", 4, 0, 310, 52, P_BELOW, 0, 10); m_cTab.CreateRadioBox(FALSE,"Auto Accept All New Contacts", 5, 0, P_LEFT | P_TOP, 10, 15, WS_GROUP); m_cTab.CreateRadioBox(TRUE,"New Contacts Must Match Connection Password", 6, 0, P_BELOW | P_LEFT, 0, 0, 0); //NOTIFICATIONS m_cTab.CreateGroupBox("Sounds",8,1,310,52); m_cTab.CreateCheckBox(FALSE,"Play Sound When Contact Comes Online", 9, 1, 0, 10, 15); m_cTab.CreateCheckBox(FALSE,"Play Sound On New Messages", 10, 1, P_BELOW | P_LEFT); //Just a simple test to demonstrate how easy it is to //access the controls CString s; //get the text from control number 8 m_cTab.GetDlgItem(8)->GetWindowText(s); TRACE("TEST: %s\n",s); return TRUE; }
您可以看到,将控件插入标签控件看起来非常漂亮整洁,而且很容易。但是,您可能会问,如何让按钮调用函数呢?该类会将一条消息发送到您的对话框,其中 WPARAM
包含被按下按钮的 uID,因此您可以用一个函数和简单的 switch 或 if/then 语句处理任何按钮事件。
BEGIN_MESSAGE_MAP(COptionsDlg, CDialog)
ON_MESSAGE(WM_BUTTONPRESSED,ButtonPressed)
END_MESSAGE_MAP()
LRESULT COptionsDlg::ButtonPressed(WPARAM w, LPARAM l)
{
CString s;
m_cTab.GetDlgItem((int)w)->GetWindowText(s);
TRACE("BUTTON %d was pressed [%s]\n",w,s);
return 0;
}
关注点
按钮和键盘导航是此类中的棘手部分,检查代码将有助于阐明我如何解决这些问题。使用 PreTranslateMessage
,我捕获了一些特定的键盘和按钮事件消息,然后将它们作为一种新型消息重新发布到类中进行处理。我这样做是为了使 PreTranslateMessage
消息函数中出现的代码量最少,以防止消息泵变慢。
为了在没有鼠标的情况下操作子控件,您可以使用 Tab 键在对话框中的控件之间移动(确保您的标签控件具有 WS_TABSTOP
样式)。然后,一旦您 Tab 到 TabControl
,请使用键盘上的向上和向下箭头键选择子控件,并使用空格键激活或取消激活它们。
历史
2007 年 1 月,公开发布。