我需要做一些我希望很简单的事情 - 创建一个包含2个标签的标签控件,这意味着我的应用程序有两种操作模式。当用户点击Tab1时,他会看到一些按钮和文本框,当他点击Tab2时,会有其他一些输入法。我注意到有一个CTABCtrl类在MFC中用来添加标签。 但是,一旦我使用UI设计器添加了tab ctrl,我就无法使用属性窗口指定有多少个选项卡。在网上搜索,我发现了一些例子,但是所有这些例子都要求你从CtabCtrl 派生,创建2个或更多子对话框等,并编写自己的自定义类。我的问题是,既然我想做一些基本的事情,为什么我不能使用熟悉的Add Event处理程序/ Add成员变量向导然后处理我应用程序类中的其他内容呢?当然,默认的CTabCtrl类可以做一些有用的事情,而不需要从中派生出来吗?
答案 0 :(得分:3)
忘掉CTabCtrl并使用更容易使用的CMFCTabCtrl(假设您正在使用VS2008 SP1)。
如果做不到这一点,你似乎误解了标签控件的工作原理。它仅在顶部提供“标签条”,并在用户点击另一个时发送消息。它没有为您提供“标签画布”,您可以在其上放置控件。显示和隐藏选项卡上的控件是程序员需要处理的事情。资源编辑器在那里提供的支持很少。就像斯图尔特所说,最常见的工作方式是在选项卡中设置子对话框,并隐藏除当前选项卡之外的所有对话框。
您不需要从CTabCtrl派生,您也可以在作为CTabCtrl的父窗口的窗口中实现切换行为。
答案 1 :(得分:1)
MFC选项卡控件是win32选项卡控件的一个非常薄的包装器,它的工作方式与您描述的方式非常相似。它是一个窗口,使用选项卡提供子窗口之间的切换。碰巧,直接win32这是它最有用的工作方式。如果您想做一些比在各个窗口之间切换更复杂的事情,可以使用子对话框来完成。 MFC并没有给你很多帮助,但是从CTabCtrl派生并使用子对话框真的不是很难做到,尽管如果你已经习惯了WinForms的标签控件方式,它似乎没必要。
如果你想在对话框的根目录下使用选项卡控件,而没有其他控件,你可能需要查看可能更易于使用的CPropertySheet(http://msdn.microsoft.com/en-us/library/d3fkt014(VS.80).aspx)。除非您想使用任何向导功能,否则您甚至不需要从中派生 - 您只需创建几个子对话框类,然后在您要创建属性表的位置,创建一个对象,添加页面并调用它。
答案 2 :(得分:1)
我使用包含CTabCtrl
的MFC对话框的方法是派生一个小类来管理选项卡控件,并使用对话框模板来创建实际的选项卡窗口内容。
这仍在进行中,因此源代码不是很干净,但这里有一些部分。例如,CTabCtrlDialog
需要构造函数和析构函数才能释放可能已创建的对象。
在资源文件中,我有一个对话框模板,其中有一个选项卡控件,后面是三个对话框模板,用于插入选项卡控件的每个不同选项卡内容窗口。虽然显示选项卡控件的对话框具有WS_POPUP
样式,但插入选项卡控件的选项卡窗口的对话框模板却改为WS_CHILD
样式。此更改使选项卡窗口成为子窗口,以便在移动对话框时,所有内容都保持正确排列,而不需要我做任何进一步的努力。
在我的情况下,插入选项卡控件的选项卡窗口显示一组复选框以指示各种操作参数。使用对话框模板方法可以非常轻松地创建必要的选项卡窗口内容。
我从CTabCtrl
派生了一个类,该类扩展了标准MFC类,并使用另一种方法根据指定的对话框模板ID插入选项卡控件的选项卡窗口。由于这只是一个对话框,我只是把这个类放在相同的文件中,.h和.cpp作为对话框组件本身。
class CTabCtrlDialog : public CTabCtrl
{
public:
void InsertItemDialogTemplate (UINT nIDTemplate, int nItem, TCITEM* pTabCtrlItem);
public:
struct {
UINT nIDTemplate;
CDialog *pDialog;
} m_pDialogData[10];
};
方法InsertItemDialogTemplate()
如下:
/*
* InsertItemDialogTemplate ()
*
* Insert into a tab control a tab pane based on the specified dialog template. The
* dialog template describes what the tab pane looks like so far as controls, etc.
*
* NOTE: The STYLE description must be WS_CHILD and not WS_POPUP. Also the dialog
* needs to have as its top coordinate some distance in pixels so that the
* various tab descriptions are visible. For instance an example dialog
* template in the resource file may look like:
* IDD_CASHIER_TAB_ONE DIALOGEX 0, 10, 178, 113
* STYLE DS_SETFONT | DS_FIXEDSYS | WS_CHILD
* FONT 8, "MS Shell Dlg", 400, 0, 0x1
* BEGIN
* LTEXT "Dialog Tab one",IDC_STATIC,6,44,90,17
* END
*
**/
void CTabCtrlDialog::InsertItemDialogTemplate (UINT nIDTemplate, int nItem, TCITEM* pTabCtrlItem)
{
InsertItem (nItem, pTabCtrlItem);
m_pDialogData[nItem].nIDTemplate = nIDTemplate;
m_pDialogData[nItem].pDialog = new CDialog ();
m_pDialogData[nItem].pDialog->Create (nIDTemplate, this);
m_pDialogData[nItem].pDialog->ShowWindow (FALSE);
}
为了处理显示各种选项卡的选项卡选择,我有以下消息映射,然后是对话框中的两个事件处理程序。
BEGIN_MESSAGE_MAP(CDiaCashierEdit, CDialog)
ON_NOTIFY(TCN_SELCHANGE, IDC_TAB_CASHIER_EDIT_STATUS, &CDiaCashierEdit::OnTcnSelchangeTabCashierEditStatus)
ON_NOTIFY(TCN_SELCHANGING, IDC_TAB_CASHIER_EDIT_STATUS, &CDiaCashierEdit::OnTcnSelchangingTabCashierEditStatus)
END_MESSAGE_MAP()
void CDiaCashierEdit::OnTcnSelchangeTabCashierEditStatus(NMHDR *pNMHDR, LRESULT *pResult)
{
// TODO: Add your control notification handler code here
*pResult = 0;
int i = TabCtrl_GetCurSel(pNMHDR->hwndFrom);
m_TabCtrl.m_pDialogData[i + 1].pDialog->ShowWindow (TRUE);
}
void CDiaCashierEdit::OnTcnSelchangingTabCashierEditStatus(NMHDR *pNMHDR, LRESULT *pResult)
{
// TODO: Add your control notification handler code here
*pResult = 0;
int i = TabCtrl_GetCurSel(pNMHDR->hwndFrom);
m_TabCtrl.m_pDialogData[i + 1].pDialog->ShowWindow (FALSE);
}
在对话框的DoDataExchange()
方法中,我首先创建了制表符控件,然后创建每个制表符窗口并将它们插入制表符控件。
void CDiaCashierEdit::DoDataExchange(CDataExchange* pDX)
{
CDialog::DoDataExchange(pDX);
DDX_Control(pDX, IDC_EDIT_CASHIER_NAME, m_CashierName);
DDX_Control(pDX, IDC_EDIT_CASHIER_SUPNO, m_SupervisorId);
DDX_Control(pDX, IDC_EDIT_CASHIER_TEAMNO, m_TeamNumber);
DDX_Control(pDX, IDC_EDIT_CASHIER_GCSTART, m_GuestCheckStart);
DDX_Control(pDX, IDC_EDIT_CASHIER_GCEND, m_GuestCheckEnd);
DDX_Control(pDX, IDC_TAB_CASHIER_EDIT_STATUS, m_TabCtrl);
if (pDX->m_bSaveAndValidate) {
m_CashierName.GetWindowText (m_paraCashier.auchCashierName, 20);
m_paraCashier.usSupervisorID = m_SupervisorId.GetWindowTextAsInt();
m_paraCashier.uchTeamNo = m_TeamNumber.GetWindowTextAsInt();
m_paraCashier.usGstCheckStartNo = m_GuestCheckStart.GetWindowTextAsInt();
m_paraCashier.usGstCheckEndNo = m_GuestCheckEnd.GetWindowTextAsInt();
for (int i = 0; i < sizeof(m_TabItemOneStatus)/sizeof(m_TabItemOneStatus[0]); i++) {
int iTab = m_TabItemOneStatus[i].sTabItem;
int iDlg = m_TabItemOneStatus[i].iDlgItem;
int iOffset = m_TabItemOneStatus[i].sOffset;
CButton *p = (CButton *) m_TabCtrl.m_pDialogData[iTab].pDialog->GetDlgItem(iDlg);
if (p->GetCheck()) {
m_paraCashier.fbCashierStatus[iOffset] |= m_TabItemOneStatus[i].uchBit;
} else {
m_paraCashier.fbCashierStatus[iOffset] &= ~(m_TabItemOneStatus[i].uchBit);
}
}
} else {
m_CashierName.SetWindowText(m_paraCashier.auchCashierName);
m_SupervisorId.SetWindowTextAsInt (m_paraCashier.usSupervisorID);
m_TeamNumber.SetWindowTextAsInt (m_paraCashier.uchTeamNo);
m_GuestCheckStart.SetWindowTextAsInt (m_paraCashier.usGstCheckStartNo);
m_GuestCheckEnd.SetWindowTextAsInt (m_paraCashier.usGstCheckEndNo);
m_TabCtrl.InsertItemDialogTemplate (IDD_CASHIER_TAB_ONE, 1, &m_TabItemOne);
m_TabCtrl.InsertItemDialogTemplate (IDD_CASHIER_TAB_TWO, 2, &m_TabItemTwo);
m_TabCtrl.InsertItemDialogTemplate (IDD_CASHIER_TAB_THREE, 3, &m_TabItemThree);
for (int i = 0; i < sizeof(m_TabItemOneStatus)/sizeof(m_TabItemOneStatus[0]); i++) {
int iTab = m_TabItemOneStatus[i].sTabItem;
int iDlg = m_TabItemOneStatus[i].iDlgItem;
int iOffset = m_TabItemOneStatus[i].sOffset;
CButton *p = (CButton *) m_TabCtrl.m_pDialogData[iTab].pDialog->GetDlgItem(iDlg);
if (m_paraCashier.fbCashierStatus[iOffset] & m_TabItemOneStatus[i].uchBit) {
p->SetCheck (1);
} else {
p->SetCheck (0);
}
}
m_TabCtrl.m_pDialogData[1].pDialog->ShowWindow (TRUE);
}
}