In the early '90s, many companies invested in traditional client/server
architectures by building fat-client applications with rich graphics that
offloaded legacy-system processing time.
Visual C++ programmers have a long history of developing GUI (graphical
user interface) applications for the Windows platform. For many of us, this
history began in the early days of Windows 2.0 and 3.0. The only way to
program GUI Windows applications back then was to use the C programming
language to access the Windows application programming interface (API). Our
guide through this territory was Charles Petzold's Programming Windows,
which showed us how to write 175 lines of C code to define a plain window
and create it!
Over the years, a variety of abstractions have been placed over this API
for UI programming, with varying degrees of usefulness. In the C++ world,
Visual C++ initially offered Microsoft Foundation Classes (MFC) based on
inheritance, which had a lot of power in many areas but were lacking in
others, as well as being complex for the average developer. MFC, although
popular, is really a thin wrapper over the Win32 API that doesn't provide a
lot of abstraction. Visual Basic offered a programming model based on events
and forms coupled with a visual RAD capability, simplifying UI programming
for the masses, but rendering itself extremely inflexible for nontrivial or
"large" tasks. We C++ programmers tended to view the VB model as a "dumbing
down" anyhow.
The visual editing and layout capabilities were impressive, but the lack
of explicit control and power were quite frustrating. Typically, the first
90% of a real-world VB application was easy, but the last 10% was hell. Many
other frameworks, such as WTL (Wrapper Template Language), were created to
provide the basic "plumbing" or template for a Windows UI application.
The problem is that all of these abstractions offered vastly different
programming models. A developer skilled in the use of one tool often
encountered a great deal of grief when switching to another. Frequently,
projects used more than one framework. Moreover, this type of programming
was still very tedious and error-prone, particularly because developers
still had to go down to the Windows API for some tasks, especially in Visual
Basic development. Perhaps it was time for something that combined the
visual rapid application development (RAD) of VB with the power of C++.
That something came in the summer of 2000, with the introduction of the
Windows Forms class library, itself a part of the impressive .NET Framework.
Much has been written about the clear benefits in developer productivity and
efficiency that come with developing managed applications with the .NET
Framework.
Windows Forms are a brand new managed platform that enables developers
to build rich user interfaces for Windows desktop applications in managed
languages like C#, MC++, and VB.NET. Because Windows Forms are part of the
.NET Framework itself, they can leverage much of the technology in the
framework, including the managed execution environment, the common type
system (CTS), the common application environment, and OO principles.
Managed C++ has been a first-class managed language since the early days
of the .NET Framework. The design of Managed C++ is quite impressive. Since
C++ predated the CLR and managed code, the Managed C++ team faced impressive
challenges in enabling C++, an existing and very popular computer language,
to emit managed IL, even with existing native C++ code. Their solution was
Managed Extensions for C++. Perhaps the most impressive feat of this
technology is IJW or "It Just Works," which allows to a large extent,
recompilation and running of the code as managed code. The problem, for
those of us choosing to use the Managed Extensions for C++ or MC++, has been
the lack of Windows Forms Designer support in the first shipping version of
Visual Studio .NET. Developers choosing to use MC++ had to develop Windows
Forms applications by hand, without the obvious visual layout and ease of
the VS.NET visual Windows Forms Designer. In this article, I will show you
how, with Visual C++ .NET 2003, Microsoft has provided the same benefits to
Managed C++ programmers that had previously been available only to Visual C#
.NET and VB.NET programmers, namely Windows Forms Designer support. In
addition, I will walk you through the basics of creating a Managed C++
Windows Form.
This article assumes that you are familiar with the basics of Windows
Forms from the C# perspective, and know the basics of Managed C++
programming. Also, please note that this article was written prior to the
final release of Visual Studio .NET 2003 and there may be slight changes,
although this appears unlikely at the time of this writing.
The Windows Forms Designer in VS.NET 2003
The Windows Forms Designer is a Visual Studio feature that enables
developers to lay out and design user interfaces based on Windows Forms
classes in the .NET Framework. In VS.NET 2002, only Visual C# and Visual
Basic were supported by this feature, which generates application code in
those languages. In VS.NET 2003, the Windows Forms Designer supports Visual
C++ by also generating Managed C++ code via the new Managed C++ CodeDOM. In
addition to the Windows Forms Designer, Visual Studio includes the Controls
Designer, the Components Designer, and the XML Schema Designer.
What does it take to have Managed C++ support in the Forms Designer?
Well, it turns out that there's another piece of .NET technology that the
forms designer depends on. The designer needs to generate code for all the
components that live on the form(s), and also needs to parse code as changes
are made to the UI. The .NET Framework technology for this is the CodeDom,
which lives in the System::CodeDom and System::CodeDom::Compiler namespaces. The CodeDom is a set of classes for representing most computer language structures, but in a
language-independent manner. Each language-specific CodeProvider is then
tasked with dealing with that language's nuances. The CodeDom is extremely
flexible and powerful.
In this context, the CodeDom has code generator implementations in the
Microsoft::CSharp and Microsoft::VisualBasic namespaces. The good news is
that Managed C++ now has a CodeDom parser, named MCppCodeDomParser.dll, and
a CodeDomProvider, MCppCodeDomProvider.dll. As of this writing, these two DLLs were not located in the same place as the C# and VB versions, but in C:\Program Files\Microsoft
Visual Studio .NET 2003\Common7\IDE\PublicAssemblies.
The designer uses the CodeDomProvider to generate Managed C++ code.
Let's look at an example.
Creating a Managed C++ Windows Form Project
Microsoft Visual C++ .NET 2003 now includes the ability to create
Windows Forms Applications. This is reflected in the Visual C++ New Project
dialog as shown in Figure 1.
The first thing that you will notice after running the New Project
wizard for an MC++ Windows Forms application is that the designer operates
on and generates code in the header file (.h). The wizard also generates a
.cpp file, but as you add UI elements and event handlers, the forms designer
will place the code in the header file. This may seem like an odd choice,
but you must remember two things. First, the C++ designers had to fit within
the existing architecture of VS.NET and its support for "single-file"
languages like C# and VB.NET. Second, C++, as a language, is all about
separating implementation and definition. It's too early to speculate, but
there are some indications that this may change in a future version.
The wizard does generate the code for the WinMain() function in the .cpp
file. We will look at that code shortly. First, let's look at the code
generated by the designer in the header file.
The Managed C++ Implementation of a Windows Form
Let's examine the code in Listing 1. For those who have looked at
Windows Forms code generated by the C# or VB.NET wizards, this code looks
very similar. The class is contained within a namespace, ManagedCPPWinForm,
and using the namespace declarations at the top does the same thing as using
directives in C#; namely bringing the type names in the namespaces into the
current scope. Of particular importance is the System::Windows::
Forms namespace, which contains almost all of the functionality of Windows
Forms, such as forms, containers, and controls.
In every Windows Forms application, you will have a class derived from
System::Windows::Forms::Form.
public __gc class Form1 : public System::Windows::Forms::Form
The Form class is derived from Control, which is derived from Component.
A form is both a component and a container of components. It is also a
container of controls.
Examining the class declaration, notice that Form1 is a managed class
signified by the __gc modifier, which means that this a class subject to
garbage collection and its lifetime is controlled by the CLR. The instance
of this class is the application's main window.
The Form class is the cornerstone of the application and provides rich
functionality. As I have mentioned, a form is a container of components,
which we see in the private member variable, components:
private:
/// <summary>
/// Required designer variable.
/// </summary>
System::ComponentModel::Container * components;
The designer uses this variable in the InitializeComponent() method to
build the form at runtime. All of the controls and properties that you set
during design time are set at runtime in this method. Any time you change
the form in Design Mode, the designer will update this method, as shown in
Listing 2.
The Message Pump
The other important piece that is needed for every Windows Forms
application is the class System::Windows::Forms::Application. In this class is a static method called Run that drives the Windows Forms application by providing a message pump. The message pump is not something you see. The .NET Framework abstracts it away
so you don't need to worry about it. In Managed C++, the code for WinMain()
looks like Listing 3.
A Window
This is enough code to display a working Windows Form on the desktop.
When built and run, it produces a window similar to that shown in Figure 2.
Obviously, from here, there is much more that can be done. The
developer can drag-and-drop controls from the toolbox and write code, but
the basic idea here is that it is now possible to have visual editing
capabilities with Managed C++.
Conclusion
I have taken you on a historical tour of C++ based GUI development,
leading up to Windows Forms with Managed C++. In the first version of
VS.NET, Managed C++ lacked the visual designers, leading to hardship for
Managed C++ developers. As I have shown you in this article, Visual Studio
.NET 2003 and Visual C++ .NET 2003 now provide visual designers and Managed
C++ code generation, enabling RAD GUI development in Managed C++. Coupled
with the power of Managed C++ itself to perform functions not available in
other CLR languages, this is a significant and welcome addition. In a future
article, I will go into detail about some of these unique abilities of
Managed C++.
About The Author
Sam is a .NET consultant, author, and speaker who has been working with .NET
since the alpha and currently works with future versions of the .NET
Framework, as well as future Microsoft products. He is a coauthor of Wrox's
Visual C++ .NET: A Primer for C++ Developers, and is currently at work on a
.NET book for O'Reilly. Sam maintains a very popular Web site:
www.samgentile.com.
managedcode@attbi.com
Listing 1: The code generated by the Managed C++ Forms Designer.
#pragma once
namespace ManagedCPPWinForm
{
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
///
/// WARNING: If you change the name of this class, you will need to
change the
/// 'Resource File Name' property for the managed resource
compiler tool
/// associated with all .resx files this class depends on.
Otherwise,
/// the designers will not be able to interact properly with
localized
/// resources associated with this form.
/// </summary>
public __gc class Form1 : public System::Windows::Forms::Form
{
public:
Form1(void)
{
InitializeComponent();
}
protected:
void Dispose(Boolean disposing)
{
if (disposing && components)
{
components->Dispose();
}
__super::Dispose(disposing);
}
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 = S"Form1";
}
};
}
Listing 2: Updating InitializeComponent()
void InitializeComponent(void)
{
this->components = new System::ComponentModel::Container();
this->Size = System::Drawing::Size(300,300);
this->Text = S"Form1";
}
Listing 3: WinMain()
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;
}
All Rights Reserved
Copyright © 2004 SYS-CON Media, Inc.
E-mail:
info@sys-con.com