如何解决新手:在 Visual C++ MFC 应用程序中显示/隐藏 STATIC TEXT 对象的代码
我在我的第一个 VC++ MFC 应用程序中遇到了一个新手问题(实际上,我遇到了很多问题,但是 RTFM 和 DuckDuckGo 帮助解决了这些问题,而没有在这里哭求帮助。除了这个!) .请记住,我将此作为自己的教程,一种通过示例学习项目,并且我在 Deplhi/Lazarus 中有几年的 Win GUI 应用程序编程经验,现在我我正试图过渡到 VC++,只是为了我自己的好奇心。虽然我也擅长 C 语言 编程,但我对 C++ 的经验却少得多。因此,新的编程环境和鲜为人知的语言共同构成了我的障碍。
这是我所做的:
在最近安装的 Visual Studio 2019 社区 中,仅选择了 Windows App development in C++ 组件,启动了一个新项目,选择了 C++ MFC App (构建具有在 Windows 上运行的复杂用户界面的应用。)。将应用程序类型设置为基于对话框,关闭所有用户界面功能,以便仅选中厚框(未选中系统菜单,未选中关于框),关闭所有高级功能,以便只选中通用控件清单(取消选中打印和打印预览,取消选中 Activex 控件,取消选中支持重启管理器),点击完成。
这为我准备了一个应用程序,它有一个小主窗口,右下角有 OK 和 Cancel 按钮,中间有一个 STATIC TEXT 项目,内容类似于 “TODO: add your own items here” em>。项目名称是 TutMFC01p。
我的目标是当我单击其中一个按钮时隐藏该静态文本,并在我再次单击同一个按钮时使其再次可见。
我花了一些时间才意识到我不应该摆弄 OK 和 Cancel 按钮来为它们添加此功能,并且单击这两个按钮中的任何一个也会退出我的应用程序(因此,没有机会再次单击).所以我在对话框上放置了一个新按钮并使用它。在我的应用程序运行时单击我的按钮完全没有任何作用 - 这正是我想要的。
在对话框编辑器中双击我的按钮,我会进入源代码编辑器,其中在TutMFC01pDlg.cpp底部自动生成了一个新函数。 >
void CTutMFC01pDlg::OnBnClickedButton1()
{
// TODO: Add your control notification handler code here
}
好的,所以我将在此处添加按钮应该执行的操作的代码。
它还向 MESSAGE MAP 注入了 ON_BN_CLICKED 行,现在看起来像这样。
BEGIN_MESSAGE_MAP(CTutMFC01pDlg,CDialogEx)
ON_WM_PAINT()
ON_WM_QUERYDRAGICON()
ON_BN_CLICKED(IDC_BUTTON1,&CTutMFC01pDlg::OnBnClickedButton1)
END_MESSAGE_MAP()
又好了。所以这是告诉系统点击我的按钮应该运行 CTutMFC01pDlg::OnBnClickedButton1().
我首先尝试完成我的目标的方法是在单击我的按钮时在 VISIBILE 属性的 TRUE 和 FALSE 值之间交替 STATIC TEXT 对象。 Delphi/Lazarus 的做法是像 mainform.mystatictext.visible := not mainform.mystatictext.visible
这样的单行代码,但我无法找到一种方法来直接引用对象的属性并通过简单的赋值操作更改其值。我发现隐藏对象的方法是使用 ShowWindow()
方法。我也遇到了困难,试图将这个 (或任何其他) 方法指向 STATIC TEXT 对象,因为它显然有一个 IDC_STATIC 的 ID,显然,它不能被称为所有静态对象有这个相同的ID。为了简化前面的任务,我没有隐藏 STATIC TEXT,而是选择隐藏按钮本身,最后得到了以下代码:
void CTutMFC01pDlg::OnBnClickedButton1()
{
// TODO: Add your control notification handler code here
CWnd* pMyButtonObj = GetDlgItem(IDC_BUTTON1);
pMyButtonObj->ShowWindow(SW_HIDE); //or SW_SHOW
}
这可以编译并且运行良好。显然,一旦按钮被按下并从窗口中消失,就没有什么可以再次按下以取消隐藏隐藏的内容。所以我试图从这个已经工作的代码继续前进并修改它以对 STATIC TEXT 而不是按钮本身起作用。逻辑建议(我的逻辑,无论如何),为了获得引用 STATIC TEXT 的 ID 的能力,我需要为 STATIC TEXT 分配一个不同的 ID。我可以参考的东西。除了不可引用的 IDC_STATIC 之外的其他东西。因此,我在对话框编辑器上选择了 STATIC TEXT 对象,并在其属性面板中将 ID 属性的值从 IDC_STATIC 更改为 IDC_STATIC1。奇怪的是,这也将对象的 NAME 属性更改为 IDC_STATIC11。早些时候,NAME 是 IDC_STATIC1。然后在 OnBnClickedButton1() 的代码中,我用 IDC_STATIC1 替换了 IDC_BUTTON1,但是编译失败,抱怨没有这样的对象。尝试使用 IDC_STATIC11 时也会发生同样的情况。
一些实验揭示了另一种我无法解释(或理解)的现象。与我如何更改 STATIC TEXT 的 ID 类似,在属性编辑器中选择我的按钮后,我将其 ID 从 IDC_BUTTON1 更改为 IDC_HideBtn。这也改变了它的 NAME 属性。
全部保存,重建项目,然后单击我的按钮仍然使它消失,就像以前一样。 然而,OnBnClickedButton1() 和 MESSAGE MAP 的源代码没有更新为引用新 ID IDC_HideBtn,它们仍然引用 IDC_BUTTON1,与以前相同。
void CTutMFC01pDlg::OnBnClickedButton1()
{
//TODO: Add your control notification handler code here
CWnd* pMyButtonObj = GetDlgItem(IDC_BUTTON1);
pMyButtonObj->ShowWindow(SW_HIDE);
}
但此时IDC_BUTTON1应该是一个不存在的ID。编译应该失败。但它编译得很好,而且工作正常。
问题:
- 为什么代码编译并使用源代码中的 IDC_BUTTON1 而按钮的 ID 现在是 IDC_HideBtn?
- 我该怎么做才能像处理 IDC_BUTTON1 一样将 STATIC TEXT 项作为 GetDlgItem() 的参数?
- 如果不应该以编程方式更改 STATIC TEXT 项目,那么我可以使用什么其他类型的项目来代替?在 Delphi/Lazarus 中,有一个类似于 STATIC TEXT 的 LABEL 对象,但旨在在程序运行时多次获取不同的 Caption 或其他值。在对话框编辑器的工具箱中,我没有看到类似的东西,只有静态文本。还是应该改用输入字段来在对话窗口中显示文本?
- 有没有办法以我最初尝试执行 Delphi/Lazarus 的方式实现按钮单击方法?将目标对象从隐藏变为可见,从可见变为隐藏。最好是单线。
- 有没有办法直接引用对象的属性并通过赋值操作更改其值?或者只是我怎么没找到?
解决方法
我有一些小的更正(我认为),我想将它们作为评论发布,但根据comment policy,尽管已经给出了答案,但最好还是发布一个答案.
问题 2:
奇怪的是,VS 在重命名 resource.h
组件时没有在 CStatic
中为 IDC_STATIC1 添加新定义(毕竟 VS 为新按钮创建了新 id)。
当然,手动编辑 resource.h
在 MFC 编程过程中是一个非常频繁的过程,但是有必要更新 _APS_NEXT_CONTROL_VALUE
(很少有 _APS_NEXT_COMMAND_VALUE
)定义,以便它指向一个新的正确值(不等于以前的定义)。
问题 3:
但是您可以在 .rc
文件中写入如下内容:
BEGIN
DEFPUSHBUTTON "OK",IDOK,209,178,50,14
PUSHBUTTON "Cancel",IDCANCEL,263,14
CTEXT "TODO: Place dialog controls here.",IDC_MidTextObj,13,96,300,8
PUSHBUTTON "Hide object",IDC_HideBtn,135,106,14
END
然后在CTutMFC01pDlg.cpp中:
void CTutMFC01pDlg::OnBnClickedHidebtn()
{
if (CWnd * pMyStaticObj = GetDlgItem(IDC_MidTextObj))
pMyStaticObj->ShowWindow(!pMyStaticObj->IsWindowVisible());
}
问题 4:
您还可以使用 DoDataExchange
机制为 CStatic、CEdit 等组件获取/设置文本,并且可以使用 ON_UPDATE_COMMAND_UI
宏来启用/禁用组件。
但基本的方法是获取一个组件作为 CWnd 类:
CWnd * pMyStaticObj = GetDlgItem(IDC_MidTextObj)
或明确获取组件:
CStatic* pMyStaticObj = static_cast<CStatic*>(GetDlgItem(IDC_STATIC1));
(不要在这里使用 dynamic_cast
)
然后调用这个获取的实例的方法。
,感谢我的问题收到的评论,我可以改进我的网络搜索的方向,并检查我的项目文件夹中的其他文件,以确定问题的来源,并了解我的 MFC 应用程序的各个部分是如何组合在一起的。此外,有时 Visual Studio 2019 的内部工作需要一些手动编辑帮助。
答案 1)
我可以在其属性选项板中分配给对象的所有 IDC_xyzwq 标识符(值可从列表中选择)都是指向其各自数值的预定义宏。这些位于项目的 resource.h 文件中。不幸的是,VS2019 从未允许我将这个文件作为可读文本打开——它总是抱怨该文件已经打开并询问我是否要关闭它。为了研究内容,我实际上不得不关闭我的 VS2019 解决方案,并在文本编辑器中打开 resource.h 文件。这是我在那里找到的:
//{{NO_DEPENDENCIES}}
// Microsoft Visual C++ generated include file.
// Used by TutMFC01p.rc
//
#define IDD_TUTMFC01P_DIALOG 102
#define IDR_MAINFRAME 128
#define IDC_BUTTON1 1002
#define IDC_ButtHide 1002
#define IDC_HideBtn 1002
// Next default values for new objects
//
#ifdef APSTUDIO_INVOKED
#ifndef APSTUDIO_READONLY_SYMBOLS
#define _APS_NEXT_RESOURCE_VALUE 130
#define _APS_NEXT_COMMAND_VALUE 32771
#define _APS_NEXT_CONTROL_VALUE 1000
#define _APS_NEXT_SYMED_VALUE 101
#endif
#endif
所以似乎每次我为我的按钮发明一个新的 ID 并在属性面板中输入它时,VS2019 都会在资源文件中注入一个新的宏定义,将其分配给相同的数值 1002。但除了 auto - 创建这样的条目,Visual Studio 不对它们执行维护,程序员有责任保持那里的秩序。这需要程序员了解什么是什么,以及它在项目文件中的存储位置。
因此,即使我的按钮对象在其属性面板中已经具有 IDC_HideBtn 的 ID 值,IDC_ButtHide 和 IDC_BUTTON1 的早期 ID 仍然有效并引用相同的数值 1002,因此使用旧 ID 编译的源代码,以及按钮工作正常。
请注意,在 Visual Studio 重新打开我的解决方案/项目之前,我还必须手动将按钮 ID NAME 替换为 TutMFC01pDlg.cpp 中我的应用程序 MESSAGE MAP 中的所选按钮。另请参阅我的答案的下一部分。
答案 2)
IDC_STATIC 是一个经过特殊处理的特殊 ID,我不能只是在我的 STATIC TEXT 项的 ID 属性字段中发明和输入任何新名称。更准确地说,我实际上可以发明任何新 ID 并将其输入到属性字段中,但是 Visual Studio 不会在 resources.h 中自动生成相应的新宏定义,很可能是因为它不知道要分配给对象的数值是什么应该没有数值(因为它应该具有特殊值 -1)。因此,程序员不应在 ID 属性字段中输入新名称,而应关闭解决方案,并在文本编辑器中手动编辑 resources.h 文件。是的,针对 Microsoft 文档和经验丰富的开发人员中的所有警告和劝阻,在这种特殊情况下必须手动完成。 (或者至少,我不知道比直接将资源文件编辑为文本更好的方法。) 这是我将宏定义更改为的内容,通过删除两个过时和不需要的按钮标识符值为 1002,并手动添加用于我的 STATIC TEXT 项目的新定义条目 - 其数值未被任何其他条目使用。就我而言,1001 尚未使用,因此这就是我分配给我发明的 IDC_MidTextObj ID 的值。
//{{NO_DEPENDENCIES}}
// Microsoft Visual C++ generated include file.
// Used by TutMFC01p.rc
//
#define IDD_TUTMFC01P_DIALOG 102
#define IDR_MAINFRAME 128
#define IDC_HideBtn 1002
#define IDC_MidTextObj 1001
// Next default values for new objects
//
#ifdef APSTUDIO_INVOKED
#ifndef APSTUDIO_READONLY_SYMBOLS
#define _APS_NEXT_RESOURCE_VALUE 130
#define _APS_NEXT_COMMAND_VALUE 32771
#define _APS_NEXT_CONTROL_VALUE 1000
#define _APS_NEXT_SYMED_VALUE 101
#endif
#endif
将这些更改保存在 resource.h 中后,我可以关闭文本编辑器并在 Visual Studio 中重新打开解决方案/项目。然后我可以在 UI 编辑器中选择 STATIC TEXT 项,在它的属性面板中,在 ID 属性字段中,我可以下拉值列表并选择我准备的 IDC_MidTextObj 值。请注意,还有另一种方法可以做到这一点,即手动编辑对话框的 .rc 文件。无论如何,您可能出于其他原因需要这样做。请参阅我的答案的下一部分。
答案 3)
这是我的 TutMFC01p.rc 文件的相关部分。
IDD_TUTMFC01P_DIALOG DIALOGEX 0,320,199
STYLE DS_SETFONT | DS_FIXEDSYS | WS_POPUP | WS_VISIBLE | WS_CAPTION | WS_THICKFRAME
EXSTYLE WS_EX_CLIENTEDGE | WS_EX_APPWINDOW
CAPTION "My VisualC++ Tutorial App 01"
FONT 8,"MS Shell Dlg",0x1
BEGIN
DEFPUSHBUTTON "OK",IDC_STATIC,8
LTEXT "Hello MFC!",22,33,86,8,WS_DISABLED
PUSHBUTTON "Hide object",14
END
我可以更改以 CTEXT 开头的行,并将 IDC_STATIC 替换为 IDC_MidTextObj,以确保 STATIC TEXT 项目使用我预先创建的值。
此外,如果您仔细观察,您会看到下一行定义了另一个 STATIC TEXT 项(我添加到对话框编辑器窗口中的一个新静态文本项)。但这是 LTEXT 而不是 CTEXT。如果不以文本形式查看此代码,我就不会知道有这两种不同的类型。也许 LTEXT 就是我所追求的。我会在文档中看到我对此的发现。
答案 4)
不是单线。有关详细信息,另请参阅我的答案的下一部分。但是可以用多行代码,调用方法先查询对象的当前可见状态,如果可见就隐藏,如果隐藏就显示。
答案 5)
不,在 VC++ 中没有办法做到这一点。它适用于其他语言,但在 C++ 中您必须调用函数/方法。请参阅 similar issue 底部的 Mark Ransom 的回答。
版权声明:本文内容由互联网用户自发贡献,该文观点与技术仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请发送邮件至 dio@foxmail.com 举报,一经查实,本站将立刻删除。