MFC MDI 在输出 window 中切换选项卡而无需重新启动(在我的例子中)

MFC MDI Toggling a tab in the output window without having to restart (in my case)

所以我正在使用 MFC 向导创建的输出窗格,一切都按我想要的方式运行。我想弄清楚的是如何 hide/show 用户从菜单中选择的特定选项卡。我有两个选项卡,一个是状态,一个是调试...我有一个用户菜单选项来打开调试 on/off 我通过使用注册表的开关来跟踪是否绘制它的状态在应用程序启动。我希望能够在不重新启动的情况下打开和关闭选项卡。

int COutputWnd::OnCreate(LPCREATESTRUCT lpCreateStruct)
{
    if (CDockablePane::OnCreate(lpCreateStruct) == -1)
        return -1;

    CRect rectDummy;
    rectDummy.SetRectEmpty();

    // Create tabs window:
    if (!m_wndTabs.Create(CMFCTabCtrl::STYLE_FLAT, rectDummy, this, 1))
    {
        TRACE0("Failed to create output tab window\n");
        return -1;      // fail to create
    }

    // Create output panes:
    const DWORD dwStyle = LBS_NOINTEGRALHEIGHT | WS_CHILD | WS_VISIBLE | WS_HSCROLL | WS_VSCROLL;

    if (!m_wndOutputBuild.Create(dwStyle, rectDummy, &m_wndTabs, 2) ||
        !m_wndOutputDebug.Create(dwStyle, rectDummy, &m_wndTabs, 3))
    {
        TRACE0("Failed to create output windows\n");
        return -1;      // fail to create
    }   

    UpdateFonts();

    CString strTabName;
    BOOL bNameValid;

    // Attach list windows to tab:
    bNameValid = strTabName.LoadString(IDS_STATUS_TAB);
    ASSERT(bNameValid);
    m_wndTabs.AddTab(&m_wndOutputBuild, strTabName, (UINT)0);

    // Enable debug tab if user sets it in settings
    int EnableDebugTab = AfxGetApp()->GetProfileInt(_T("Settings"), _T("EnableDebugTab"), 0); //Get value from registry
    if (EnableDebugTab == TRUE)
    {
        bNameValid = strTabName.LoadString(IDS_DEBUG_TAB);
        ASSERT(bNameValid);
        m_wndTabs.AddTab(&m_wndOutputDebug, strTabName, (UINT)1);
    }

    return 0;
}

我试过:

m_wndOutputDebug.ShowWindow(FALSE);

但这没有用。

m_wndOutputDebug.EnableWindow(FALSE);

那也没用。

我知道选项卡可以打开和关闭,我只是不明白打开和隐藏调试选项卡的机制。

处理此任务的最佳方法是什么?


编辑

所以根据 IInspectable 的评论,我试图 show/hide 选项卡而不破坏选项卡数据,好的,不用担心....我对我想要实现的目标有错误的期望... . 我修改了调试选项卡以始终启动测试,如:

bNameValid = strTabName.LoadString(IDS_STATUS_TAB);
ASSERT(bNameValid);
m_wndTabs.AddTab(&m_wndOutputBuild, strTabName, (UINT)0);

bNameValid = strTabName.LoadString(IDS_DEBUG_TAB);
ASSERT(bNameValid);
m_wndTabs.AddTab(&m_wndOutputDebug, strTabName, (UINT)1);

添加:

m_wndTabs.RemoveTab(1);

这是有效的,因为它总是删除调试选项卡...太棒了!...但是...

知道有效...m_wndTabs.RemoveTab(1); 被注释掉并添加到下面的新函数中。

选项卡代码位于 COutputWnd,我通过消息映射将此新功能称为 application.cpp 中的菜单项:

ON_COMMAND(ID_SETTINGS_DEBUG_TAB, &COutputWnd::OnDebugTabShow)

OutputWnd.cpp中添加了这个函数:

void COutputWnd::OnDebugTabShow()
{
    m_wndTabs.RemoveTab(1); //exact code from on create that works there in test
}

我在 oncreate 中注释掉了 m_wndTabs.RemoveTab(1);(因为我知道它在那里作为测试工作)...当我调试并调用 OnDebugTabShow() 时...我得到一个

Exception thrown: read access violation. this->m_wndTabs. was nullptr.

我不清楚为什么指针是空的,因为它是构建和加载的。

只是需要帮助来解决这个问题。

您不必删除和添加标签。 Show/Hide 使用 ShowTab() 的 Tab 是合适的。 这里以 ShowTab 为例。因为Tab可以动态移动到任何地方,所以一定要搜索到想要的Tab。

void COutputWnd::OnOutputtabsDebug()
{
  // Toggling OutputTab
  CString strDebugLabelShould;
  strDebugLabelShould.LoadString(IDS_DEBUG_TAB);

  CString strLabelIs;
  int nMax = m_wndTabs.GetTabsNum();

  for (int nId = 0; nId < nMax; nId++)
  {
    m_wndTabs.GetTabLabel(nId, strLabelIs);
    if (strLabelIs == strDebugLabelShould)
    {
        // found
        CWnd* pWnd = m_wndTabs.GetTabWnd(nId);
        BOOL bShow = !pWnd->IsWindowEnabled();

        m_wndTabs.ShowTab(nId, bShow);   // Toggle
        pWnd->EnableWindow(bShow);  
        break;                          
    }
  }
}

命令转发路由到 COutputWnd
由于 MFC 命令路由不会为您执行此操作,因此必须在大型机(或视图)中添加处理程序并手动转发,例如这样

  ON_COMMAND(ID_OUTPUTTABS_DEBUG, &CMainFrame::OnOutputtabsDebug)

void CMainFrame::OnOutputtabsDebug()
{
    m_wndOutput.OnOutputtabsDebug();
}

回复:

The tab code lives in COutputWnd, I am calling this new function a menu item from my application.cpp via the message map:

ON_COMMAND(ID_SETTINGS_DEBUG_TAB, &COutputWnd::OnDebugTabShow)

你的 application.cpp 中有这个 ON_COMMAND 宏吗?那是错误的!

在那种情况下,您的应用程序的 this 指针将用于 COutputWnd::OnDebugTabShow()(转换为 COutputWnd),从而导致各种问题。

这应该在您视图的消息映射中。