Chris Sells and Sam Gentile
April 2003
Applies to:
Microsoft®Visual C++® .NET
Windows Forms
Summary: This article discusses Windows Forms programming with Managed Extensions for Visual C++ .NET and provides examples of both using manual programming techniques that directly access the Windows Forms classes, and using the Windows Forms Designer. In addition, the article compares Windows Forms and Microsoft Foundation Classes (MFC) applications. (20 printed pages)
Contents
Introduction
What Are Windows Forms?
Creating Windows Forms from Scratch
Creating Windows Forms Using the Visual Studio .NET Designer
Arranging Controls
Data Binding
Moving from MFC
Conclusion
Introduction
Programmers have a long history of developing Windows® GUI applications using C and C++. For many of us, this history dates back to the Windows 2.0 days when we used the C-based 16-bit Windows API and it took tens of lines of code just to display a window. Fortunately, as the years have gone by, the level of abstraction has gotten higher and better. In 1992, Microsoft released the Microsoft Foundation Class Library version 1.0 as part of the Programmer's Workbench. The Microsoft Foundation Class Library version 1.0 was a set of about 60 classes targeted at wrapping the windowing and drawing parts of the 16-bit Windows API. By 2002, Microsoft Foundation Class Library version 7.0 had evolved to over 200 classes and expanded in its goals to provide a complete C++ object model replacement for the Microsoft® Win32® API.
While MFC is powerful, it suffers in many places from being merely a thin veneer over the Win32 API, as well as being difficult for many programmers to use effectively. Often, direct access to the Win32 API is still required to develop Windows applications, especially as the bar for the base features required for Windows applications continues to rise. As a consequence, it takes a great deal of time and effort to develop any non-trivial Windows GUI application. To address the growing challenges in developing applications, in early 2002, Microsoft released a new programming environment for the Windows platform. This environment, known as the .NET Framework, provides a managed runtime for applications as well as a large set of libraries, known as the .NET Framework class library for developers. The .NET Framework manages memory and security, resulting in more robust applications. The .NET Framework class library provides a vast, rich and uniform class library, accessible equally and in the same way, from any .NET language, including Managed Extensions for C++, the managed version of C++ that Microsoft provides for .NET programmers. As part of the .NET Framework, Windows Forms is a set of classes for building client-side Windows GUI applications.
In this article, we'll dig into how to write Windows Forms code using Managed Extensions for C++, first from scratch, and then using Microsoft® Visual Studio® .NET 2003. Along the way, we'll highlight some of the popular features of Windows Forms, like automatic layout and data binding. Finally, we’ll focus on how Windows Forms compares to MFC and how to intermingle the two worlds as you move forward into working with Managed Extensions.
What Are Windows Forms?
Windows Forms is a windowing toolkit, and not a complete application framework like MFC. The fact is that MFC provides more features than Windows Forms does for building stand-alone document-based applications. For example, if you want to build a text editor, you can do that in MFC by running a wizard, choosing the right options and writing only a few lines of code. The application you start with just from running the wizard will include a status bar, a toolbar (floating), all of the File, Edit and Help menu items implemented, including an most-recently used file list, printing and context-sensitive help, all in a fully logo-compliant single document interface (SDI), multi-SDI or multiple document interface (MDI) application. As a document-based application framework, MFC has no peer.
However, rather than building document-based applications, programmers are tending to build more HTML-based applications, or applications that talk to business object or database servers. It's for this use that the .NET Framework and Windows Forms have been tailored.
That's not to say that Windows Forms can't be used to build nice document-based applications. In fact, because Windows Forms is but a small piece of the over 2000 classes provided in the .NET Framework, it's likely that if what you're looking for isn't provided in Windows Forms, it's provided somewhere else in the Framework. For example, Windows Forms itself doesn't provide any object serialization support, but the rest of the .NET Framework class library provides several ways to serialize object graphs.
This is the chief difference between MFC and Windows Forms. MFC was meant as a replacement for the underlying Win32 API, but that didn't stop the Win32 API from growing. In fact, as much as MFC grew over the years, the functionality of the underlying OS increased at least by ten times. Windows Forms, on the other hand, is meant to be a replacement only for the windowing part of Win32. It's the rest of the .NET Framework classes that are meant to replace the rest of Win32. Of course, the Framework will never replace the entire Win32 API, but since most new functionality added to Windows in the foreseeable future is going to be added to the Framework, that's the way to go.
So, while Windows Forms cannot do everything that MFC does, it does provide an impressive array of features that can make life easier for client-side application developers, including things that MFC doesn't do at all. Following is a description of how to build an application from scratch, and then a discussion of the productivity aids that Visual Studio .NET 2003 provides for the Windows Forms C++ programmer.
Creating Windows Forms from Scratch
A typical Windows Forms application has at least one form. A form is just a window, which is the unit of user interface that we've seen from Microsoft since Windows 1.0. One form in a Windows Forms application is typically the main form, which means that it is either the parent or the owner of any other form that might be shown during the lifetime of the application. It's where the main menu is shown, along with the toolbar, status bar, and so on. When the main form goes away, the application exits.
The main form of an application can be a simple message box, a dialog box, an SDI window, an MDI window or something more complicated like those you see in applications like Visual Studio .NET, with multiple child windows, tool windows and floating toolbars.
If your application is enormously simple, you can implement it with a staple of any windowing system, the lowly message box:
#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");
}
As C++ programmers, you are no doubt familiar with the WinMain entry point, which must still be present in a managed application. The single line of real code in our first Windows Forms application calls the static Show method of the Windows::System::Forms::MessageBox class, which is really just a long way of calling a static method on the MessageBox class contained within the Window::Systems::Forms namespace. Namespaces are used extensively in the .NET Framework class library to separate types, such as classes, structures, enumerations, and so on, into logical groupings. This separation is necessary when you've got thousands of Microsoft employees adding to the .NET Framework class library as well as hundreds of third parties extending it and millions of programmers trying to learn it. Without namespaces, you need all kinds of complex conventions to keep things uniquely named (as demonstrated by the existing Win32 API).
The definition of the types from a namespace comes from a .NET assembly. An assembly is a container of managed types packaged as a DLL or an EXE. The #using directive is used to bring the types from an assembly into your application. For example, the mscorlib and System assemblies provide the basic .NET Framework types, like int and string. The System.Windows.Forms assembly is where the Windows Forms types come from.
Once you've brought an assembly into your application with #using, you can reference the types in the long way shown previously, or you can use the standard C++ using namespace statement to save yourself typing:
#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");
}
So, while this simple sample has been a useful way to demonstrate the most basic .NET Framework and Managed Extensions for C++ concepts, it is not very indicative of a typical Windows Forms program. For real applications, you'll need an instance of the Form class (or a Form-derived class), as shown here:
void __stdcall WinMain(HINSTANCE hInstance,
HINSTANCE hPrevInstance,
long lpCmdLine,
int nCmdShow)
{
Form* form = new Form();
...
}
The form variable refers to an instance of a managed type. Managed objects are handled by the .NET Framework's common language runtime (CLR) and their lifetime is controlled by the garbage collector, which will deallocate the memory at some point. Because of this, the C++ programmer does not need to explicitly delete the managed type, but also must not depend on an object being destroyed at any certain time, like at the closing of a scope.
Once a form is created, it also needs to be shown. If you've looked at the documentation for Windows Forms at all yet, you've probably noticed the Form method Show, which suggests the following:
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.
}
While this code will show the form, you'll have to be quick to see it, because Show shows the form modelessly. This means that immediately after Show puts the new form on the screen, it returns control to the Main function, which, when it returns, exits the process taking the nascent form with it. To show a form modally, the documentation suggests the ShowDialog function:
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.
}
While this code would, in fact, show a blank form and wait for the user to close it before returning control to the Main function, it's not the code you will generally be writing. Instead, you’ll be designating a form as the main form so that other parts of your application can access it as such. To do this, pass the main form as an argument to the Run method of the Windows Forms Application object:
#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);
}
The Application class's static Run method will show the main form and start pumping Windows messages until the main form closes. When the main form is closed, Run will return, letting our Main function exit, which ends the process. To see this in action, you can compile this tiny Windows Forms application using the following command line:
C:\MSDN\MYFIRS~1>cl /clr MyFirstApp.cpp
Now that that the compiler has produced MyFirstApp.exe, you can execute it. When you close the form, MyFirstApp.exe will exit, ending your first Windows Forms experience.
To spice things up a bit, you can set a property on your new form. Like most objects in the .NET Framework class library, Form objects have several properties to access, methods to call and events to handle. We could set properties directly on an instance of the Form class, but that's not the way we generally do things. Instead, each custom form is a class that derives from Form and initializes its own properties, like so:
__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);
}
In Managed Extensions for C++, a custom managed type is declared using the __gc modifier. Notice that the MyForm class derives from Form and then initializes its own properties in the constructor. This provides a nice usage model, as shown in the new Main function that creates an instance of the MyForm class.
Still, this form is pretty boring. It has no interactivity besides the system-provided adornments. We can add some interactivity by adding a button, as follows:
MyForm()
{
Text = "Hello, Windows Forms!";
Button* button = new Button();
button->Text = "Click Me!";
this->Controls->Add(button);
}
Adding a button to the form is a matter of creating a new Button object, setting the properties that we like and adding it to the list of controls that the form manages. And while this code will produce a button on the form that looks clicked when you click it, nothing else interesting will happen, because you are still not handling the button's click event. You can do that as follows:
__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!");
}
};
Handling the button's Click event involves two things. The first is creating a handler method with the appropriate signature, which we called button_Click. The signature of the vast majority of .NET Framework events is a function that returns nothing and takes two parameters, an object that represents the sender of the event (our button, in this case), and an instance of a System::EventArgs object (or an object that derives from the EventArgs class).
The second thing that's needed to subscribe to an event is the line using the += operator in the MyForm constructor. This notation means that you would like to add a method to the list of all the other methods that care about a particular event on a particular object. This requires an instance of an EventHandler delegate object. A delegate is a class that translates invocations on an event into calls on the method that have subscribed to the event.
Notice that the Click event on the Button class is a reference to an EventHandler delegate and therefore, to add your own method to the list of subscribers, you need to also create an instance of that kind of delegate. Of course, figuring out the delegate signatures of all the events you're interested in or adding controls to a form via code by hand can quickly become tedious. Luckily, it's also unnecessary because of the integration of Windows Forms for C++ in Visual Studio .NET 2003.
Creating Windows Forms Using the Visual Studio .NET Designer
Prior to Visual Studio .NET 2003, only C# and Visual Basic .NET had Visual Forms Designer support. Managed Extensions for C++ did not. Fortunately, Visual Studio .NET now ships with Windows Forms Designers for Managed Extensions for C++.
Most Windows Forms projects start in the New Project dialog box, which you can access by clicking File, pointing to New, and then clicking Project, or by pressing CTRL+SHIFT+N.
When you run the Windows Application Wizard, choosing whatever you like for the project name and location, you'll get a blank form in the Designer.
In MFC, drag-and-drop design and layout of user interfaces is only supported for dialog boxes. Normal views must be laid out in code. Windows Forms, on the other hand, treats all forms in a unified manner, so the same drag-and-drop Designer works for any kind of form. The type of form — model or modeless, dialog box or view — depends on how it's used, not how it is designed.
The next big difference between Windows Forms in the Designer and MFC is that the Designer keeps control and layout information in code instead of a separate resource file. This is very different from MFC, which keeps dialog box layout information in a Win32 dialog resource in a .rc file. Both schemes have their pros and cons, but MFC programmers will notice the difference right away. It takes some getting used to.
Another thing you should notice is that the Managed Extensions for C++ project wizards have generated implementation code into an .h file instead of a .cpp file. This is probably due to the fact that the C++ designers had to fit into the existing designer architecture in place for Visual Studio .NET 2002, which only supports languages like C# and Visual Basic .NET that don't split declaration and definition code. The generated header file (available by right-clicking on the design surface, and choosing View Code or by pressing F7) looks like the following:
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";
}
};
}
Most of this code should be familiar, including the using statements at the top and the custom form that derives from the Form base class. The only thing that's different from what you did yourselves is the call to InitializeComponent in the form's constructor to set the form's properties instead of doing this in the constructor itself. This is done so that the Windows Forms Designer has a place to put the code to initialize the controls on the form and the form itself.
The Windows Forms Application project template does generate a .cpp file:
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;
}
Again, this looks very familiar. The Main function provides the entry point to the application and the call to Application::Run, passing in an instance of the main form class. Setting the ApartmentState variable to single-threaded apartment (STA) is for the proper lazy initialization of COM to be UI-friendly for use in drag-and-drop and for hosting COM controls.
Returning to the InitializeComponent implementation, you can see that the Forms Designer places code here. For example, dragging a button from the Toolbox onto the form's design surface will change the InitializeComponent implementation to look like this:
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);
}
Notice again code that is very similar to what you built before, but that has been created by the designer. Unfortunately, for this process to work reliably, the designer is going to need complete control over the InitializeComponent method. In fact, notice that the Wizard-generated InitializeComponent code is wrapped in a region (which will hide the code by default), and marked with a telling comment:
/// Required method for Designer support - do not modify
/// the contents of this method with the code editor.
It might look like your favorite programming language, but InitializeComponent is actually the serialized form of the object model that the designer is using to manage the design surface. While you can make minor changes to this code, such as changing the Text property on the new button, major changes are likely to be ignored or worse, thrown away. We recommend putting custom form initialization into the form's constructor, after the call to InitializeComponent, giving you confidence that your code will be safe from the designer.
Of course, the transgression of the designer is balanced by the benefits it provides. For example, instead of writing lines of code to set properties on the form or the controls contained therein, you merely have to right-click on the object of interest and choose Properties (or press F4) to bring up the Property Browser for the selected object.
Any non-default properties, as indicated by values in bold, will be written into the InitializeComponent method for you, saving you the trouble of writing that code. Likewise, to choose an event to handle for the form or the form's controls, you can click the Events lightning bolt at the top of the Property Browser window.
Handling an event from the Property Browser window can be done in a few ways. One way is to find the event you'd like to handle on the object selected by clicking and typing in the name of the function you'd like to call when this event is fired, such as button_Click. Pressing Enter after inputting the name of the event handler will take you to the body of an event handler with that name and the correct signature, all ready for you to implement:
private: System::Void button_Click(Object* sender, EventArgs* e)
{
}
If, as is often the case, you'd like each event that you handle for each object to be unique or you just don't care about the name of the handler, you can simply double-click on the name of the event in the Property Browser and an event-handler name will be generated for you, based on the name of the control and the name of the event. For example, if you double-clicked on the Load event for the Form1 form, the event handler name would be Form1_Load.
Further, if you're handling the default event of an object, you can handle it simply by double-clicking on the object itself, which will generate an event handler name just as if you'd double-clicked on that event name in the Property Browser event list. The default event of an object is meant to be intuitively the most handled event for a particular type. For example, the default event for a button is Click and the default event for a form is Load. Unfortunately, neither the designer nor the Property Browser gives any indication what the default event will be for a particular type, but experimentation should reveal few surprises.
Arranging Controls
The beauty of the designer is that it lets you lay out your controls within your form, making sure everything lines up nicely (as shown in Figure 1), unlike when someone resizes it (as shown in Figure 2).
.gif)
Figure 1 Nicely laid out form at ideal size
.gif)
Figure 2 Nicely laid out form resized
The user isn't resizing the form so that they can have more gray space; they're resizing to get more room to enter data into the controls. For that to happen, the controls need to resize to take up the newly available space. This can be done manually by handling the form's Resize event and writing the code. Or, it can be done with anchoring.
Anchoring is one of the means that Windows Forms provides for automatic layout control of your forms and the controls contained therein. By default, all controls are anchored to the top-left so that as the form is resized and moved all controls are kept at their position relative to the upper left-hand corner of the form. However, in this case, it would be best to have the text box controls widen or narrow as the form is resized. This can be accomplished by setting each text box's Anchor property. Showing the Property Browser for a control and choosing the Anchor property displays an editor like that seen in Figure 3.
.gif)
Figure 3 Setting the Anchor property
To change the text boxes so that they anchor to the right-hand edge as well as the top and left edges is a matter of clicking on the anchor rectangle on the right, changing the Anchor property to Top, Left, Right. This will cause the text boxes to resize as the form resizes, as shown in Figure 4.
.gif)
Figure 4 Anchoring Text Boxes Top, Left, Right and Buttons Bottom, Right
While the default anchoring is upper left, those edges need not be a part of the anchoring settings at all. For example, notice that Figure 4 anchors the OK and Cancel buttons to the lower right, as is customary with Windows dialog boxes.
If instead of building a dialog box-style form, you'd like to build a Window-style form, anchoring is not going to be your best bet. For example, if you're building an Explorer-style application, with a menu bar and toolbar on the top, a status bar on the bottom and a tree view and a list view taking up the rest of the space as determined by a splitter between them, anchoring won't do. Instead, you'll want docking.
By default, a control has the Dock property set to None. You can change the Dock property in the Property Browser by picking a single edge to dock to or to take up whatever space is left.
For example, the Dock properties for a status bar, a tree view and a list view, might show the latter two being split with a splitter control, all arranged without your needing to write a line of code.
Anchoring, docking and splitting are not the only ways to arrange controls on a form. Windows Forms also supports grouping controls and handling custom layout for special situations. In addition, Windows Forms supports arranging windows within a parent, commonly called Multi-Document Interface (MDI).
Data Binding
Data binding is the ability to bind the contents of one or more controls to a data source so that when one is updated, so is the other. Not only is data binding well-supported in Windows Forms, it's fully integrated into Visual Studio .NET itself.
Dragging a table from Server Explorer onto a designer surface creates two components, a connection to connect to the database and an adapter to shuttle data back and forth across the connection. Right-clicking on the adapter in the designer and choosing Generate Dataset allows you to create a new dataset, a DataSet-derived class specially generated just to hold data for the table you dragged from the Server Explorer. The default General Dataset options will also create an instance of the new dataset for you to associate with controls.
Once you've got a source of data, you can bind the data to one or more controls. There are several data-bound controls provided with Windows Forms, including the ListBox, and ComboBox, but of all of them, the DataGrid is the most flexible.
Once you have a dataset on the form, all that's required to bind the data grid to it as a data source is to set the data grid's DataSource and DataMember properties in the Property Browser and to fill the dataset when the form is loaded:
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);
}
This is but a scratch on the surface of what can be done with data binding in general and the data grid specifically. See the References section that follows for pointers to more data-binding resources.
Moving from MFC
As MFC programmers, you will very likely have significant investments in your existing code base. Moving MFC code to the .NET Framework requires some careful planning. Here are some considerations:
- If you can afford to start over from scratch, that will yield the most maintainable code base, but will take the longest.
- If the bulk of your MFC code is in COM Controls (or can be moved to COM Controls), then you can use Windows Forms as a host for those controls and write new managed code for the framework.
- If you need to bring the MFC application itself forward, MFC 7.1 provides the ability to host Windows Forms controls as COM controls, while still leaving your MFC code unmanaged. See the References section for pointers to the specifics of this.
- If you'd like to use managed code in your Windows Forms application without the overhead of using COM Interop, you can flip the "Use Managed Extensions" bit on your MFC project and gain the ability to mix managed and unmanaged types together in the same code. You can see the References section for pointers to the specifics of this, too.
The options that apply to you depend on your specific circumstances, but in general, we recommend a strategy that lets you write the bulk of your new code for the .NET Framework, even if it means building some of the features for Windows Forms that you've come to know and love in MFC.
Conclusion
This article covers some of the most compelling of the new Windows Forms features, but there's more to Windows Forms, such as no-touch deployment, dialog box data exchange and validation, MDI applications, drawing and printing (including print preview), user controls, custom components and design-time support, manifest and typed resources, no-compile resource localization, and application settings, to name a few.
When it comes to comparing MFC and Windows Forms, it's important to remember that they were built at very different times to solve very different problems. MFC is a document-based application framework. Windows Forms is a windowing library for n-tier applications. It's not surprising that things are different between them. Some of those differences, like anchoring and docking, make Windows Forms better. Others, like object serialization, can be found elsewhere in the .NET Framework class library. Still others are just plain missing, which makes your work as a document-based application developer all the more interesting.
However, two things are quite clear; Microsoft is moving heavily in the direction of managed code, and the benefits of managed code are quite compelling. The .NET Framework provides automatic handling of both memory and security, resulting in more robust applications, as well as increased developer productivity. The .NET Framework class library provides a vast, rich and uniform class library. This combination is an improved and more productive way to build Windows applications.
References
Visual Studio .NET: Managed Extensions Bring .NET CLR Support to C++, Chris Sells, MSDN Magazine, July 2001.
Windows Forms: A Modern-Day Programming Model for Writing GUI Applications, Jeff Prosise, MSDN Magazine, Feb 2002.
.NET Delegates: A C# Bedtime Story, Chris Sells.
Cutting Edge: Data Binding Between Controls in Windows Forms, Dino Esposito, MSDN Magazine, Feb 2002
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, March 2003.
Introduction to Managed C++. Sam Gentile, O'Reilly OnDotNet, January 2003.