Recommended Books



ASPAlliance.com Sample Book Chapters

Hour 3: Writing a Simple C++ .NET Program

Inevitably every programming book and programming course has you create as your first application—a simple "Hello World" program. Therefore, this hour's lesson is dedicated to writing your first simple application with Visual C++ .NET.

The difference is, this lesson already assumes you know how to create a simple application and are at least somewhat experienced with the previous version of Visual C++. With that assumption, the lesson will take you through creating two applications: one with MFC and one using the .NET Framework. This allows you to compare the two methods of programming Windows applications with Visual C++ .NET.

In this hour you will learn:

  • How to build a simple application with MFC

  • How to build a managed application with the .NET Framework

  • How to study and understand the differences between the two types of applications

Building an MFC Application

Building an MFC application with Visual Studio .NET is very similar to the way it was done with previous versions of Visual Studio. The Application Wizard is available to allow you to customize your settings, although it has a different look.

As usual with Visual Studio, first select the New, Project menu option to display the New Project dialog, as shown in Figure 3.1. Select the MFC application type and name the application HelloMFC.

Figure 3.1 New Project Window in Visual Studio .NET for an MFC application.

Pressing the OK button in the New Project dialog brings up the MFC Application Wizard, shown in Figure 3.2. This wizard is no longer a linear process; you can now select the different sections on the left side of the wizard page and directly access the settings you want to change. The differences between this and the Application Wizard for MFC applications in the previous version of Visual Studio are mainly visual and navigational.

Select the Application Type section in the wizard and change the settings to match those shown in Figure 3.3. These selections will result in creating a dialog-based application when you click the Finish button.

The first thing you will see after dismissing the settings dialog is the dialog editor. If your dialog requires any controls, you can easily select them from the toolbox displayed on the left. For this project, however, we are just going to change the controls that are already present.

Figure 3.2 The MFC Application Wizard.

Figure 3.3 Dialog-based application settings for the HelloMFC application.

First of all, select the button labeled OK. On the right side of the IDE, you should see a window titled Properties. This is a departure from the old way of doing things in Visual C++, which required editing properties through small tool windows. Properties are now categorized and always displayed without having to select a menu item or click a toolbar button. Change the property value labeled Caption to Message by typing the new value in and pressing Enter. You should see the text on the button change as you do this.

Now you need to change the control ID of the static text control. Select the static text control located in the middle of the dialog in the dialog editor and change the ID property to the value IDC_MESSAGE.

Figure 3.4 HelloMFC dialog template.

Assign a member variable to the static text control by right-clicking the control and selecting Add Variable from the context menu. The Add Member Variable Wizard, shown in Figure 3.5, is displayed. Edit the member variable properties, as shown, and finish the wizard.

Figure 3.5 The Add Member Variable Wizard for the MFC application.

One final step in the dialog editor is to double-click the Message button to add a handler for when the button is clicked. This is a nice new feature added to Visual Studio .NET. When you double-click objects within the dialog editor and other form editors, the most appropriate message map entry and method definition is added to your application. In this case, the ON_BN_CLICKED message map entry and the CHelloMFCDlg::OnBnClickedOK() method are added. Because this button is the OK button, you could have overridden the OnOK() method to handle the selection; however, this method works for all buttons and other control types.

Change the OnBnClickedOK() method to what is shown in the following code to set the message text to a message:

void CHelloMFCDlg::OnBnClickedOk()
{
  m_MessageST.SetWindowText("Hello from the world of MFC.");
}

Compile and run the HelloMFC application by selecting the Debug, Start menu item from the menu bar or by pressing the F5 key. Clicking the Message button within the application displays your message, as shown in Figure 3.6.

Figure 3.6 Executing the HelloMFC application.

If you are already familiar with building MFC applications in previous versions of Visual Studio, the HelloMFC application should have been simple for you to create within Visual Studio .NET. What you've created, however, is an unmanaged C++ project. This application has nothing to do with .NET and, as such, does not run within the .NET runtime. Any memory allocation, versioning issues, and other things that benefit .NET applications will have to be done manually using this type of project. To run in the .NET runtime, you need a managed C++ application.

Building a Managed .NET Framework Application

Switching gears to the .NET Framework and building the same type of application as a managed C++ application is a bit different, as you will see. The first step is the same: Select to build a new project from the main menu, only this time select Managed C++ Application as the type and name the application HelloNET.

Notice that you don't receive any wizard to ask what settings you would like to have in your application, as you did with the MFC application. The C++ .NET applications are generated with a script that builds a basic application framework that is a console "Hello World" application. One difference between Visual C++ .NET and the other .NET languages is that there is no form editor for Visual C++ .NET to edit Windows Forms, the basis of building user interfaces in .NET. Although it may seem daunting at first having to hand-code the user interface, you'll find that working with the .NET Framework Forms classes is rather intuitive.


Tip -

If you want to learn C# also, you can actually use a dummy application in C# that does provide the Windows Form editor to build your forms and then port the code into VC++ .NET. You have to know how to do the porting, but it isn't that difficult with a little practice—and it saves time on designing your forms.


The next step is to declare a class that represents the form for the application. Select Project, Add Class from the main menu. In the list of available templates in the dialog that is displayed, select the Generic C++ Class template and click the Open button. Name the class CHelloNETForm and specify the base class Form, the .NET Framework's class found in the System::Windows::Forms namespace, which provides the Windows Form functionality. You may get an error message explaining that Visual Studio .NET cannot find the Form base class. Click Yes to continue adding the class. Performing the previous steps sets up a new file and skeleton code for your class. The next steps will transform your generic class into a managed C++ .NET class.

Click the Class View tab next to the Solution Explorer tab on the right side of the IDE window. Expand the project tree and locate the Classes item and expand that also. You should now see your CHelloNETForm class. Right-click the class and select Add, Add Variable from the context menu. You will now be presented with the Add Variable dialog. Set the access to protected, the variable type to Button*, and the variable name to m_pbtnMessage. Then click Finish. Add another Button* variable named m_pbtnDone and a Label* variable named m_pstMessage.

The variables you just added are .NET Framework classes within the Forms namespace. Controls, however, need to have a container object to hold them. Add another variable of type System::ComponentModel::Container* with the name m_pComponents and the same protected access level.

Now that you've added the member variables, its time to add some member functions. Right-click the CHelloNETForm class again, but this time select Add, Add Function. Enter void as the return type, InitForm as the function name, and an access level of protected. Click Finish to close the dialog.

The last step to finish the design of the class is to add the necessary elements that are not supported by wizards within the IDE. Using Listing 3.1 as a guide, add the appropriate using statements and add the __gc keyword immediately preceding the class keyword. This indicates to the compiler that the class is managed by the .NET Framework and its memory manager.

When you are finished, you're HelloNETForm.h file should look similar to Listing 3.1.

Listing 3.1 Transforming a Generic C++ class into Managed Code

 1: #pragma once
 2:
 3: #using <mscorlib.dll>
 4:
 5: #using <System.DLL>
 6: #using <System.Drawing.DLL>
 7: #using <System.Windows.Forms.DLL>
 8:
 9: using namespace System;
10: using namespace System::Windows::Forms;
11:
12: __gc class CHelloNETForm : public Form
13: {
14: public:
15:  CHelloNETForm(void);
16:  ~CHelloNETForm(void);
17: protected:
18:  Button* m_pbtnMessage;
19:  Button* m_pbtnDone;
20:  Label*  m_pstMessage;
21:  System::ComponentModel::Container* m_pComponents;
22:  void InitForm();
23: };

Now it's time to fill in the functions you just created. Refer to Listing 3.2 as you read through this section. To begin with, try and compile your project. One thing you'll notice is that the compiler complains that NULL is undefined. One great addition to Visual C++ .NET is that when you're adding member variables to a class like we did earlier, the generated code also includes class initializers for these variables. In this case, because we declared pointers, the generated code set these variables to NULL. However, it didn't add the appropriate #include statement. To fix this problem, include the header file stdlib.h at the top of the HelloNETForm.cpp file. Your program should now compile with zero errors and zero warnings.

Begin by filling in the constructor code. Because the member variables you added are simply pointers, you need to create the objects before you can use them. Therefore, create the form control container member variable (m_pComponents) by using the C++ keyword new. The remaining line in the constructor calls the InitForm function.

In the InitForm() function, create the three controls, one by one, and set the properties appropriately. After all the controls are created, they are added to the form with the Form::Controls->Add() method. The order in which the controls are added has an effect on the tab order if the tab order isn't specified for the controls. In this case, it is specified and therefore doesn't matter.

Listing 3.2 Creating the Form

 1: #include "stdafx.h"
 2: #include "hellonetform.h"
 3: #include <stdlib.h>
 4:
 5: #using <mscorlib.dll>
 6: CHelloNETForm::CHelloNETForm(void)
 7: : m_pbtnMessage(NULL)
 8: , m_pbtnDone(NULL)
 9: , m_pstMessage(NULL)
10: , m_pComponents(NULL)
11: {
12:  m_pComponents = new System::ComponentModel::Container();
13:
14:  // Initialize the Form
15:  InitForm();
16: }
17:
18: CHelloNETForm::~CHelloNETForm()
19: {
20: }
21:
22: void CHelloNETForm::InitForm()
23: {
24:
25:  // Allocate the controls
26:  m_pbtnMessage = new Button();
27:  m_pbtnDone  = new Button();
28:  m_pstMessage = new Label();
29:
30:  SuspendLayout();
31:
32:  // Initialize all control properties
33:  //
34:  // Message Button
35:  //
36:  m_pbtnMessage->Location = System::Drawing::Point(240, 8);
37:  m_pbtnMessage->Name = "Message";
38:  m_pbtnMessage->TabIndex = 0;
39:  m_pbtnMessage->Text = "Message";
40:  m_pbtnMessage->add_Click(
41:   new System::EventHandler( this, &CHelloNETForm::OnMessageClick ) );
42:
43:  //
44:  // Done Button
45:  //
46:  m_pbtnDone->Location = System::Drawing::Point(240, 40);
47:  m_pbtnDone->Name = "Done";
48:  m_pbtnDone->TabIndex = 1;
49:  m_pbtnDone->Text = "Done";
50:  m_pbtnDone->add_Click(
51   new System::EventHandler( this, &CHelloNETForm::OnDoneClick ) );
52:  //
53:  // Message Label
54:  //
55:  m_pstMessage->Location = System::Drawing::Point(8, 8);
56:  m_pstMessage->Name = "Label";
57:  m_pstMessage->Size = System::Drawing::Size(192, 40);
58:  m_pstMessage->TabIndex = 2;
59:  m_pstMessage->Text = "";
60:
61:  // Set form properties and add controls to form
62:  //
63:  // Form1
64:  //
65:  AutoScaleBaseSize = System::Drawing::Size(5, 13);
66:  ClientSize = System::Drawing::Size(322, 87);
67:  Controls->Add( m_pstMessage );
68:  Controls->Add( m_pbtnDone );
69:  Controls->Add( m_pbtnMessage );
70:
71:  FormBorderStyle = System::Windows::Forms::FormBorderStyle::FixedDialog;
72:  Name = "HelloNET";
73:  Text = "HelloNET";
74:
75:  ResumeLayout(false);
76: }

In order for your form to respond to user events, you'll need to capture the button click events. Events that were previously handled with the MFC message map are handled quite differently in the .NET Framework. Events are handled by delegates within your class. A delegate is quite similar to a C/C++ function pointer. You assign delegates to handle object events with the following statement:

m_pbtnMessage->add_Click(
   new System::EventHandler( this, &CHelloNETForm::OnMessageClick ) );

Every time the Message button is clicked, the CHelloNETForm::OnMessageClick() method is called. In your project, you will add two event handlers to the two buttons.

Event handlers must have a specific set of parameters, much in the same way they did for MFC message map handlers. For most events, pointers to a generic Object and an EventArgs object are passed as parameters. The generic Object pointer is a pointer to the object that caused the event, whereas the EventArgs object contains information specific to the event. Following the instructions given earlier, add two member functions. The first function is named OnMessageClick, and the second function is named OnDoneClick. Both functions have a void return type, protected access level, and two parameters: Object* source and EventArgs* e. Listing 3.3 shows the final version of the HelloNETForm.h file.

Listing 3.3 Adding Event Handler Declarations

 1: #pragma once
 2:
 3: #using <mscorlib.dll>
 4:
 5: #using <System.DLL>
 6: #using <System.Drawing.DLL>
 7: #using <System.Windows.Forms.DLL>
 8:
 9: using namespace System;
10: using namespace System::Windows::Forms;
11:
12: __gc class CHelloNETForm : public Form
13: {
14: public:
15:  CHelloNETForm(void);
16:  ~CHelloNETForm(void);
17: protected:
18:  Button* m_pbtnMessage;
19:  Button* m_pbtnDone;
20:  Label*  m_pstMessage;
21:  System::ComponentModel::Container* m_pComponents;
22:  void InitForm();
23:  void OnMessageClick( Object* source, EventArgs* e);
24:  void OnDoneClick( Object* source, EventArgs* e);
25: };

Now open the HelloNETForm.cpp file. The two functions you just added will be at the bottom of the file. In OnMessageClick, set the m_pstMessage Text property to "Hello from the .NET World." For the OnDoneClick function, call the function Close, which shuts down the application. The two functions should appear similar to the following:

1: void CHelloNETForm::OnMessageClick( Object* source, EventArgs* e )
 2: {
 3:  m_pstMessage->Text = "Hello from the .NET World.";
 4: }
 5:
 6: void CHelloNETForm::OnDoneClick( Object* source, EventArgs* e )
 7: {
 8:  Close();
 9: }

The final change to the application is to have the main() function create and run the new Windows Form class. Make the changes shown in the following code segment:

#include "helloNETform.h"
int _tmain(void)
{
  Application::Run(new CHelloNETForm());
  return 0;
}

With those final additions, the application is ready to compile and run. This is the same as with the MFC application. Simply press the F5 key or select the Debug, Start menu command. The resulting Visual C++ .NET application should have the same appearance as the MFC application created earlier.

Comparing the Differences

Although the process of building the MFC application was more automated with wizards and form designers, the resulting code of the two applications shows that the MFC application is much more complex. If you take into consideration that the .NET application has no resource file to describe the Windows Form and remove the form definition from the comparison, the .NET application is significantly smaller and even easier to read.

Looking at a few of the major differences between the applications shows a fundamental distinction in the way a .NET application is developed versus how an MFC/Win32 application is developed.

The first main difference is that an MFC Windows application always starts with a CWinApp derivative. The InitInstance() method is overridden and provides the startup initialization for the application. By contrast, the .NET application doesn't require an application class. The .NET application's entry point is the main() function, whereas an MFC/Win32 application's entry point is a WinMain() function encapsulated within the MFC library.

Another major difference you should notice while looking at both applications is that the .NET application does not delete anything it allocates with new. This is because the .NET Framework frees all objects once they are no longer referenced. This is done by the garbage collector automatically. This eliminates the problems of memory leaks in your applications.

Finally, the way that events are handled is quite different between the two applications. With MFC, a message map entry is added to the class's message map, which maps an event or Windows message to a class function. In the .NET Framework, there is no message map; therefore, each object has events associated with it to which you can attach an event handler that is called when the events occur.

For a further comparison of the two applications, look at the code for the applications on the accompanying CD and compare them in more detail. In the end, you should find the .NET application cleaner and easier to work with than the MFC application.

Summary

In this hour you created two applications that perform the same task. One application was created with MFC and the other with the .NET Framework. Both application display a hello message when a push button is clicked. You also learned about the differences between the two application implementations.

Being familiar with how things are implemented in .NET as compared to MFC will help you in making better decisions when the time comes to convert legacy applications to the .NET platform.

Q&A

    Q Is using Windows Forms the only way to produce a user interface within .NET?

    A Windows Forms represent windows of all types, not just dialogs. They can be MDI frame windows, pop-up windows, tool windows, and so on. They are the only means within the .NET Framework to represent a Windows-based user interface.

    Q Is it possible to create a dialog in MFC and use it from a .NET application?

    A Yes, it is possible to do so. In fact, in Hour 5, "Understanding Managed Versus Unmanaged Code," there is a lesson on mixing managed (.NET Framework) code with unmanaged (MFC/Win32) code that will help you understand how this is done.

Workshop

The Workshop provides quiz questions to help solidify your understanding of what was covered in this chapter. Answers are provided in Appendix A, "Quiz Answers."

Quiz

  1. What is the base class for a Windows Form?

  2. How is a delegate related to an event?

  3. How are events different in MFC versus the .NET Framework?


sponsor links - buy

Submit a chapter

Are you an author or publisher? Interested in seeing a sample chapter of your book on the ASPAlliance.com website? Click here to list your sample chapter.