MFC MDI 收集 "apply" 按钮例程的控制状态
MFC MDI Collecting control states for the "apply" button routine
在我的应用程序的其他一些线程中提到我的代码不正确,因为应用按钮存在。我现在明白了。据说收集控件然后点击应用发送数据。
我现在知道如何处理了。所以为了通识教育。我有 1 property sheet
和 5 property pages
。只是为了使用中的一般控件。在所有 5 个中,只有 radio
控件和 buttons
,没有 edit controls
(还)。
假设每个页面上有 2 radios buttons
和 1 button
.. 其中 page 1
是 radio1
、radio2
、button 1
和page 2
是 radio3
、radio4
、button2
...等等。
我知道当用户选择按钮或收音机之类的东西时,需要调用 IsModified(TRUE)
以使应用按钮从灰色变为活动状态。
扫描所有控件然后应用它们的代码是什么样的?我从来没有做过,而且我似乎找不到一个不是已经超级忙的例子来了解如何做。
任何人都有教程或代码片段或从我上面描述的控件构建入门,可以演示如何执行此操作?
更新:
好的,我添加了 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 的成员变量。我错过了什么?
编辑:选项的 运行ge 在 resource.h
中按顺序排列为 200-207,这是我知道的,我知道很多人不喜欢 运行ge 选项,因为它们可能会损坏...这是我的代码,所以我不担心 运行ge 被弄乱。
更新 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_tabs
在SettingsUserTabs.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 个时,该选项存储在注册表中并在 [=66] 期间读取=] 然后使用该样式。由于它是 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
中正在使用的 Apply:
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:
到目前为止一切正常,我 运行 遇到了一个障碍,试图应用在这里和这里学到的经验教训 link:
MFC MDI Substituting a class member dynamically
本主题中所做的工作是针对 OutputWnd 窗格,它运行得非常出色!我的 MDI 打开一个文件并使用 CTabView 并使用更新 6 中显示的 link 允许我在启动时更改选项卡。现在我让 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,不知道是不是索引的问题,因为可以同时打开多个文档?
下面是我前段时间写的一个应用程序的例子。这是一个简单的“设置”对话框。与你的不同,这个来自 CDialogEx
。但由于 CPropertyDialog
派生自 CDialog
,这些也适用于您的情况。
使用向导,我将成员变量添加到对话框 class,绑定到对话框控件。在向导的“类别”组合中选择“值”而不是“控件”。这些在 class 定义中声明。为了简单起见,我只展示了三个。有一个 CString、一个 int 和一个 BOOL 变量,分别绑定到一个编辑、一个组合框(下拉列表)和一个复选框控件。
class CSettingsDlg : public CDialogEx
{
.
.
public:
CString m_DBConn;
int m_DumpSQL;
BOOL m_bLineNums;
}
在实现中,向导修改了构造函数和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_CHANGE
、EN_UPDATE
、BN_CLICKED
等通知消息(在 属性 编辑器中添加事件)并调用 SetModified()
函数 - 这可以证明很乏味,但我看不到任何其他方法。
这样的对话框class可以在应用中使用,如下图:
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;
}
}
编辑:
我在上面的代码部分有一个用法示例。该过程是创建对话框的实例 class,设置成员变量的值(初始值,例如从注册表中读取),调用 DoModal()
,如果成功将变量存储(复制)到其他地方(如果不丢弃它们)。这应该在某些事件处理程序中完成,例如 Update2 中的 CMainFrame::OnSettingsTools()
。 CMainFrame::DoDataExchange()
覆盖没有意义。
您无法访问 m_style_tabs
变量,这很奇怪。它不是 CSettingsUserTabs
class 的非静态 public 成员吗?它应该在 SettingsUserTabs.h 中声明。如果您以 pgeUserTabs.m_style_tabs
访问它,它不会工作吗?在 CSettingsUserTabs
class 函数中,它可以简单地作为 m_style_tabs
访问。我还看到它在构造函数中被初始化为 FALSE
。它是 BOOL
而不是 int
吗? (顺便说一句,BOOL 在 Win32 中定义为 int,因此编译器不会抱怨)但是向导会为单选按钮生成一个 int 变量,还可以选择进行范围验证。
还有一点,你通常不需要在CSettingsUserTabs::OnTabRadioClicked()
中调用UpdateData(TRUE)
。请只留下 SetModified()
电话。 UpdateData(TRUE)
通常在 OnOK()
函数中调用。通常你不需要覆盖这些,因为默认实现就足够了。 CPropertyPage documentation btw 提到 OnApply
的默认实现调用 OnOK
.
编辑 2:
在OnApply()
中您应该首先检查验证是否成功。此外, OnCreate()
不是可以直接调用的“方法”。它应该被认为是一个“事件”。创建 window 时,框架会调用它。您应该改为调用 Create()
。在您的情况下,您可以销毁输出 Window 并重新创建它(使用新样式)。但是,我看到 CMFCTabCtrl
class 有一个 ModifyTabStyle()
函数,您可以尝试调用它(无需破坏 windows 并重新创建它)。因此,您的代码将变为:
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_wndOutput
和 m_wndTabs
成员受到保护。你必须让他们 public.
在我的应用程序的其他一些线程中提到我的代码不正确,因为应用按钮存在。我现在明白了。据说收集控件然后点击应用发送数据。
我现在知道如何处理了。所以为了通识教育。我有 1 property sheet
和 5 property pages
。只是为了使用中的一般控件。在所有 5 个中,只有 radio
控件和 buttons
,没有 edit controls
(还)。
假设每个页面上有 2 radios buttons
和 1 button
.. 其中 page 1
是 radio1
、radio2
、button 1
和page 2
是 radio3
、radio4
、button2
...等等。
我知道当用户选择按钮或收音机之类的东西时,需要调用 IsModified(TRUE)
以使应用按钮从灰色变为活动状态。
扫描所有控件然后应用它们的代码是什么样的?我从来没有做过,而且我似乎找不到一个不是已经超级忙的例子来了解如何做。
任何人都有教程或代码片段或从我上面描述的控件构建入门,可以演示如何执行此操作?
更新:
好的,我添加了 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 的成员变量。我错过了什么?
编辑:选项的 运行ge 在 resource.h
中按顺序排列为 200-207,这是我知道的,我知道很多人不喜欢 运行ge 选项,因为它们可能会损坏...这是我的代码,所以我不担心 运行ge 被弄乱。
更新 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_tabs
在SettingsUserTabs.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 个时,该选项存储在注册表中并在 [=66] 期间读取=] 然后使用该样式。由于它是 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
中正在使用的 Apply:
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:
到目前为止一切正常,我 运行 遇到了一个障碍,试图应用在这里和这里学到的经验教训 link:
MFC MDI Substituting a class member dynamically
本主题中所做的工作是针对 OutputWnd 窗格,它运行得非常出色!我的 MDI 打开一个文件并使用 CTabView 并使用更新 6 中显示的 link 允许我在启动时更改选项卡。现在我让 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,不知道是不是索引的问题,因为可以同时打开多个文档?
下面是我前段时间写的一个应用程序的例子。这是一个简单的“设置”对话框。与你的不同,这个来自 CDialogEx
。但由于 CPropertyDialog
派生自 CDialog
,这些也适用于您的情况。
使用向导,我将成员变量添加到对话框 class,绑定到对话框控件。在向导的“类别”组合中选择“值”而不是“控件”。这些在 class 定义中声明。为了简单起见,我只展示了三个。有一个 CString、一个 int 和一个 BOOL 变量,分别绑定到一个编辑、一个组合框(下拉列表)和一个复选框控件。
class CSettingsDlg : public CDialogEx
{
.
.
public:
CString m_DBConn;
int m_DumpSQL;
BOOL m_bLineNums;
}
在实现中,向导修改了构造函数和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_CHANGE
、EN_UPDATE
、BN_CLICKED
等通知消息(在 属性 编辑器中添加事件)并调用 SetModified()
函数 - 这可以证明很乏味,但我看不到任何其他方法。
这样的对话框class可以在应用中使用,如下图:
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;
}
}
编辑:
我在上面的代码部分有一个用法示例。该过程是创建对话框的实例 class,设置成员变量的值(初始值,例如从注册表中读取),调用 DoModal()
,如果成功将变量存储(复制)到其他地方(如果不丢弃它们)。这应该在某些事件处理程序中完成,例如 Update2 中的 CMainFrame::OnSettingsTools()
。 CMainFrame::DoDataExchange()
覆盖没有意义。
您无法访问 m_style_tabs
变量,这很奇怪。它不是 CSettingsUserTabs
class 的非静态 public 成员吗?它应该在 SettingsUserTabs.h 中声明。如果您以 pgeUserTabs.m_style_tabs
访问它,它不会工作吗?在 CSettingsUserTabs
class 函数中,它可以简单地作为 m_style_tabs
访问。我还看到它在构造函数中被初始化为 FALSE
。它是 BOOL
而不是 int
吗? (顺便说一句,BOOL 在 Win32 中定义为 int,因此编译器不会抱怨)但是向导会为单选按钮生成一个 int 变量,还可以选择进行范围验证。
还有一点,你通常不需要在CSettingsUserTabs::OnTabRadioClicked()
中调用UpdateData(TRUE)
。请只留下 SetModified()
电话。 UpdateData(TRUE)
通常在 OnOK()
函数中调用。通常你不需要覆盖这些,因为默认实现就足够了。 CPropertyPage documentation btw 提到 OnApply
的默认实现调用 OnOK
.
编辑 2:
在OnApply()
中您应该首先检查验证是否成功。此外, OnCreate()
不是可以直接调用的“方法”。它应该被认为是一个“事件”。创建 window 时,框架会调用它。您应该改为调用 Create()
。在您的情况下,您可以销毁输出 Window 并重新创建它(使用新样式)。但是,我看到 CMFCTabCtrl
class 有一个 ModifyTabStyle()
函数,您可以尝试调用它(无需破坏 windows 并重新创建它)。因此,您的代码将变为:
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_wndOutput
和 m_wndTabs
成员受到保护。你必须让他们 public.