MFC笔记:初始化tab控件一点改进

背景:
MFC工具带有tab标签,作为导航。需要在对话框初始化时进行初始化。

一、流程简述

实现此功能需要做如下事情:

  • 创建主对话框、多个子对话框。
  • 子对话框需要设置属性:
    外观Style为Child,Boarder选None。
  • 关联tab标签控件变量(也可以直接用控件ID)。
  • 初始化,子对话框添加到tab标签上。
  • 响应点击函数,以便切换对话框。

下面先列出原始版本,分析问题,再解决问题。
本文省略MFC控件布局的说明。

二、原始版本

2.1 变量声明

namespace NSONVIF {
    enum PAGE_TYPE {
        PAGE_DISCOVER = 0,
        PAGE_DEVICE,
        PAGE_MEDIA,
        PAGE_IMAGING,
        PAGE_DEBUG,

        PAGE_MAX,
    };
}

    std::vector<CDialog *> m_pvPage;
    NSONVIF::PAGE_TYPE m_nCurTab;
    CDiscover m_cDlgDiscover;
    CDeviceService m_cDlgDevice;
    CMediaService m_cDlgMedia;
    CImagingService m_cDlgImaging;
    CDebugInfo m_cDlgDebug;

    CTabCtrl m_ctrTab;

使用m_pvPage存放子对话框指针。m_cDlg开头的变量为子对话框。m_ctrTab为Tab控件关联的类。

2.2 初始化

    m_ctrTab.InsertItem(0, "Discover");
    m_ctrTab.InsertItem(1, "Device");
    m_ctrTab.InsertItem(2, "Media");
    m_ctrTab.InsertItem(3, "Imaging");
    m_ctrTab.InsertItem(4, "Debug");

    m_pvPage.resize(NSONVIF::PAGE_MAX);

    m_cDlgDiscover.Create(IDD_DLG_DISCOVER, &m_ctrTab);
    m_pvPage[NSONVIF::PAGE_DISCOVER] = &m_cDlgDiscover;
    m_cDlgDiscover.SetOnvifProxy(&m_cProxy);

    m_cDlgDevice.Create(IDD_DLG_DEVICE, &m_ctrTab);
    m_pvPage[NSONVIF::PAGE_DEVICE] = &m_cDlgDevice;
    m_cDlgDevice.SetOnvifProxy(&m_cProxy);

    m_cDlgMedia.Create(IDD_DLG_MEDIA, &m_ctrTab);
    m_pvPage[NSONVIF::PAGE_MEDIA] = &m_cDlgMedia;
    m_cDlgMedia.SetOnvifProxy(&m_cProxy);

    m_cDlgImaging.Create(IDD_DLG_IMAGING, &m_ctrTab);
    m_pvPage[NSONVIF::PAGE_IMAGING] = &m_cDlgImaging;
    m_cDlgImaging.SetOnvifProxy(&m_cProxy);

    m_cDlgDebug.Create(IDD_DLG_DEBUG, &m_ctrTab);
    m_pvPage[NSONVIF::PAGE_DEBUG] = &m_cDlgDebug;
    m_cDlgDebug.SetOnvifProxy(&m_cProxy);

    CRect rc;
    m_ctrTab.GetClientRect(rc);
    rc.top += 22;
    rc.bottom -= 1;
    rc.left += 1;
    rc.right -= 1;

    for (unsigned int i = 0; i < m_pvPage.size(); i++)
    {
        m_pvPage[i]->MoveWindow(&rc);
    }

    // first page
    m_nCurTab = NSONVIF::PAGE_DISCOVER;
    m_pvPage[m_nCurTab]->ShowWindow(SW_SHOW);

2.3 响应点击事件

即添加OnTcnSelchangeTab事件并实现

void COnvifClientDlg::OnTcnSelchangeTab(NMHDR *pNMHDR, LRESULT *pResult)
{
    *pResult = 0;

    m_pvPage[m_nCurTab]->ShowWindow(SW_HIDE);
    m_nCurTab = static_cast<NSONVIF::PAGE_TYPE>(m_ctrTab.GetCurSel());
    if (m_pvPage[m_nCurTab])
        m_pvPage[m_nCurTab]->ShowWindow(SW_SHOW);
}

三、存在问题

原始版本的代码有点死板,不够灵动:

  • 多了控件顺序的宏定义。命名空间可删除。
  • 重复但又有差异的代码较多。如初始化代码。

四、改良版本

4.1 变量声明

    std::vector<CDialog *> m_pvPage;
    int m_nCurTab; // 直接用int即可
    CDiscover m_cDlgDiscover;
    CDeviceService m_cDlgDevice;
    CMediaService m_cDlgMedia;
    CImagingService m_cDlgImaging;
    CDebugInfo m_cDlgDebug;

    CTabCtrl m_ctrTab;

初始化

struct cDlgCtrl_t {
    LPCTSTR name;
    CDialogEx* dlg;
    int id;
};

    LPCTSTR lpName[10]; // make bigger...
    int i = 0;
    lpName[i++] = L"Discover";
    lpName[i++] = L"Device";
    lpName[i++] = L"Media";
    lpName[i++] = L"Imaging";
    lpName[i++] = L"Debug";
    
    i = 0;
    struct cDlgCtrl_t dlgCtrls[10]; // make bigger...
    dlgCtrls[i].name = lpName[i];
    dlgCtrls[i].dlg = &m_cDlgDiscover;
    dlgCtrls[i].id = IDD_DLG_DISCOVER;
    dlgCtrls[++i].name = lpName[i];
    dlgCtrls[i].dlg = &m_cDlgDevice;
    dlgCtrls[i].id = IDD_DLG_DEVICE;
    dlgCtrls[++i].name = lpName[i];
    dlgCtrls[i].dlg = &m_cDlgMedia;
    dlgCtrls[i].id = IDD_DLG_MEDIA;
    dlgCtrls[++i].name = lpName[i];
    dlgCtrls[i].dlg = &m_cDlgImaging;
    dlgCtrls[i].id = IDD_DLG_IMAGING;
    dlgCtrls[++i].name = lpName[i];
    dlgCtrls[i].dlg = &m_cDlgDebug;
    dlgCtrls[i].id = IDD_DLG_DEBUG;
    
    CRect rc;
    m_ctrTab.GetClientRect(rc);
    rc.top += 22;
    rc.bottom -= 1;
    rc.left += 1;
    rc.right -= 1;

    m_pvPage.resize(i+1);
    for (unsigned int i = 0; i < m_pvPage.size(); i++)
    {
        m_ctrTab.InsertItem(i, dlgCtrls[i].name);
        dlgCtrls[i].dlg->Create(dlgCtrls[i].id, &m_ctrTab);
        dlgCtrls[i].dlg->SetOnvifProxy(&m_cProxy);

        m_pvPage[i] = dlgCtrls[i].dlg;      
        m_pvPage[i]->MoveWindow(&rc);
    }

    m_nCurTab = 0;
    m_pvPage[m_nCurTab]->ShowWindow(SW_SHOW);

4.2 响应点击事件

void COnvifClientDlg::OnTcnSelchangeTab(NMHDR *pNMHDR, LRESULT *pResult)
{
    *pResult = 0;

    m_pvPage[m_nCurTab]->ShowWindow(SW_HIDE);
    m_nCurTab = m_ctrTab.GetCurSel();
    if (m_pvPage[m_nCurTab])
        m_pvPage[m_nCurTab]->ShowWindow(SW_SHOW);
}

相对而言,改良后的代码更好维护。

五 切换页面

在初始化时,遍历每个子对话框,并调用 Create 创建窗口,注意,此时会调用到子对话框的OnInitDialog函数(哪怕当时没有显示出来)。
当切换 Tab 时,子对话框并不会再次初始化。因此,需要在切换响应函数 OnTcnSelchangeTab 中显示进行。示例如下:

void COnvifClientDlg::OnTcnSelchangeTab(NMHDR *pNMHDR, LRESULT *pResult)
{
    *pResult = 0;

    m_pvPage[m_nCurTab]->ShowWindow(SW_HIDE);
    m_nCurTab = m_ctrTab.GetCurSel();
    if (m_pvPage[m_nCurTab])
    {
        m_pvPage[m_nCurTab]->ShowWindow(SW_SHOW);
        if (m_nCurTab == 0) // 当切换到第0个子对话框时
        {
            CDiscover* dlg = (CDiscover*)m_pvPage[m_nCurTab];
            dlg->Reinit(); // 重新初始化
        }
    }
}

注:是否可以直接调用子对话框的 OnInitDialog 函数,未测试。

发布了481 篇原创文章 · 获赞 244 · 访问量 110万+

猜你喜欢

转载自blog.csdn.net/subfate/article/details/103651134