使用 C++ 的托管扩展针对 Windows 窗体编程

Chris Sells 和 Sam Gentile

适用于:

Microsoft®Visual C++® .NET

Windows Forms

摘要:本文讨论了如何使用 Visual C++ .NET 的托管扩展针对 Windows 窗体编程,并提供了使用直接访问 Windows 窗体类的手动编程技术的示例,以及使用 Windows 窗体设计器的示例。此外,本文还对 Windows 窗体和 Microsoft 基础类 (MFC) 应用程序进行了比较。

本页内容

简介简介
什么是 Windows 窗体?什么是 Windows 窗体?
从头开始创建 Windows 窗体从头开始创建 Windows 窗体
使用 Visual Studio .NET 设计器创建 Windows 窗体使用 Visual Studio .NET 设计器创建 Windows 窗体
安排控件安排控件
数据绑定数据绑定
从 MFC 迁移从 MFC 迁移
小结小结

简介

长期以来,程序员们都使用 C 和 C++ 来开发 Windows GUI 应用程序。对于我们当中很多人来说,这一段历史可以追溯到 Windows 2.0 时期,那时,我们使用基于 C 的 16 位 Windows API,即便只是显示一个窗口,也需要编写数十行代码。幸运的是,随着时间的推移,抽象的级别越来越高,越来越好。在 1992 年,Microsoft 发行了 Programmer's Workbench,其中包括 Microsoft 基础类库 1.0 版。Microsoft 基础类库 1.0 版包含大约 60 个类,用于包装窗口化编程和绘制部分的 16 位 Windows API。到 2002 年为止,Microsoft 基础类库 7.0 版已经发展为超过 200 个类,并且,其用途已经扩展为能够提供 Microsoft Win32 API 的完整 C++ 对象模型替代物。

虽然 MFC 非常强大,但它也有很多缺点,比如它只是 Win32 API 外的一层薄薄的面板,并且对于很多程序员来说,它太过复杂,很难有效地使用。通常,要开发 Windows 应用程序,仍需要直接访问 Win32 API,特别是在对 Windows 应用程序所需的基本功能的要求不断提升的情况下。因此,要开发任何功能真正强大的 Windows GUI 应用程序,需要耗费大量时间精力。为了应对应用程序开发难度不断提高的状况,Microsoft 于 2002 年初发行了一个针对 Windows 平台的新编程环境。该环境称为 .NET 框架,它为开发人员提供了一个托管 应用程序运行库,以及称为 .NET 框架类库的大量库。.NET 框架可以管理内存和安全性,从而能够产生更为可靠的应用程序。.NET 框架类库提供了一个大型、资源丰富和统一的类库,任何 .NET 语言(包括 Micrisoft 为 .NET 程序员提供的 C++ 的托管扩展和 C++ 的托管版),都可以以相同的方式同等地访问该类库。作为 .NET 框架的一部分,Windows 窗体是一组用于构建客户端 Windows GUI 应用程序的类。

本文中,我们将深入了解如何使用 C++ 的托管扩展编写 Windows 窗体代码,先介绍如何从头开始编写,然后讲解如何使用 Microsoft Visual Studio .NET 2003 来完成这一任务。与此同时,我们将着重介绍 Windows 窗体的一些常用功能,如自动布局和数据绑定。最后,我们将把注意力集中到 Windows 窗体与 MFC 的比较以及在进一步使用托管扩展时,如何混合使用这两套工具。

什么是 Windows 窗体?

Windows 窗体是一个窗口化工具包,而不像 MFC 一样是完整的应用程序框架。事实上,相对于 Windows 窗体所提供的用于构建基于文档的独立应用程序的功能来说,MFC 提供的功能更多。例如,如果要生成一个文本编辑器,在 MFC 中,只需运行一个向导,选择适当的选项并编写若干行代码即可完成。仅仅是通过运行该向导得到的应用程序就包含了一个状态栏、一个工具栏(浮动),并实现了所有的 File、Edit 和 Help 菜单项,其中包括最近使用的文件列表和打印以及上下文相关的帮助,所有这些内容都包含在一个完全徽标兼容的单文档界面 (SDI)、多 SDI 或多文档界面 (MDI) 应用程序中。作为基于文档的应用程序框架,没有能够与 MFC 相匹敌的竞争者。

但是,程序员们现在倾向于构建更多基于 HTML 的应用程序,或者能够与业务对象或数据库服务器通信的应用程序,而不是基于文档的应用程序。.NET 框架和 Windows 窗体正是为这一目的而量身定做的。

这并不是说 Windows 窗体不能用来构建优秀的基于文档的应用程序。实际上,由于 Windows 窗体只是 .NET 框架所提供的超过 2000 个类中的一小部分,您所需要的内容很有可能 Windows 窗体并没有提供,而是位于该框架中的其他部分中。例如,Windows 窗体本身并不提供任何对象序列化支持,但是 .NET 框架类库的其余部分提供了多种序列化对象图的方法。

这是 MFC 和 Windows 窗体之间的主要区别。MFC 旨在替代基础 Win32 API,但这并不能阻止 Win32 API 增长。事实上,就像 MFC 随时间的不断增长一样,基础 OS 的功能最少增加了十倍。但是,Windows 窗体只是 Win32 的窗口化部分的替代物。.NET 框架类的其余部分负责替代其余的 Win32。当然,框架永远不可能替代整个 Win32 API,不过,由于在可预期的未来,大多数要添加到 Windows 中的新功能都要被添加到框架中,替代整个 Win32 API 将是一个未来的目标。

因此,虽然 Windows 窗体不能具有 MFC 的全部功能,但它的确提供了一组很强的功能,可以极大地便利客户端应用程序开发人员,这其中包括一些 MFC 完全不具备的功能。下面,我们将介绍如何从头开始构建应用程序,然后讲解 Visual Studio .NET 2003 为 Windows 窗体 C++ 程序员提供的工作效率改进功能。

从头开始创建 Windows 窗体

典型的 Windows 窗体应用程序有至少一个窗体。窗体就是一个窗口,也就是从 Windows 1.0 开始我们所看到的 Microsoft 用户界面单元。通常,Windows 窗体应用程序中的一个窗体是主窗体,意思是它是应用程序的生存周期内可能显示的所有其他窗体的父级或所有者。该窗体是显示主菜单以及工具栏、任务栏等的位置。主窗体结束时,应用程序也退出。

应用程序的主窗体可以是一个简单的消息框、一个对话框、一个 SDI 窗口、一个 MDI 窗口,或者更为复杂的控件,如 Visual Studio .NET 这样的应用程序中具有多个子窗口、工具窗口和浮动工具栏的窗体。

如果您的应用程序极为简单,可以使用充斥在任何窗口化系统中的简朴的消息框来实现它:

#using <mscorlib.dll>
#using <System.dll>
#using <System.Windows.Forms.dll>

void __stdcall WinMain(HINSTANCE hInstance,
             HINSTANCE hPrevInstance,
          long lpCmdLine,
          int nCmdShow)
{
  System::Windows::Forms::MessageBox::Show("Hello, Windows Forms");
}

作为 C++ 程序员,您必定非常熟悉 WinMain 入口点,在托管应用程序中,仍需要该入口点。在我们的第一个 Windows 窗体应用程序中,唯一一行实际的代码调用了 Windows::System::Forms::MessageBox 类的静态 Show 方法,这其实只是调用包含在 Window::Systems::Forms 命名空间 内的 MessageBox 类的静态方法的长写形式。.NET 框架类库中广泛使用了命名空间来将类、结构、枚举等类型分隔为不同的逻辑组。由于有数以千计的 Microsoft 雇员要从事添加 .NET 框架类库的工作,而且有数以百计的第三方组织要扩展该类库,同时有数以百万计的程序员正在尝试学习它,因此这种分隔是非常必要的。没有命名空间,就需要采用各种复杂的约定来唯一地命名各种对象(像现有的 Win32 API 一样)。

命名空间中的类型的定义位于 .NET 程序集 中。程序集是打包为 DLL 或 EXE 的托管类型的容器。#using 指令用于将程序集中的类型应用到您的应用程序中。例如,mscorlibSystem 程序集提供了基本的 .NET 框架类型,如 intstringSystem.Windows.Forms 程序集包含了 Windows 窗体类型。

使用 #using 将程序集应用到应用程序中后,就可以以前面讲到的长写形式引用类型,或者,可以使用标准的 C++ using namespace 语句来省略键入代码的工作:

#using <mscorlib.dll>
#using <System.Windows.Forms.dll>

using namespace System::Windows::Forms;

void __stdcall WinMain(HINSTANCE hInstance,
             HINSTANCE hPrevInstance,
          long lpCmdLine,
          int nCmdShow)
{
  MessageBox::Show("Hello, Windows Forms");
}

因此,虽然这个简单的示例很有效地演示了最基本的 .NET 框架和 C++ 的托管扩展的概念,但它并不能很好地演示典型的 Windows 窗体程序。对于真实的应用程序,将需要一个 Form 类(或从 Form 派生的类)的实例,如下所示:

void __stdcall WinMain(HINSTANCE hInstance,
             HINSTANCE hPrevInstance,
          long lpCmdLine,
          int nCmdShow)
{
  Form* form = new Form();
  ...
}

form 变量引用了托管类型的一个实例。托管对象是由 .NET 框架的公共语言运行库 (CLR) 处理的,它们的生存周期由垃圾回收器控制的,该回收器在一定的时间取消分配内存。因此,C++ 程序员无需显式地删除托管类型,但是同样不能指望在任何特定时刻(如关闭范围时)破坏对象。

创建窗体后,就需要显示它。如果您曾经看过 Windows 窗体的文档,可能已经注意到了 Form 方法 Show,这意味着:

void __stdcall WinMain(HINSTANCE hInstance,
             HINSTANCE hPrevInstance,
          long lpCmdLine,
          int nCmdShow)
{
  Form* form = new Form();
  form->Show(); // This is not what you want to do.
}

虽然上面的代码可以显示窗体,但是必须眼疾手快才可以看到该窗体,这是因为 Show 以无模式的方式显示窗体。这意味着在 Show 将新的窗体显示到屏幕上之后,会立即将控制权返回给 Main 函数,该函数在返回时将退出进程,同时关闭刚刚显示不久的窗体。要以有模式的方式显示窗体,文档建议使用 ShowDialog 函数:

void __stdcall WinMain(HINSTANCE hInstance,
             HINSTANCE hPrevInstance,
          long lpCmdLine,
          int nCmdShow)
{
  Form* form = new Form();
  form->ShowDialog(); // This is not quite what you want to do.
}

虽然这些代码事实上可以显示一个空窗体,并且在用户关闭它之后才会将控制权返回给 Main 函数,但通常您并不会编写这样的代码。相反,您将把一个窗体指定为主窗体,使得应用程序的其他部分可以把它当作主窗体来进行访问。为此,可以将主窗体作为一个参数传递给 Windows 窗体的 Application 对象的 Run 方法:

#using <mscorlib.dll>
#using <System.dll>
#using <System.Windows.Forms.dll>

using namespace System::Windows::Forms;

void __stdcall WinMain(HINSTANCE hInstance,
             HINSTANCE hPrevInstance,
          long lpCmdLine,
          int nCmdShow)
{
  Application::Run(new Form);
}

Application 类的静态 Run 方法将显示主窗体并开始发送 Windows 消息,直到主窗体关闭为止。主窗体关闭后,Run 将返回,让我们的 Main 函数退出,以便结束进程。要实际查看这一过程,可以使用下面的命令行编译这个小小的 Windows 窗体应用程序:

C:/MSDN/MYFIRS~1>cl /clr MyFirstApp.cpp

现在,编译器已经生成了 MyFirstApp.exe,可以执行它了。关闭窗体时,MyFirstApp.exe 将退出,结束您的第一个 Windows 窗体应用程序。

要想增添一些趣味,可以在新窗体上设置一个属性。像 .NET 框架类库中的大多数对象一样,Form 对象有一些可以访问的属性、可以调用的方法和可以处理的事件。可以直接在 Form 类的实例上设置属性,但通常我们不会这么做。相反,每个自定义窗体都是一个派生自 Form 的类,并且会初始化自己的属性,如下所示:

__gc class MyForm : public Form
{
public:
  MyForm()
  {
      Text = "Hello, Windows Forms!";
  }
};

void __stdcall WinMain(HINSTANCE hInstance,
             HINSTANCE hPrevInstance,
          long lpCmdLine,
          int nCmdShow)
{
  Application::Run(new MyForm);
}

在 C++ 的托管扩展中,自定义托管类型是使用 __gc 修饰符声明的。请注意,MyForm 类派生自 Form,然后在构造函数中初始化了它自己的属性。这提供了一个很好的用法模型,如新的 Main 函数中所示,该函数创建了 MyForm 类的一个实例。

但这个窗体仍旧很乏味。除系统提供的特点外,它没有任何交互功能。我们可以通过添加一个按钮来增加一些交互功能,如下所示:

MyForm()
{
  Text = "Hello, Windows Forms!";
  Button* button = new Button();
  button->Text = "Click Me!";
  this->Controls->Add(button);
}

向窗体添加按钮就是创建一个新的 Button 对象,设置我们需要的属性,并将其添加到该窗体所管理的控件列表中。虽然上面的代码会在窗体上产生一个按钮,在单击它后看上去像是被单击了,但并不会发生别的事情,这是因为您还没有处理该按钮的 click 事件。您可以这样来处理该事件:

__gc class MyForm : public Form
{
public:
  MyForm()
  {
    Text = "Hello, Windows Forms!";
    Button* button = new Button();
    button->Text = "Click Me!";
    button->Click += new EventHandler(this, button_click);
    this->Controls->Add(button);
  }

  void button_click(Object* sender, EventArgs* e)
  {
    MessageBox::Show("Ouch!");
  }
};

处理按钮的 Click 事件涉及两项工作。第一项是创建具有适当签名的处理程序方法,我们称其为 button_Click。绝大多数 .NET 框架事件的签名都是一个函数,该函数什么都不返回,但是获取两个参数:一个表示事件的发送方(本例中为按钮)的对象,和一个 System::EventArgs 对象(或者一个从 EventArgs 类派生的对象)的实例。

预订事件所需的第二项内容是在 MyForm 构造函数中使用 += 运算符的行。这种表示法意味着您要向与特定对象上的特定事件有关的所有其他方法的列表中添加一个方法。这要求 EventHandler 委托对象的一个实例。委托是一个类,该类将对事件的调用转换为预订该事件的方法的调用。

请注意,Button 类上的 Click 事件是对 EventHandler 委托的引用,因此,要将自己的方法添加到订户列表中,您还需要创建一个该种类的委托的实例。当然,搞清楚您所感兴趣的所有事件的委托签名,或者通过手动编写代码来将控件添加到窗体上,很快会成为很乏味的事情。幸运的是,这同样可以避免,因为 Visual Studio .NET 2003 中集成了 Windows Forms for C++。

使用 Visual Studio .NET 设计器创建 Windows 窗体

在 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。不幸的是,设计器和属性浏览器都没有提示特定类型的默认事件是什么,但是实践中通常不会由多少例外情形。

安排控件

设计器的好处在于您可以在窗体内安排控件的布局,确保整齐地排列所有的元素(如图 1 所示),而不像大小被调整时的状态(如图 2 所示)。


1 理想大小且整齐布局的窗体


2 整齐布局的窗体大小被调整

用户调整窗体大小不是为了能有更多灰色的空间,他们是为了能够有更多空间用于将数据输入控件中。要达到这一目的,需要重新调整控件的大小,以占据新的可用空间。这可以通过处理窗体的 Resize 事件并编写代码来手动完成。或者,也可以通过定位 来实现。

定位是 Windows 窗体为了对窗体和窗体所包含的控件进行自动布局控制而提供的一种方法。默认情况下,所有的控件都定位到左上方,这样,在调整窗体的大小并移动时,所有的控件都将相对于窗体左上角保持它们的位置。但是,在本例中,最好在调整窗体大小时加宽或缩窄文本框控件。这可以通过设置各个文本框的 Anchor 属性来实现。显示控件的属性浏览器并选择 Anchor 属性,将显示如图 3 所示的编辑器。


3 设置 Anchor 属性

要将文本框更改为定位到右边缘以及上边缘和左边缘,只需要单击右侧的定位框,然后将 Anchor 属性更改为 Top、Left 和 Right。这将导致文本框的大小随窗体的大小一起变化,如图 4 所示。


4 将文本框定位到顶部、左侧和右侧以及将按钮定位到底部和右侧

虽然默认的定位为左上方,但根本不需要将这些边缘作为定位设置的一部分。例如,您可以看到在图 4 中,OK 和 Cancel 按钮被定位到了右下角,这与 Windows 对话框的习惯相同。

如果您不想生成对话框样式的窗体,而是要生成窗口样式的窗体,定位将不是最好的选择。例如,如果您要构建资源管理器样式的应用程序,该应用程序在顶部有一个菜单栏和一个工具栏,底部有一个状态栏,一个树视图和一个列表视图占据其余空间,并用控件之间的拆分器来确定控件所占据的空间,将不能使用定位。此时,您需要使用停靠。

默认情况下,控件的 Dock 属性设置为 None。您可以在属性浏览器中更改 Dock 属性,方法是选择一个要停靠的边缘,或者选择占据剩余的空间。

例如,状态栏、树视图和列表视图的 Dock 属性可能会显示后两者被一个拆分器控件拆分开来,所有这些都已经安排完毕,您无须编写任何代码。

定位、停靠和拆分并不是排列窗体上的控件的仅有的方法。Windows 窗体还支持分组控件和处理特殊情况下的自定义布局。此外,Windows 窗体支持在父级内排列窗口、这通常称为多文档界面 (MDI)。

数据绑定

数据绑定 是指这样一种能力:将一个或多个控件的内容绑定到一个数据源,使得当其中一方被更新时,另一方也得到更新。数据绑定不光在 Windows 窗体中受到良好支持,它还完全集成到了 Visual Studio .NET 本身当中。

从服务器资源管理器将一个表拖放到设计图面上将创建两个组件,一个用于连接到数据库的连接 和一个通过连接在双方之间传送数据的适配器。在设计器中右键单击适配器并选择 Generate Dataset,将创建一个新的数据集,它是一个从 DataSet 派生的类,生成该类专用于保存您从服务器资源管理器拖出来的表的数据。默认的 General Dataset 选项还会创建新数据集的一个实例,用于与控件相关联。

得到数据的源后,就可以将数据绑定到一个或多个控件。Windows 窗体提供了多个数据库绑定控件,包括 ListBox 和 ComboBox 等,其中 DataGrid 灵活性最高。

窗体上有了数据集后,要将数据网格绑定到它并将其作为数据源,只需在属性浏览器中设置数据网格的 DataSourceDataMember 属性,并在加载窗体时填充该数据集:

void InitializeComponent(void)
{
  ...
  this->dataGrid1->DataMember = "Customers";
  this->dataGrid1->DataSource = this->dataSet11;
  ...
}

private: System::Void Form1_Load(System::Object* sender, System::EventArgs* e)
{
  sqlDataAdapter1->Fill(dataSet11);
}

以上只是数据绑定的一般用途以及数据网格的特定用途的冰山一角。有关指向更多数据绑定资源的链接,请参阅后面的“参考”部分。

从 MFC 迁移

作为 MFC 程序员,您花在现有代码基上的时间与精力一定是非常多的。将 MFC 代码迁移到 .NET 框架需要进行仔细的规划。以下是一些注意事项:

如果可以承受得了从头开始,将能够得到可维护性最好的代码基,但是耗时最长。

如果您的大多数 MFC 代码在 COM 控件中(或者可以迁移到 COM 控件),您就可以使用 Windows 窗体作为这些控件的宿主,并为框架编写新的托管代码。

如果您需要升级 MFC 应用程序本身,可以使用 MFC 7.1 提供的功能,将 Windows 窗体控件宿主为 COM 控件,仍旧保留 MFC 代码为非托管。有关指向以上方案的详细信息的链接,请参阅“参考”部分。

如果您想要在 Windows 窗体应用程序中使用托管代码,但是要避免使用 COM Interop 的开销,可以取消 MFC 项目的“Use Managed Extensions”的选项,以便能够在相同的代码中混合使用托管和非托管类型。有关指向以上方案的详细信息的链接,也请参阅“参考”部分。

适用于您的选项要视具体情况而定,但是一般说来,我们建议您采用您自己可以编写 .NET 框架的大部分新代码的策略,虽然这意味着您所生成的一些 Windows 窗体功能原本是在 MFC 中开始了解并喜欢上的。

小结

本文介绍了一些最引人注目的新 Windows 窗体功能,但是 Windows 窗体具有更多功能,如全自动部署、对话框数据交换和验证、MDI 应用程序、绘制和打印(包括打印预览)、用户控件、自定义组件和设计时支持、清单和类型化资源、非编译资源本地化和应用程序设置等等。

对于 MFC 和 Windows 窗体的比较,您需要牢记的一点是它们是在非常不同的时代为了解决大不相同的问题而分别构建的。MFC 是一个基于文档的应用程序框架。Windows 窗体是一个 n 层应用程序的窗口化库。两者之间存在区别很正常。其中一些区别,如定位和停靠,使得 Windows 窗体更具优势。其他的区别,如对象序列化,存在于 .NET 框架类库的其他部分中。另有一些功能根本不存在,这正是进行基于文档的应用程序开发的有趣之处。

不过,有两件事非常清楚:Microsoft 正在大踏步靠向托管代码,而托管代码的优势是非常引人注目的。.NET 框架提供了内存和安全性两者的自动处理,从而能够得到更为可靠的应用程序,并可以提高开发人员的工作效率。.NET 框架类库提供了一个大型、资源丰富和统一的类库。有了这样的组合,我们将能够多快好省地构建 Windows 应用程序。

参考

Visual Studio .NET: Managed Extensions Bring .NET CLR Support to C++,作者是 Chris Sells,MSDN Magazine 2001 年 7 月刊。

Windows Forms: A Modern-Day Programming Model for Writing GUI Applications,作者是 Jeff Prosise,MSDN Magazine 2002 年 2 月刊。

.NET Delegates: A C# Bedtime Story, Chris Sells.

Cutting Edge: Data Binding Between Controls in Windows Forms,作者是 Dino Esposito,MSDN Magazine 2002 年 2 月刊

Pragmatic ADO.NET: Data Access for the Internet World,作者是 Shawn Wildermuth、Addison-Wesley,2002 年出版。

Windows Forms Programming in C#,作者是 Chris Sells、Addison-Wesley,2003 年出版。

Windows Forms: .NET Framework 1.1 Provides Expanded Namespace, Security, and Language Support for Your Projects,作者是 Chris Sells,MSDN Magazine 2003 年 3 月刊。

Introduction to Managed C++。作者是 O'Reilly OnDotNet 的 Sam Gentile,2003 年 1 月出版。

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值