Programming for Windows forms using C ++ managed extensions

Source: Internet
Author: User
Tags mscorlib object serialization
Introduction
Programmers have long used C and C ++ to develop Windows GUI applications. For many of us, this history dates back to the Windows 2.0 era, when we used the C-based 16-bit Windows API, and even just displaying a window required writing dozens of lines of code. Fortunately, over time, the level of abstraction gets higher and better. In 1992, Microsoft released Programmer's Workbench, which includes version 1.0 of the Microsoft Foundation Class Library. The Microsoft Foundation Class Library version 1.0 contains approximately 60 classes that wrap the 16-bit Windows API for the windowed programming and drawing parts. As of 2002, version 7.0 of the Microsoft Foundation Class Library has grown to more than 200 classes, and its use has expanded to provide a complete C ++ object model alternative to the Microsoft Win32 API.

Although MFC is very powerful, it also has many shortcomings. For example, it is just a thin layer outside the Win32 API, and it is too complicated for many programmers to use effectively. In general, to develop Windows applications, direct access to the Win32 API is still required, especially as the requirements for the basic functions required by Windows applications continue to increase. Therefore, developing any truly powerful Windows GUI application requires a lot of time and effort. In response to the increasing difficulty of application development, Microsoft released a new programming environment for the Windows platform in early 2002. The environment is called the .NET Framework, and it provides developers with a hosted application runtime and a number of libraries called .NET Framework class libraries. The .NET framework can manage memory and security, which can lead to more reliable applications. The .NET Framework Class Library provides a large, resource-rich, and unified class library. Any .NET language (including the managed extension of C ++ and managed version of C ++ provided by Micrisoft for .NET programmers) can be equivalent in the same way To access the library. As part of the .NET framework, Windows Forms is a set of classes for building client-side Windows GUI applications.

In this article, we will take a deeper look at how to write Windows Forms code using C ++ 's managed extensions. We first show you how to write from scratch, then explain how to do this using Microsoft Visual Studio .NET 2003. In the meantime, we will focus on some common features of Windows Forms, such as auto layout and data binding. Finally, we will focus on how Windows Forms compares to MFC and how to mix these two tools as we use managed extensions further.

Back to top
What is a Windows Form?
Windows Forms is a windowing toolkit, not a complete application framework like MFC. In fact, MFC offers more than the functionality provided by Windows Forms for building standalone document-based applications. For example, if you want to build a text editor, in MFC, you can simply run a wizard, select the appropriate options, and write a few lines of code. The application just obtained by running the wizard contains a status bar, a toolbar (floating), and implements all the File, Edit, and Help menu items, including a list of recently used files and printing, and context-sensitive Help, all of which is contained in a fully logo-compatible single document interface (SDI), multiple SDI, or multiple document interface (MDI) application. As a document-based application framework, there is no competitor that can compete with MFC.

However, programmers now tend to build more HTML-based applications, or applications that can communicate with business objects or database servers, rather than document-based applications. The .NET framework and Windows Forms are tailored for this purpose.

This is not to say that Windows Forms cannot be used to build great document-based applications. In fact, because Windows Forms is only a small part of the more than 2000 classes provided by the .NET framework, it is likely that what you need is not provided by Windows Forms, but is located elsewhere in the framework. For example, Windows Forms itself does not provide any object serialization support, but the rest of the .NET Framework class library provides multiple ways to serialize object graphs.

This is the main difference between MFC and Windows Forms. MFC is designed to replace the underlying Win32 API, but this does not stop the Win32 API from growing. In fact, just like MFC has grown over time, the functionality of the base OS has increased at least tenfold. However, Windows Forms is only a replacement for the windowed part of Win32. The rest of the .NET framework classes are responsible for replacing the rest of Win32. Of course, the framework can never replace the entire Win32 API, but since most of the new features to be added to Windows will be added to the framework in the foreseeable future, replacing the entire Win32 API will be a future goal.

Therefore, although Windows Forms cannot have all the features of MFC, it does provide a strong set of features that can greatly facilitate client application developers, including some features that MFC does not have at all. Next, we'll show you how to build an application from scratch and then explain the productivity improvements that Visual Studio .NET 2003 provides for Windows Forms C ++ programmers.

Back to top
Create a Windows Form from scratch
A typical Windows Forms application has at least one form. A form is a window, the Microsoft user interface unit that we have seen since Windows 1.0. Generally, one form in a Windows Forms application is the main form, meaning that it is the parent or owner of all other forms that may be displayed during the life cycle of the application. This form is where the main menu and toolbar, taskbar, etc. are displayed. When the main form ends, the application also exits.

The main form of the application can be a simple message box, a dialog box, an SDI window, an MDI window, or more complex controls. Applications such as Visual Studio .NET have multiple child windows and tool windows And floating toolbar form.

If your application is extremely simple, you can implement it with simple message boxes that flood any windowing system:

#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 a C ++ programmer, you must be very familiar with the WinMain entry point, which is still required in managed applications. In our first Windows Forms application, the only line of actual code called the static Show method of the Windows :: System :: Forms :: MessageBox class, which is actually just calling the name contained in Window :: Systems :: Forms Long-form version of the static method of the MessageBox class in space. Namespaces are widely used in the .NET Framework class library to separate classes, structures, enumerations, and other types into different logical groups. Because there are thousands of Microsoft employees working on adding the .NET Framework class library, hundreds of third-party organizations want to extend the class library, and millions of programmers are trying to learn it, so this This kind of separation is very necessary. Without a namespace, complex conventions are needed to uniquely name various objects (like the existing Win32 API).

The definitions of the types in the namespace are in .NET assemblies. An assembly is a managed type container that is packaged as a DLL or EXE. The #using directive is used to apply the types in an assembly to your application. For example, the mscorlib and System assemblies provide basic .NET framework types such as int and string. The System.Windows.Forms assembly contains Windows Form types.

After you use #using to apply the assembly to your application, you can reference the type in the long case described earlier, or you can use the standard C ++ using namespace statement to omit the task of typing code:

#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 example effectively demonstrates the most basic .NET framework and the concept of managed extensions for C ++, it does not do a good job of demonstrating typical Windows Forms programs. For a real application, you will need an instance of the Form class (or a class derived from Form) as follows:

void __stdcall WinMain (HINSTANCE hInstance, HINSTANCE hPrevInstance, long lpCmdLine, int nCmdShow) {Form * form = new Form (); ...}
The form variable refers to an instance of the managed type. Managed objects are handled by the Common Language Runtime (CLR) of the .NET framework, and their life cycle is controlled by a garbage collector that deallocates memory at a certain time. As a result, C ++ programmers don't need to explicitly delete managed types, but they also can't count on destroying objects at any particular moment, such as when scope is turned off.

After you create the form, you need to display it. If you've ever seen the documentation for a Windows Form, you may have noticed the Form method Show, which means:

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.}
Although the above code can display the form, you must be quick to see the form because Show shows the form in a modeless manner. This means that immediately after Show displays the new form on the screen, control is returned to the Main function, which exits the process when it returns, and closes the form that was just displayed. To display the form in a modal manner, the documentation suggests using 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.}
Although this code can actually display an empty form and return control to the Main function after the user closes it, you usually don't write such code. Instead, you designate a form as the main form so that other parts of the application can access it as the main form. To do this, you can pass the main form as a parameter to the Run method of the Windows Form's 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 static Run method of the Application class displays the main form and starts sending Windows messages until the main form closes. After the main form is closed, Run will return and let our Main function exit to end the process. To actually see this process, you can compile this little Windows Forms application using the following command line:

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

Now that the compiler has generated MyFirstApp.exe, you can execute it. When you close the form, MyFirstApp.exe will exit, ending your first Windows Forms application.

To add some fun, you can set a property on the new form. Like most objects in the .NET Framework class library, Form objects have properties that can be accessed, methods that can be called, and events that can be handled. You can set properties directly on an instance of the Form class, but usually we don't. Instead, each custom form is a class derived from Form and initializes its own properties, as shown below:

__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 C ++ managed extensions, custom managed types are declared using the __gc modifier. Note that the MyForm class is derived from Form and then initializes its own properties in the constructor. This provides a good usage model, as shown in the new Main function, which creates an instance of the MyForm class.

But this form is still tedious. Except for the features provided by the system, it does not have any interactive functions. We can add some interactive features 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 a form is to create a new Button object, set the properties we need, and add it to the list of controls managed by the form. Although the above code will generate a button on the form that looks like it was clicked after clicking on it, nothing else will happen because you haven't handled the button's click event. You can handle this event like this:

__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 click event of a button involves two tasks. The first is to create a handler method with the appropriate signature, which we will call button_Click. The signature of most .NET framework events is a function that returns nothing, but gets two parameters: an object representing the sender of the event (button in this example), and a System :: EventArgs object (Or an object derived from the EventArgs class).

The second item required to subscribe to an event is a line using the + = operator in the MyForm constructor. This notation means that you want to add a method to the list of all other methods related to a specific event on a specific object. This requires an instance of the EventHandler delegate object. A delegate is a class that translates a call to an event into a call to a method that subscribes to the event.

Note that the Click event on the Button class is a reference to the EventHandler delegate, so to add your own method to the subscriber list, you also need to create an instance of that kind of delegate. Of course, figuring out the delegate signatures for all the events you are interested in, or manually adding code to add controls to a form, can quickly become tedious. Fortunately, this can also be avoided because Windows Forms for C ++ is integrated in Visual Studio .NET 2003.

Back to top
Create a Windows Form with Visual Studio .NET Designer
Prior to Visual Studio .NET 2003, only C # and Visual Basic .NET had Visual Forms Designer support. C ++ 's managed extensions don't. Fortunately, Visual Studio .NET now ships with the C ++ managed extensions Windows Forms Designer.

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, you can choose the project name and location at will, and you will get a blank form in the designer.

In MFC, drag-and-drop design and user interface layout are only supported for dialog boxes. Normal views must be laid out in code. However, Windows Forms treats all forms in a uniform way, so the same drag-and-drop designer works for all forms. The type of form (modal or modeless, dialog box or view) depends on how it is used, not how it is designed.

The next big difference between Windows Forms and MFC in the designer is that the designer saves control and layout information in code rather than in separate resource files. This is very different from MFC, which saves the dialog layout information in the Win32 dialog resource in an .rc file. Both schemes have advantages and disadvantages, but MFC programmers will soon notice the differences. Adaptation requires a process.

Another thing you should note is that the C ++ managed extension project wizard generated the implementation code into a .h file instead of a .cpp file. This may be because the C ++ designer must adapt to the existing designer architecture and not Visual Studio .NET 2002, which only supports languages such as C # and Visual Basic .NET, which do not split declaration and definition code. The generated header file (which can be accessed by right-clicking the design surface and selecting View Code or by pressing F7) is as follows:

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 the above code should be familiar to everyone, including the using statement at the top and a custom form derived from the Form base class. The only difference from your implementation is that you call InitializeComponent in the form's constructor to set the properties of the form, not in the constructor itself. This is done to give the Windows Forms designer a place to place the code that initializes the controls on the form and the form itself.

The Windows Forms application project template produces 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;}
The above code also looks familiar. The Main function provides the entry point of the application and a call to Application :: Run, passing in an instance of the main form class. The ApartmentState variable is set to single-threaded apartment (STA) for proper lazy initialization of COM to be used in a user-friendly manner in drag-and-drop operations, and to host COM controls.

Returning to the InitializeComponent implementation, you can see that the form designer placed the code there. For example, dragging a button from the toolbox to the form's design surface will change the InitializeComponent implementation to the code shown below:

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);}
Note that the code above is similar to the code you generated previously, but they are created by the designer. Unfortunately, for this process to work reliably, the designer needs full control over the InitializeComponent method. In fact, you can see that the InitializeComponent code generated by the wizard is wrapped in an area (the code is hidden by default) and is descriptive:

/// Required method for Designer support-do not modify /// the contents of this method with the code editor.
This may seem like your preferred programming language, but InitializeComponent is actually a serialized form of the object model that the designer uses to manage the design surface. Although you can make some minor changes to these codes, such as changing the Text property of the new button, major changes may be ignored or even deleted. We recommend that you place your custom form initialization after the call to InitializeComponent in the form's constructor to ensure that the designer does not break your code.

Of course, the benefits of the designer offset its invasion of code. For example, you don't need to write multiple lines of code to set the properties of a form or the controls it contains. You just need to right-click the desired object and select Properties (or press F4) to bring up the property browser for the selected object .

All non-default properties (represented as values in bold) are written to you by the InitializeComponent method, and you don't need to write this code anymore. Similarly, to select events for a form or form's controls for processing, click the Events lightning-bolt icon at the top of the property browser window.

In the property browser window, event handling can be implemented in a number of ways. One method is to find out the event of the selected object to be processed, such as button_Click, by clicking and typing the name of the function to be called when the event is fired. Pressing Enter after entering the name of the event handler will take you to the body of the event handler with that name and the correct signature, which you can immediately start implementing:

private: System :: Void button_Click (Object * sender, EventArgs * e) {}
If, as usual, you want each event to be unique for each object, or you don't care about the name of the handler, just double-click the name of the event in the property browser, and the name of the control and event will be You generate an event handler name. For example, if you double-click on the Load event of a Form1 form, the event handler name will be Form1_Load.

In addition, if you want to handle the object's default event, you can double-click on the object itself to process it-this will generate an event handler name just like you double-click on the event name in the event list of the Property Browser. An object's default event refers to the most commonly handled event of a particular type intuitively. 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 suggests what a particular type of default event is, but in practice there are usually not many exceptions.

Back to top
Arranging controls
The benefit of the designer is that you can arrange the layout of the controls within the form, ensuring that all elements are neatly arranged (shown at 1), unlike the state when resized (shown at 2).

Figure 1 An ideally sized and neatly laid out window

Figure 2 The neatly laid out form is resized

Users do not resize the form to have more gray space, they are to allow more space for data entry into the control. To do this, the controls need to be resized to take up the new available space. This can be done manually by handling the form's Resize event and writing code. Alternatively, it can also be achieved by positioning.

Positioning is a method provided by Windows Forms for automatic layout control of forms and controls contained in forms. By default, all controls are positioned to the upper left so that when the form is resized and moved, all controls will maintain their position relative to the upper left corner of the form. However, in this case, it is best to widen or narrow the text box controls when resizing the form. This can be achieved by setting the Anchor property of each text box. Display the control's property browser and select the Anchor property. The editor shown in Figure 3 is displayed.

Figure 3 Setting Anchor Properties

To change the text box to be positioned to the right and top and left edges, simply click the positioning box on the right and change the Anchor properties to Top, Left, and Right. This will cause the size of the text box to change with the size of the form, as shown in Figure 4.

Figure 4 Positioning the text box to the top, left, and right and buttons to the bottom and right

Although the default positioning is the upper left, these edges need not be part of the positioning settings at all. For example, you can see that in Figure 4, the OK and Cancel buttons are positioned in the lower right corner, which is the same as the Windows dialog box habits.

If you don't want to generate dialog-style forms, but want to generate window-style forms, positioning is not the best choice. For example, if you are building an Explorer-style application that has a menu bar and a toolbar at the top, a status bar at the bottom, a tree view and a list view occupy the remaining space, and use the Splitter to determine the space occupied by the control, positioning cannot be used. At this point, you need to use docking.

By default, the control's Dock property is set to None. You can change the Dock property in the property browser by selecting an edge to dock or choosing to occupy the remaining space.

For example, the Dock properties of the status bar, tree view, and list view may show that the latter two are split by a splitter control, all of which are already scheduled and you don't need to write any code.

Docking, docking, and splitting are not the only ways to arrange controls on a form. Windows Forms also supports grouping controls and custom layouts for special cases. In addition, Windows Forms supports arranging windows within a parent, which is often referred to as the Multi-Document Interface (MDI).

Back to top
Data binding
Data binding refers to the ability to bind the content of one or more controls to a data source so that when one party is updated, the other party is also updated. Data binding is not only well supported in Windows Forms, it is fully integrated into Visual Studio .NET itself.

Dragging and dropping a table from the Server Explorer onto the design surface creates two components, a connection to the database and an adapter to transfer data between the two parties through the connection. Right-clicking the adapter in the designer and selecting Generate Dataset will create a new data set, which is a class derived from DataSet that is generated to hold the data of the table you dragged from Server Explorer. The default General Dataset option also creates an instance of a new dataset to associate with the control.

Once you have the source of the data, you can bind the data to one or more controls. Windows Forms provides multiple database-bound controls, including ListBox and ComboBox, among which DataGrid has the most flexibility.

Once you have a dataset on the form, to bind the data grid to it as a data source, you just need to set the DataGrid and DataMember properties of the data grid in the property browser and populate the form when loading the form data set:

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);}
These are just the tip of the iceberg for general uses of data binding and specific uses of data grids. For links to more data-bound resources, see the "References" section that follows.

Back to top
Migrating from MFC
As an MFC programmer, you must spend a lot of time and energy on your existing code base. Migrating MFC code to the .NET framework requires careful planning. Here are some considerations:


If you can afford to start from scratch, you will get the most maintainable code base, but it will take the longest.


If most of your MFC code is in a COM control (or you can migrate to a COM control), you can use Windows Forms as a host for those controls and write new managed code for the framework.


If you need to upgrade the MFC application itself, you can use the features provided by MFC 7.1 to host the Windows Forms control as a COM control and still retain the MFC code as unmanaged. For links to more information on the above scenarios, see the "References" section.


If you want to use managed code in a Windows Forms application, but to avoid the overhead of using COM Interop, you can deselect the "Use Managed Extensions" option for MFC projects so that you can mix managed and unmanaged in the same code Types of. See also the Reference section for links to more information on the above scenarios.

The options that apply to you depend on your situation, but in general we recommend that you adopt a strategy where you can write most of the new code for the .NET framework yourself, although this means that some of the Windows Forms features you generate are I started to understand and like it in MFC.

Back to top
summary
This article describes some of the most compelling new Windows Forms features, but Windows Forms have more features such as fully automated deployment, dialog data exchange and validation, MDI applications, drawing and printing (including print preview), users Controls, custom components and design-time support, manifest and typed resources, localization of non-compiled resources, application settings, and more.

One thing to keep in mind when comparing MFC and Windows Forms is that they were built separately to solve very different problems in very different eras. MFC is a document-based application framework. Windows Forms is a windowing library for n-tier applications. The difference between the two is normal. Some of these differences, such as positioning and docking, make Windows Forms even more advantageous. Other differences, such as object serialization, exist in other parts of the .NET Framework class library. There are other features that simply don't exist, which is what makes document-based application development interesting.

However, two things are clear: Microsoft is making strides towards managed code, and the benefits of managed code are compelling. The .NET framework provides automatic processing of both memory and security, resulting in more reliable applications and increased developer productivity. The .NET Framework Class Library provides a large, resource-rich, and unified class library. With this combination, we will be able to build Windows applications quickly and easily.



Related Article

Contact Us

The content source of this page is from Internet, which doesn't represent Alibaba Cloud's opinion; products and services mentioned on that page don't have any relationship with Alibaba Cloud. If the content of the page makes you feel confusing, please write us an email, we will handle the problem within 5 days after receiving your email.

If you find any instances of plagiarism from the community, please send an email to: info-contact@alibabacloud.com and provide relevant evidence. A staff member will contact you within 5 working days.

A Free Trial That Lets You Build Big!

Start building with 50+ products and up to 12 months usage for Elastic Compute Service

  • Sales Support

    1 on 1 presale consultation

  • After-Sales Support

    24/7 Technical Support 6 Free Tickets per Quarter Faster Response

  • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.