MFC 8.0 and Windows Forms Integration, Part I

Although Microsoft Foundation Classes (MFC) admittedly are not the future of rich client development frameworks, many companies have a huge investment in the technology and Microsoft continues to support MFC to ensure that it works well with Windows Forms today and Avalon in the future. The migration path from MFC to fully native applications is a slow one. Windows Forms lacks many of the advanced features of MFC, such as an in-built Document/View Framework and Windows shell integration, which prevents full migration for applications that rely on these features. Even if full framework feature parity existed, porting and testing an application from one framework to another is a massive job. For these reasons, having bridging technologies between the past (MFC) and the future (Windows Forms and Avalon) is very important.

This first of two articles looks at MFC today, and shows how much easier MFC 8.0 makes Windows Forms integration. Part II will drill into the MFC classes that implement the integration, as well as offer a brief look ahead at integrating with Avalon.

The Evolution of MFC

Before diving into Windows Forms/MFC integration, let's look at the evolution of MFC. In the beginning, a C++ developer had no application frameworks for writing Windows applications. He or she started building an application typically by copying and pasting an existing code base that contained skeleton code like message pumps that a Windows application required in order to get up and running.

This cut-and-paste approach had obvious problems, so Microsoft released a C++ framework called the Microsoft Foundation Classes that made getting a Windows application up and running relatively easy. As with Visual Basic, MFC contained wrappers for the underlying Windows controls that made interacting with the control pretty easy. The vast differences between the Visual Basic Forms library and MFC resulted in very little similarity between a Visual Basic control and a MFC control.

The differences between VB and MFC controls became an issue for third-party control vendors. Because a third-party Visual Basic control (VBX) was very different from a third-party MFC control, a vendor either needed two controls that provided the same UI functionality but were implemented very differently, or it ignored one of the markets. The VB market turned out to be much bigger and more successful, and VBX controls formed the basis for ActiveX (OCX) controls in the 32-bit world. Visual C++ was a great OCX producer, and it had the modern, lightweight Active Template Library (ATL) to produce COM-based DLLs and ActiveX controls. Ironically enough, Visual C++ and MFC were not great OCX consumers compared with Visual Basic.

The situation got even weirder with .NET. The new .NET Windows Forms totally superseded the Visual Basic Forms library. MFC and ATL continued to ship and receive support from Microsoft, and Visual C++ also gained the ability to write Windows Forms applications, though the initial release of Visual Studio.NET did not include a WYSIWYG designer. Windows Forms controls replaced OCXs as the favored delivery format for third-party control vendors, which meant that the vast majority of Visual C++ applications—which were not based on the managed Windows Forms framework—were again locked out of the most recent control delivery format (so it was back to the VBX days in some ways).

Thankfully, the situation improves with MFC 8.0. It contains a bunch of new classes that allow Windows Forms controls to be hosted within MFC Views and MFC Dialogs. This article doesn't drill into the various classes and technologies that are used to achieve this integration (Part II will), but it instead looks at how simple using the integration is.

Integrating MFC Applications and Windows Forms Controls

The well-traveled Scribble sample application that shipped with Visual C++ 6 can demonstrate how easy it is to integrate a Windows Forms control with an MFC application written well before the advent of managed code. To dispel any doubts that this technology works well with simple dummy controls but falls apart when you use real-world controls, this demonstration uses a free third-party control that is specialized for the input of numeric and currency data to replace Scribble's existing pen width thickness text box control. This new control prevents non-numeric text from being typed into a text box. You can download this control from WindowsForms.net.

The first step in the integration is to upgrade Scribble to the new version of the Visual Studio file format. Simply open the DSW file that contains the project workspace with Visual C++ 2005 and agree to the upgrade prompt. You then can run Scribble without any code modifications, though there will be a couple of minor warnings. The next step is to switch from native to managed compilation, which you can do on the General page of the Configuration Properties for the solution, as shown in Figure 1.

Figure 1: Configuring a C++ Application for Managed Compilation

MFC 8.0 and Windows Forms Integration, Part I

After upgrading and converting the project to a managed application (which takes less than a minute), you need to include the new MFC header file that supports Windows Forms control integration (afxwinforms.h) in stdafx.h. Once this is done, you can add a reference to the control for the solution by selecting the Project | References . menu option, clicking the Add New Reference button, selecting the Browse tab, and navigating to the control's assembly, which is NumericBox.dll in this case. For those new to .NET, adding a reference is the .NET equivalent to including a header file. Figure 2 shows the dialog for adding a reference.

Figure 2: Adding a Reference to a .NET Assembly

Because the Scribble application already has a control in the correct location, all you need to do is change the type of the control object. Because the original Scribble application uses DDX to bypass the Windows control and instead go straight to an integer (which the Scribble document then accesses directly), adding a new control variable and using DDX to hook this up with the dialog is a simpler modification than replacing the integer member variable. To implement this, add a new member variable to CPenWidthsDlg:

CWinFormsControl<NumericBox::NumericBox> m_cThinWidth;

The DoDataExchange function is modified to the following (new code in italics):

void CPenWidthsDlg::DoDataExchange(CDataExchange*pDX)
{
   CDialog::DoDataExchange(pDX);
   //{{AFX_DATA_MAP(CPenWidthsDlg)
   DDX_Text(pDX, IDC_THICK_PEN_WIDTH, m_nThickWidth);
   DDV_MinMaxInt(pDX, m_nThickWidth, 1, 20);
   DDX_ManagedControl(pDX, IDC_THIN_PEN_WIDTH, m_cThinWidth);
   //}}AFX_DATA_MAP

   //move the thin pen width value between the managed
   // control and the existing member variable
   if (pDX->m_bSaveAndValidate)
   {
      m_nThinWidth = System::Int32::Parse(m_cThinWidth->Text);
   }
   else
   {
      m_cThinWidth->Text = System::String::Format("{0}",
         m_nThinWidth);
   }
}

Notice the new templated type CWinFormsControl, which is used when declaring Windows Forms controls, and the new DDX_ function, which manages the creation and exchange of data between the actual control and the CWinFormsControl member variable. Standard data exchange code also manages the exchange of data between the control and the existing m_nThinWidth variable. Preserving m_nThinWidth meant that none of the other code that accessed this member variable needed to be changed.

The other code change to note is the use of the .NET Framework types Int32 and String, which have excellent parsing and formatting functionality. These types became available as soon as the CLR switch was turned on, which is another key benefit of moving past Visual C++ 6 and into the managed world. With only a couple of lines of new code and a couple of changes in the projects options, you have successfully migrated a very old application to take advantage of a new Windows Forms control and the rich functionality of the .NET Framework.

As the Windows Forms integration with MFC is based on COM and ActiveX technology, a call to the MFC function AfxEnableControlContainer was added to the CScribbleApp::InitInstance function, but this is the only change to the code in the Scribble application not directly related to the control instantiation.

But Wait, There's More!

Next month, Part II will drill into the classes that make Windows Forms controls use so easy.

About the Author

Nick Wienholt is an independent Windows and .NET consultant based in Sydney, Australia. He is the author of Maximizing .NET Performance from Apress, and specializes in system-level software architecture and development with a particular focus on performance, security, interoperability, and debugging. Nick can be reached at NickW@dotnetperformance.com.



Downloads

Comments

  • There are no comments yet. Be the first to comment!

Leave a Comment
  • Your email address will not be published. All fields are required.

Top White Papers and Webcasts

  • Today's agile organizations pose operations teams with a tremendous challenge: to deploy new releases to production immediately after development and testing is completed. To ensure that applications are deployed successfully, an automatic and transparent process is required. We refer to this process as Zero Touch Deployment™. This white paper reviews two approaches to Zero Touch Deployment--a script-based solution and a release automation platform. The article discusses how each can solve the key …

  • Learn How A Global Entertainment Company Saw a 448% ROI Every business today uses software to manage systems, deliver products, and empower employees to do their jobs. But software inevitably breaks, and when it does, businesses lose money -- in the form of dissatisfied customers, missed SLAs or lost productivity. PagerDuty, an operations performance platform, solves this problem by helping operations engineers and developers more effectively manage and resolve incidents across a company's global operations. …

Most Popular Programming Stories

More for Developers

RSS Feeds