扫一扫
分享文章到微信
扫一扫
关注官方公众号
至顶头条
在 Visual Studio .NET 2003 之前,只有 C# 和 Visual Basic .NET 具有可视窗体设计器支持。C++ 的托管扩展没有。很幸运,现在 Visual Studio .NET 附带了 C++ 的托管扩展的 Windows 窗体设计器。
大多数 Windows 窗体项目都在 New Project 对话框中开始,您可以通过单击 File,指向 New,然后单击 Project,或者通过按 CTRL+SHIFT+N 来访问该对话框。
运行 Windows 应用程序向导时,可以随意选择项目名称和位置,然后就可以在设计器中得到一个空白窗体。
在 MFC 中,仅对对话框支持拖放设计和用户界面布局。普通视图必须在代码中进行布局。但是,Windows 窗体则以一种统一的方式对待所有的窗体,因此相同的拖放设计器适用于各种窗体。窗体的类型(有模式或无模式,对话框或视图)取决于它的使用方式,而不是设计方式。
设计器中的 Windows 窗体和 MFC 的下一个重大区别是设计器在代码中而不是单独的资源文件中保存控件和布局信息。这与 MFC 很不相同,MFC 将 Win32 对话框资源中的对话框布局信息保存在 .rc 文件中。这两种方案都各有优缺点,但是 MFC 程序员很快会注意到其中的不同之处。适应起来需要一个过程。
您应当注意的另一件事情是 C++ 的托管扩展项目向导将实现代码生成到了 .h 文件而不是 .cpp 文件中。这可能是由于 C++ 设计器必须适应现有的设计器体系结构而不是 Visual Studio .NET 2002,而后者仅支持 C# 和 Visual Basic .NET 这样的语言,这些语言不分割声明和定义代码。生成的头文件(可以通过右键单击设计图面然后选择 View Code 或通过按 F7 来访问)如下所示:
namespace MySecondApp { using namespace System; using namespace System::ComponentModel; using namespace System::Collections; using namespace System::Windows::Forms; using namespace System::Data; using namespace System::Drawing; /// <summary> /// Summary for Form1 public __gc class Form1 : public System::Windows::Forms::Form { public: Form1(void) { InitializeComponent(); } private: /// <summary> /// Required designer variable. /// </summary> System::ComponentModel::Container * components; /// <summary> /// Required method for Designer support - do not modify /// the contents of this method with the code editor. /// </summary> void InitializeComponent(void) { this->components = new System::ComponentModel::Container(); this->Size = System::Drawing::Size(300,300); this->Text = "Form1"; } }; }
上面的大多数代码应当都是为大家所熟知的,包括顶部的 using 语句和从 Form 基类派生的自定义窗体。唯一一处与您的实现不同的地方是窗体的构造函数中调用 InitializeComponent 以设置窗体的属性,而不是在构造函数本身中进行设置。之所以这么做,是为了使 Windows 窗体设计器有地方放置初始化窗体上的控件和窗体本身的代码。
Windows 窗体应用程序项目模板产生了一个 .cpp 文件:
int APIENTRY _tWinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance, LPTSTR lpCmdLine, int nCmdShow) { System::Threading::Thread::CurrentThread->ApartmentState = System::Threading::ApartmentState::STA; Application::Run(new Form1()); return 0; }
以上代码看上去同样很熟悉。Main 函数提供了应用程序的入口点和对 Application::Run 的调用,传入主窗体类的一个实例。将 ApartmentState 变量设置为单线程单元 (STA),是为了 COM 的适当惰性初始化能够在拖放操作中以用户友好的方式使用,以及宿主 COM 控件。
返回到 InitializeComponent 实现,可以看到窗体设计器在那里放置了代码。例如,将按钮从工具箱拖动到窗体的设计图面将把 InitializeComponent 实现更改为如下所示的代码:
void InitializeComponent(void) { this->button1 = new System::Windows::Forms::Button(); this->SuspendLayout(); // // button1 // this->button1->Location = System::Drawing::Point(0, 0); this->button1->Name = "button1"; this->button1->TabIndex = 0; this->button1->Text = "button1"; // // Form1 // this->AutoScaleBaseSize = System::Drawing::Size(5, 13); this->ClientSize = System::Drawing::Size(288, 253); this->Controls->Add(this->button1); this->Name = "Form1"; this->Text = "Form1"; this->ResumeLayout(false); }
请注意,上面的代码同样与您以前生成的代码很类似,但它们是由设计器创建的。不幸的是,要使这一进程能够可靠地运行,设计器需要能够完全控制 InitializeComponent 方法。事实上,您可以看到向导生成的 InitializeComponent 代码包装在一个区域中(默认情况下将隐藏代码),并标记了说明性的注释:
/// Required method for Designer support - do not modify /// the contents of this method with the code editor.
这可能看上去像是您首选的编程语言,但 InitializeComponent 实际上是设计器用于管理设计图面的对象模型的序列化形式。虽然您可以对这些代码进行某些细微的更改,如更改新按钮的 Text 属性,但是重大的更改可能会被忽略,甚至被删除。我们建议您将自定义窗体初始化放入窗体的构造函数中对 InitializeComponent 的调用之后,以确保设计器不会破坏您的代码。
当然,设计器所带来的好处抵消了它的这种对代码的侵犯。例如,您无须通过编写多行代码来设置窗体或它所包含的控件的属性,只需要右键单击所需的对象并选择 Properties(或按下 F4)来调出选定对象的属性浏览器。
所有非默认的属性(表示为以粗体显示的值)都会为您写入 InitializeComponent 方法,您无须再编写这些代码。与此类似,要选择窗体或窗体的控件的事件以进行处理,可以单击属性浏览器窗口顶部的 Events 闪电形图标。
在属性浏览器窗口中,可以以多种方式实现事件的处理。一种方法是通过单击和键入在激发事件时要调用的函数的名称来找出要处理的选定对象的事件,如 button_Click。在输入事件处理程序的名称后按 Enter,将把您带到具有该名称和正确的签名的事件处理程序的正文,您可以立即着手实现:
private: System::Void button_Click(Object* sender, EventArgs* e) { }
如果您像一般情况下一样希望为各个对象处理的各个事件是唯一的,或者您不关心处理程序的名称,只需在属性浏览器中双击该事件的名称,就会根据控件和事件的名称为您生成一个事件处理程序名称。例如,如果在 Form 1 窗体的 Load 事件上双击,事件处理程序名称将为 Form1_Load。
此外,如果您要处理对象的默认事件,只需双击对象本身就可以进行处理 - 这样会像您双击属性浏览器事件列表中的事件名称一样生成一个事件处理程序名称。对象的默认事件指的是直观上特定类型最常处理的事件。例如,按钮的默认事件为 Click,窗体的默认事件为 Load。不幸的是,设计器和属性浏览器都没有提示特定类型的默认事件是什么,但是实践中通常不会由多少例外情形。
查看本文来源如果您非常迫切的想了解IT领域最新产品与技术信息,那么订阅至顶网技术邮件将是您的最佳途径之一。
现场直击|2021世界人工智能大会
直击5G创新地带,就在2021MWC上海
5G已至 转型当时——服务提供商如何把握转型的绝佳时机
寻找自己的Flag
华为开发者大会2020(Cloud)- 科技行者