MFC MDI 收集“应用”按钮例程的控制状态

Posted

技术标签:

【中文标题】MFC MDI 收集“应用”按钮例程的控制状态【英文标题】:MFC MDI Collecting control states for the "apply" button routine 【发布时间】:2021-05-05 19:21:29 【问题描述】:

在我的应用程序的其他一些线程中提到我的代码不正确,因为存在应用按钮。我现在明白了。据说是收集控件,然后在点击应用时发送数据。

我现在知道如何解决这个问题。所以为了通识教育。我有 1 个property sheet 和 5 个property pages。为了使用中的一般控件。在所有 5 个之间,只有 radio 控件和 buttons,没有 edit controls(还没有)。

假设每个页面上都有2 radios buttons1 button.. 其中page 1radio1radio2button 1page 2radio3radio4、@9876 @....等等。

我知道,当用户选择按钮或单选按钮时,需要调用 IsModified(TRUE) 以使应用按钮从灰色变为活动状态。

扫描所有控件然后应用它们的代码是什么样的?我从来没有做过,我似乎找不到一个不是已经超级忙的例子来了解如何做。

任何人都有教程或代码 sn-p 或从我上面描述的控件构建入门,可以演示如何执行此操作?

更新:

好的,我添加了 DDX 变量:

void CSettingsUserTabs::DoDataExchange(CDataExchange* pDX)

    CMFCPropertyPage::DoDataExchange(pDX);
    DDX_Control(pDX, STYLE_3D_USER, m_style_3d);
    DDX_Control(pDX, STYLE_FLAT_USER, m_style_flat);
    DDX_Control(pDX, STYLE_FLAT_SHARED_HORZ_SCROLL_USER, m_style_flat_shared_h_scroll);
    DDX_Control(pDX, STYLE_3D_SCROLLED_USER, m_style_3d_scroll);
    DDX_Control(pDX, STYLE_3D_ONENOTE_USER, m_style_onenote);
    DDX_Control(pDX, STYLE_3D_VS2005_USER, m_style_vs2005);
    DDX_Control(pDX, STYLE_3D_ROUNDED_USER, m_style_3d_rounded);
    DDX_Control(pDX, STYLE_3D_ROUNDED_SCROLL_USER, m_style_3d_rounded_scroll);

我的电台选择如下:

void CSettingsUserTabs::OnBnClicked3dUser()

    //AfxGetMainWnd()->SendMessage(WM_COMMAND, STYLE_3D_USER);
    UpdateData(TRUE);



void CSettingsUserTabs::OnBnClickedFlatUser()

    // TODO: Add your control notification handler code here
    //AfxGetMainWnd()->SendMessage(WM_COMMAND, STYLE_FLAT_USER);
    UpdateData(TRUE);


..... and the rest of them....

所以对我来说,当我单击任何radio button 时,我希望它会扫描该属性页上的所有控件?如果是这样,所有变量都有值..我是否调用IsModified(); 来启用apply 按钮...然后执行所有单选值,即只选择一个?是流量吗?

更新 2:

这就是我在MainFrame.cpp 中的模态对话框代码:

void CMainFrame::OnSettingsTools()

    SettingsSheet SettingsSheet(L"Application Settings");

    CSettingsPowerUser      pgePowerUser;   
    CSettingsToolbars       pgeToolbars;
    CSettingsTheme          pgeTheme;
    CSettingsUserTabs       pgeUserTabs;
    CSettingsReset          pgeReset;    
    
    SettingsSheet.AddPage(&pgeToolbars);
    SettingsSheet.AddPage(&pgeTheme);
    SettingsSheet.AddPage(&pgeUserTabs);
    SettingsSheet.AddPage(&pgePowerUser);
    SettingsSheet.AddPage(&pgeReset);

    INT_PTR nRet = -1;
    nRet = SettingsSheet.DoModal();

    // Handle the return value from DoModal
    switch (nRet)
    
    case -1:
        AfxMessageBox(_T("Dialog box could not be created!"));
        break;
    case IDABORT:
        // Do something
        AfxMessageBox(_T("ABORT!"));
        break;
    case IDOK:
        // Do something
        OnUserTabStyles(1);
        AfxMessageBox(_T("OK!"));
        break;
    case IDCANCEL:
        // Do something
        AfxMessageBox(_T("CANCEL"));
        break;
    default:
        // Do something
        break;
    ;    

验证SettingsTabs.cpp 中是否有任何单选按钮更改的例程:

void CSettingsUserTabs::OnTabRadioClicked(UINT nCmdID)

    BOOL IsChecked = nCmdID;
    CheckRadioButton(STYLE_3D_USER, STYLE_3D_ROUNDED_SCROLL_USER, nCmdID);
    UpdateData(TRUE);
    m_tabCmdID = nCmdID;
    SetModified();

SettingsUserTabs.cpp 中的成员变量是什么样的:

void CSettingsUserTabs::DoDataExchange(CDataExchange* pDX)
    
    CMFCPropertyPage::DoDataExchange(pDX);
    DDX_Radio(pDX, STYLE_3D_USER, m_style_3d);
    DDX_Radio(pDX, STYLE_FLAT_USER, m_style_flat);
    DDX_Radio(pDX, STYLE_FLAT_SHARED_HORZ_SCROLL_USER, m_style_flat_h_scroll);
    DDX_Radio(pDX, STYLE_3D_SCROLLED_USER, m_style_3d_scroll);
    DDX_Radio(pDX, STYLE_3D_ONENOTE_USER, m_style_3d_onenote);
    DDX_Radio(pDX, STYLE_3D_VS2005_USER, m_style_vs2005);
    DDX_Radio(pDX, STYLE_3D_ROUNDED_USER, m_style_3d_rounded);
    DDX_Radio(pDX, STYLE_3D_ROUNDED_SCROLL_USER, m_style_3d_rounded_scroll);
    

SettingsUserTabs.cpp 中构造函数的样子:

CSettingsUserTabs::CSettingsUserTabs()
    : CMFCPropertyPage(IDD_SETTINGS_TABS)
    , m_style_3d(FALSE)
    , m_style_flat(FALSE)
    , m_style_flat_h_scroll(FALSE)
    , m_style_3d_scroll(FALSE)
    , m_style_3d_onenote(FALSE)
    , m_style_vs2005(FALSE)
    , m_style_3d_rounded(FALSE)
    , m_style_3d_rounded_scroll(FALSE)
    , m_tabCmdID(FALSE)


我现在看到的这个问题是当我尝试使用成员变量m_tabCmdID 时,它又回到了unknown identifier,所以我不确定为什么看不到成员变量。我期待像OnUserTabStyles(m_tabCmdID); 一样使用它,以便它将所选按钮的参数传递给方法OnUserTabStyles。现在我只是在其中转储了一个1 以查看该机制是否有效。我只是不清楚如何从IDOK 的 SettingsUserTabs.cpp 访问成员变量。我错过了什么?

编辑:resource.h 中的选项范围为 200-207,这是我所知道的,我知道很多人不喜欢范围选项,因为它们可能会损坏......这是我的代码,所以我不用担心范围被弄乱了。

更新 3:

好的,所以我终于明白了康斯坦丁在以下帮助下描述的机制:

https://helgeklein.com/blog/2009/10/radio-buttons-in-mfc-visual-studio-2008-c/

我没有设置正确的 Tab 键顺序,也没有将组的第一个控件设置为 true。

有了这个,我现在在调试时得到按钮组中提到的值 0-7,因为我根据每个单选按钮在组中的位置从 0-7 (即 8 个按钮)单击它。这是代码现在的样子。

SettingsUserTabs.cpp:

CSettingsUserTabs::CSettingsUserTabs()
    : CMFCPropertyPage(IDD_SETTINGS_TABS)
    , m_style_tabs(FALSE)



void CSettingsUserTabs::DoDataExchange(CDataExchange* pDX)

    CMFCPropertyPage::DoDataExchange(pDX);
    DDX_Radio(pDX, STYLE_3D_USER, m_style_tabs);


void CSettingsUserTabs::OnTabRadioClicked(UINT nCmdID)

    UpdateData(TRUE);
    BOOL RadioValueSelected = m_style_tabs; // only here to see 0-7 value for debugging only, not needed, test only
    SetModified();

上面的mainframe.cpp(更新2):void CMainFrame::OnSettingsTools()还是一样。

所以现在我的问题还不清楚,我从mainframe.cpp 调用domodal,成员变量m_style_tabsSettingsUserTabs.cpp 中。当我尝试访问成员变量时,当我尝试执行类似after domodal int temp = m_styles_tabs; 的操作时,它会显示未知标识符。我在mainframe.cpp中有一个这个

void CMainFrame::DoDataExchange(CDataExchange* pDX)

    // TODO: Add your specialized code here and/or call the base class

    CMDIFrameWndEx::DoDataExchange(pDX);
 

我希望该成员会出现在 mainframe.cpp 中,所以我可以在 domodal 之后处理它,这就是我认为的全部意义所在?

如何访问成员变量,以便对它执行OnApply?我想我差不多明白了,我只是不清楚如何执行实际应用本身的最后几个步骤。

更新 4:

我询问重启的原因是当用户选择tabs 属性页并选择8 个选项中的1 个时,该选项存储在注册表中并在Oncreate 期间读取,然后使用该样式。因为它是OnCreate,所以除了重新启动应用程序之外,我还没有找到“重绘”或使用新的tab 设置的方法。因此,通过执行 reg save > respawn > end old ...我希望对话框在同一个选项卡属性页上重新打开,以便用户可以看到如果选择了 apply 会发生什么变化...与。一些说“重新启动”的对话框..或其他。下面是用于演示它现在如何工作的代码。

OutputWnd.cpp

int COutputWnd::OnCreate(LPCREATESTRUCT lpCreateStruct)

    if (CDockablePane::OnCreate(lpCreateStruct) == -1)
        return -1;

    CRect rectDummy;
    rectDummy.SetRectEmpty();

    // Create User Define tab style:
    int UserTabStyle = AfxGetApp()->GetProfileInt(_T("Settings"), _T("UserTabStyle"), 0); //Get value from registry
    // If the key doesn't exist, UserTableStyle will be 0 or FALSE;

    if (UserTabStyle != FALSE && UserTabStyle <= 8)  // User selected tab style type

        int EnumUserTabStyle = UserTabStyle - 1; // Fix enum if key doesn't exist.

        if (!m_wndTabs.Create(static_cast<CMFCTabCtrl::Style>(EnumUserTabStyle), rectDummy, this, 1))
        
            TRACE0("Failed to create output tab window\n");
            return -1;      // fail to create
        
    
    else  // Default tabs style if Reg key does not exist i.e. new install/program reset
        
        if (!m_wndTabs.Create(CMFCTabCtrl::STYLE_FLAT, rectDummy, this, 1))
            
                TRACE0("Failed to create output tab window\n");
                return -1;      // fail to create
            
    

… rest of function....

更新 5:

这是来自SettingsUserTabs.cpp的正在使用中的应用:

BOOL CSettingsUserTabs::OnApply()

    // TODO: Add your specialized code here and/or call the base class

    AfxGetApp()->WriteProfileInt(_T("Settings"), _T("UserTabStyle"), m_style_tabs); // Save value to registry

    return CMFCPropertyPage::OnApply();

更新 6:

到目前为止一切正常,我遇到了一个障碍,试图应用这里和这个链接中的经验教训:

MFC MDI Substituting a class member dynamically

在本主题中所做的事情是用于出色工作的 OutputWnd 窗格!我的 MDI 打开一个文件并使用 CTabView 并使用更新 6 中显示的链接允许我在启动时更改选项卡。现在我已经让 OutputWnd 使用 OnApply 执行此操作,我正在尝试在加载文件时将其应用于文档视图。当我调用我在TrainView.cpp 中创建的新函数并从UserSettingsTabs.cpp 调用它时,我遇到了access violation。我以为是static_cast 操作,但即使我使用GetControlTabs() 做一个简单的bold 也会崩溃(在注释掉的代码中显示,正在启动,现在在OnApply 中测试理论)。很明显,我需要捕获 MDI 文档,但不确定如何完成。我认为这很简单:

GetTabControl().ModifyTabStyle(static_cast<CMFCTabCtrl::Style>(EnumUserTabStyle));

但是当它因 Cx000000005 访问冲突而崩溃时,我知道我的结果有问题。我无法修改CTabView 操作,所以我想看看我们是否可以修复我做错的事情,让OnApply 更改标签样式而不像我们刚刚修复的OutputWnd 中所做的那样重新启动。

就目前而言,OnApply 正在运行,现在经过修改以尝试集成 CTabView 功能:

SettingsUserTabs.cpp:

BOOL CSettingsUserTabs::OnApply()

    BOOL bResult = CMFCPropertyPage::OnApply();
    if (bResult)
    
        AfxGetApp()->WriteProfileInt(_T("Settings"), _T("UserTabStyle"), m_style_tabs); // Save value to registry
        ((CMainFrame*)AfxGetMainWnd())->m_wndOutput.m_wndTabs.ModifyTabStyle((CMFCTabCtrl::Style)m_style_tabs);     
        ((CMainFrame*)AfxGetMainWnd())->m_wndOutput.m_wndTabs.RecalcLayout();

        CTrainView* TrainTabs; // User Call from anywhere method
        TrainTabs->TrainDocUpdateTabsControl();
    
    return bResult;

我添加了函数CTrainView::TrainDocUpdateTabsControl() 来更新标签...其余代码完全可以操作,即void CTrainView::OnInitialUpdate()

TrainView.cpp

IMPLEMENT_DYNCREATE(CTrainView, CTabView)

void CTrainView::OnInitialUpdate()

    CMainFrame* pMainFrame = (CMainFrame*)AfxGetMainWnd();
    pMainFrame->m_wndOutput.AddStringDebugTab(_T("Debug: TrainView--CTrainView::OnInitialUpdate()."));

    // add views // cmb         
    AddView(RUNTIME_CLASS(CInformationView), AfxStringID(IDS_INFORMATION));
    AddView(RUNTIME_CLASS(CChaptersView), AfxStringID(IDS_CHAPTERS));

    // Nicely hack to access protected member
    class CMFCTabCtrlEx : public CMFCTabCtrl
    
    public:
        void SetDisableScroll()  m_bScroll = FALSE; 
    ;

    // One-Liner to Disable navigation control
    ((CMFCTabCtrlEx*)&GetTabControl())->SetDisableScroll();

    GetTabControl().EnableTabSwap(TRUE);
    GetTabControl().SetLocation(CMFCBaseTabCtrl::Location::LOCATION_BOTTOM);    
    //GetTabControl().SetActiveTabBoldFont(TRUE);
    GetTabControl().EnableAutoColor(TRUE);

    // Modify User Define tab style:
    int UserTabStyle = AfxGetApp()->GetProfileInt(_T("Settings"), _T("UserTabStyle"), 0); //Get value from registry
    
    // If the key doesn't exist, UserTableStyle will be 0 or FALSE;
    if (UserTabStyle != FALSE && UserTabStyle <= 8)  // User selected tab style type

        int EnumUserTabStyle = UserTabStyle - 1; // Fix enum if key doesn't exist.
        GetTabControl().ModifyTabStyle(static_cast<CMFCTabCtrl::Style>(EnumUserTabStyle));

    
    else  // Default tabs style if Reg key does not exist i.e. new install/program reset

        GetTabControl().ModifyTabStyle(CMFCTabCtrl::STYLE_FLAT);
    

    CTabView::OnInitialUpdate();    


void CTrainView::TrainDocUpdateTabsControl()
   
    CTabView::AssertValid();
    GetTabControl().SetActiveTabBoldFont(TRUE); << CAUSES Cx000000005 ACCESS ERROR CRASH WHEN CALLED.

    //int EnumUserTabStyle;
    //int UserTabStyle = AfxGetApp()->GetProfileInt(_T("Settings"), _T("UserTabStyle"), 0); //Get value from registry
    //((CMainFrame*)AfxGetMainWnd())->GetTabControl().ModifyTabStyle(static_cast<CMFCTabCtrl::Style>(EnumUserTabStyle)); 

更新 6 编辑:

tabview 是从 Application.cpp 创建的,如下所示:

//Load Train Template
m_pkDocTrainTemplate = new CMultiDocTemplate(
    IDR_TRAIN, // Loads TRAIN operation
    RUNTIME_CLASS(CTrainDoc),
    RUNTIME_CLASS(CChildFrame),
    RUNTIME_CLASS(CTrainView));
AddDocTemplate(m_pkDocTrainTemplate);

我试图投射m_pkDocTrainTemplate,因为我认为那是指针?既然是MDI,不知道是不是索引问题,因为可以同时打开多个文档?

【问题讨论】:

我建议您使用DDX/DDV 功能来读取控件的值。您可以使用向导为每个控件添加适当类型的类变量(例如,用于编辑框的字符串、用于单选按钮的整数、用于复选框的布尔值等)并调用UpdateData(TRUE) 将控件内容读入变量中。这里不需要使用 Win32 函数(尽管你实际上可以)。该向导将添加 DDX/DDV 例程调用。 OnChange() (EN_CHANGE) 等事件可用于启用“应用”按钮。 我试试看....谢谢指点。 我已经更新了第一个帖子,它应该是这样的吗? 不,我不是那个意思,抱歉。您应该将对话框控件绑定到“Value”而不是“Control”变量(在“Category”组合中),因此这将生成 CString、int、BOOL 等变量而不是控件类型的变量,以及像 DDX_Check()、@ 这样的函数987654409@ 或DDV_MaxChars()DDV_ 函数执行验证,例如 ints 的 min/max、stings 的最大长度等。UpdateData(TRUE) 通常在 OnOK() 事件中调用,并验证和复制所有控件的内容。在OnClicked()OnChange() 事件中,您应该只启用“应用”按钮。 不,这不行,您有一组 8 个单选按钮、8 个布尔变量和 8 个 DDX_Radio() 调用。相反,您应该有一个 int 变量,绑定到组中的第一个按钮,以及一个 DDX_Radio() 调用。该变量将获得值 0..7(-1 表示没有选择)。 【参考方案1】:

以下是我前段时间编写的应用程序示例。这是一个简单的“设置”对话框。与您的不同,这个来自CDialogEx。但由于CPropertyDialog 是从CDialog 派生的,因此这些也适用于您的情况。

使用向导,我将成员变量添加到对话框类,绑定到对话框控件。在向导的“类别”组合中选择“值”,而不是“控制”。这些在类定义中声明。为简单起见,我只展示了三个。有一个 CString、一个 int 和一个 BOOL 变量,分别绑定到一个编辑、一个组合框(下拉列表)和一个复选框控件。

class CSettingsDlg : public CDialogEx

    .
    .
public:
    CString m_DBConn;
    int m_DumpSQL;
    BOOL m_bLineNums;

在实现中,Wizard 修改了构造函数和DoDataExchange() 成员函数:

CSettingsDlg::CSettingsDlg(CWnd* pParent /*=NULL*/)
    : CDialogEx(CSettingsDlg::IDD, pParent)
    , m_DBConn(_T(""))
    , m_DumpSQL(0)
    , m_bLineNums(FALSE)



void CSettingsDlg::DoDataExchange(CDataExchange* pDX)

    CDialogEx::DoDataExchange(pDX);
    DDX_Text(pDX, IDC_EDIT_DBCONN, m_DBConn);
    DDV_MaxChars(pDX, m_DBConn, 255);
    DDX_CBIndex(pDX, IDC_COMBO_DUMPSQL, m_DumpSQL);
    DDV_MinMaxInt(pDX, m_DumpSQL, 0, 2);
    DDX_Check(pDX, IDC_CHECK_LINENUMS, m_bLineNums);

构造函数中的值是初始(默认)值。 DoDataExchange() 函数调用DDX/DDV 例程。 DDX 例程执行数据传输(控件变量),而 DDV 则执行验证 - 它们是可选的。 DoDataExchange() 函数由UpdateData() 调用。此外,OnOK() 的默认实现调用UpdateData(TRUE),如果成功则关闭对话框。

如果某些内容已被修改,您将需要启用“应用”按钮。您可以捕获通知消息,如EN_CHANGEEN_UPDATEBN_CLICKED 等(在属性编辑器中添加一个事件)并调用SetModified() 函数 - 这可能很乏味,但我看不到任何其他方式去做吧。

这样的对话框类可以在应用程序中使用,如下所示:

void CChildView::OnSetoptions()

    // Create a Settings-dialog class instance
    CSettingsDlg sd;  // Main application window as parent - will block every UI item in the application

    // Set initial values for the member variables
    sd.m_DBConn = szDBconn;
    sd.m_DumpSQL = nDumpSQL;
    sd.m_bLineNums = bDumpLineNums;

    if (sd.DoModal() == IDOK)
    
        // Store the values entered by the user
        lstrcpyn(szDBconn, sd.m_DBConn, MAX_PATH);
        nDumpSQL = sd.m_DumpSQL;
        bDumpLineNums = sd.m_bLineNums;
    



编辑:

我在上面的代码部分中有一个使用示例。该过程是创建对话框类的实例,设置成员变量的值(初始值,例如从注册表中读取),调用DoModal(),如果成功将变量存储(复制)到其他地方(如果不丢弃它们)。这应该在某些事件处理程序中完成,例如 Update2 中的CMainFrame::OnSettingsTools()CMainFrame::DoDataExchange() 覆盖没有意义。

您无法访问m_style_tabs 变量,这很奇怪。它不是CSettingsUserTabs 类的非静态公共成员吗?它应该在 SettingsUserTabs.h 中声明。如果您以pgeUserTabs.m_style_tabs 的身份访问它,它不会起作用吗?在CSettingsUserTabs 类函数中,它可以简单地作为m_style_tabs 访问。我还看到它在构造函数中被初始化为FALSE。是BOOL 而不是int? (顺便说一句,在 Win32 中 BOOL 被定义为 int,因此编译器不会抱怨)但是向导会为单选按钮生成一个 int 变量,还可以选择进行范围验证。

另外一点,您通常不需要在CSettingsUserTabs::OnTabRadioClicked() 中调用UpdateData(TRUE)。请只留下SetModified() 电话。 UpdateData(TRUE) 通常在 OnOK() 函数中调用。而且通常你不需要覆盖这些,因为默认实现就足够了。 CPropertyPage documentation btw 提到 OnApply 的默认实现调用 OnOK



编辑 2:

OnApply() 中,您应该首先检查验证是否成功。此外,OnCreate() 不是可以直接调用的“方法”。它应该被视为一个“事件”。它在创建窗口时由框架调用。你应该打电话给Create()。在您的情况下,您可以销毁输出窗口并重新创建它(使用新样式)。但是,我看到CMFCTabCtrl 类有一个ModifyTabStyle() 函数,您可以尝试调用它(无需破坏窗口并再次创建它)。因此,您的代码将变为:

BOOL CSettingsUserTabs::OnApply()

    BOOL bResult = CMFCPropertyPage::OnApply();
    if (bResult)
    
        AfxGetApp()->WriteProfileInt(_T("Settings"), _T("UserTabStyle"), m_style_tabs); // Save value to registry
        ((CMainFraime*)AfxGetMainWnd())->m_wndOutput.m_wndTabs.ModifyTabStyle((CMFCTabCtrl::Style)m_style_tabs);
    
    return bResult;

上面的代码不会编译,因为m_wndOutputm_wndTabs 成员是受保护的。您必须将它们公开。

【讨论】:

感谢详细的例子,好在你之前提到的我能够申请并拥有与你的.h 3 项非常接近的东西。我有一些类似于constructor variables(使用我的)的东西,而ddx data exchange..all 看起来很相似(我会在进一步深入时发布)。我有一个case 结构来捕获我的radio buttons。唯一的问题是,我没有IDOK()IDAPPLY(),我假设我创建了它.. 其他事情,UpdateData() 在哪里被调用,在IDOK/IDAPPLY 之前......因为它不是t 显示在您的 sn-p 上。 对于单选按钮,WinAPI 确实没有提供任何特殊功能,只是获取每个按钮的检查状态。但是单选按钮组是一个“多选”类型的 UI 项(就像下拉列表组合一样),因此最好使用 int“状态”。 MFC 使用DDX_Radio() 例程为您执行此操作。它是向导生成的。您可以将一个 int 成员变量绑定到第一个单选按钮;它必须具有组样式集(其他不是)。跳位顺序也必须正确。测试您的对话框资源(Format->Test Dialog 或 Ctrl+T)以确保它工作正常。 一些澄清,没有IDOK()函数。对话框通常有一个“确定”按钮,通常带有“默认”(BS_DEFPUSHBUTTON) 样式集(捕获 ENTER 键),控件 ID 为IDOK。单击此按钮将调用对话框的OnOK() 事件。默认(CDialog::OnOK())实现,正如我在帖子中描述的那样,调用UpdateData(TRUE),如果成功则关闭对话框。在我的情况下没关系,不需要覆盖。你可以在 MFC 源代码中找到这些东西,去 Edit->Find and Replace->Find in Files,你的文本,然后在“Look In”中选择 Visual C++ Source Directories 我在原帖中创建了更新 2,我认为我这样做的方式满足了您的关注。实际代码现在就在那里,因此您可以确切地看到我在做什么,我希望我正确地遵循了您的指导。 我得到了它的工作.. pgeUserTabs.m_style_tabs 是我的解决办法,我不清楚如何去做,这澄清了它。我有 applyok 函数工作。我将BOOL 转换为int,从main 中转储ddx 交换并调整UpdateData(FALSE) 以在对话框打开并选择选项卡时更新控件。我确实有最后一个问题,选项卡更改需要重新启动应用程序,我很容易,在发送菜单命令以从菜单打开设置对话框后,如何激活“选项卡”列表链接?想对您的所有帮助表示感谢!

以上是关于MFC MDI 收集“应用”按钮例程的控制状态的主要内容,如果未能解决你的问题,请参考以下文章

MFC MDI 无打开文档 = 禁用新文件

MDI 应用程序 MFC 中文档的多个视图

MFC/VC 在多文档MDI程序中 子窗口初始最大化显示 如何去掉菜单栏右侧的最小化及向下还原按钮

使用 MFC 功能区升级 MDI 应用程序

关闭 MFC MDI 应用程序中的所有子窗口

在 MFC MDI 应用程序中级联特定窗口