【问题标题】:How to use a CTabCtrl in a MFC dialog based application?如何在基于 MFC 对话框的应用程序中使用 CTabCtrl?
【发布时间】:2011-02-12 17:09:28
【问题描述】:

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

【问题讨论】:

    标签: user-interface mfc tabcontrol ctabctrl


    【解决方案1】:

    MFC 选项卡控件是对 win32 选项卡控件的一个非常薄的包装,它的工作方式与您描述的差不多。它是一个窗口,它使用选项卡提供子窗口之间的切换。碰巧的是,在直接的 win32 中,这是最有用的工作方式。如果你想做一些比在单个窗口之间切换更复杂的事情,你可以使用子对话框来做到这一点。 MFC 对您帮助不大,但是从 CTabCtrl 派生并使用子对话框确实不是很难做到,尽管如果您习惯 WinForms 执行选项卡控件的方式,它似乎没有必要。

    如果您希望选项卡控件位于对话框的根部,并且旁边没有其他控件,您可能需要查看 CPropertySheet (http://msdn.microsoft.com/en-us/library/d3fkt014(VS.80).aspx),它可能更易于使用。除非您想使用您甚至不需要从它派生的任何向导功能 - 您只需创建几个子对话框类,然后在您要创建属性表的地方,创建一个对象,添加页面并调用它。

    【讨论】:

      【解决方案2】:

      忘记 CTabCtrl 并使用 CMFCTabCtrl 更容易使用(这是假设您正在使用 VS2008 SP1)。

      否则,您似乎误解了选项卡控件的工作原理。它仅在顶部提供“标签条”,并在用户单击另一个时发送消息。它没有为您提供可以放置控件的“选项卡画布”。显示和隐藏选项卡上的控件是程序员需要处理的事情。资源编辑器在那里提供的支持很少。正如 Stewart 所说,最常见的工作方式是在您的选项卡中添加子对话框,并隐藏除当前选项卡之外的所有子对话框。

      您不需要从 CTabCtrl 派生,您也可以在作为 CTabCtrl 的父窗口的窗口中实现切换行为。

      【讨论】:

        【解决方案3】:

        我对包含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);
            }
        }
        

        【讨论】:

          猜你喜欢
          • 2011-11-01
          • 1970-01-01
          • 2021-03-12
          • 1970-01-01
          • 2013-08-08
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          相关资源
          最近更新 更多