如何在基于MFC对话框的应用程序中使用CTabCtrl?

时间:2010-04-27 05:08:05

标签: user-interface mfc tabcontrol ctabctrl simpletabcontrol

我需要做一些我希望很简单的事情 - 创建一个包含2个标签的标签控件,这意味着我的应用程序有两种操作模式。当用户点击Tab1时,他会看到一些按钮和文本框,当他点击Tab2时,会有其他一些输入法。我注意到有一个CTABCtrl类在MFC中用来添加标签。 但是,一旦我使用UI设计器添加了tab ctrl,我就无法使用属性窗口指定有多少个选项卡。在网上搜索,我发现了一些例子,但是所有这些例子都要求你从CtabCtrl 派生,创建2个或更多对话框等,并编写自己的自定义类。我的问题是,既然我想做一些基本的事情,为什么我不能使用熟悉的Add Event处理程序/ Add成员变量向导然后处理我应用程序类中的其他内容呢?当然,默认的CTabCtrl类可以做一些有用的事情,而不需要从中派生出来吗?

3 个答案:

答案 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);
    }
}