Hosting
WPF
Content in an
VC++ MFC
Application.
Introduction
This article will showcase the technique to host
WPF content in an MFC-based application. The
main usage of this implementation is to enhance
the graphical appearance of conventional Win32/MFC-based
applications. The demo program that comes along
with this article will host an animated WPF
clock in an MFC dialog program.
Development Tools & Library
To build and run the demo program, the following
components must be installed:
-
MS Visual Studio 2005 or
above
-
MS .NET Framework 3.0 or
above (download)
Platforms
The .NET Framework 3.0 supported only in the
following platforms:
-
Windows Vista
-
Windows XP SP2
-
Windows Server 2003 SP1
Prerequisites
If you are a
C++ developer and want to use
WPF
as an add-on to enhance the graphical UI of your
existing Win32/MFC applications, this article is
written for you. To get the most out from this
article, you should be familiar with
VC++/CLi,
MFC, XAML, and C#. But, if you don't know C# or
XAML, you can still proceed just by referencing
the generated DLL and make use of it in your
MFC
application.
What Is WPF?
WPF
stands for Windows Presentation Foundation.
It's a subsystem of Microsoft .NET Framework
3.0. It allows developers to productively create
visually appealing applications and improve user
experience. The deployment of WPF enables richer
control, design, and development of the visual
aspects of Windows programs. It aims to unify a
host of application services: user interface, 2D
and 3D drawing, fixed and adaptive documents,
advanced typography, vector graphics, animation,
data binding, audio, and video. WPF also
provides a consistent programming model for
building applications and provides a clear
separation between the UI and the business
logic.
WPF APIs are managed code, but
most existing Win32/MFC programs are coded in
unmanaged C++. Conventionally,
WPF APIs cannot
be called from a true unmanaged program.
However, by using the /clr option
with the
VC++ compiler, you can create a mixed
managed-unmanaged program where you can mix
managed and unmanaged API calls seamlessly.
One thing to note is that you are not allowed to
compile XAML files into a C++ project. So, you
have the following options to handle this.
-
Create a C# DLL that contains
all your XAML pages as a compiled assembly,
and then have your C++ executable include
that DLL as a reference.
-
Drop XAML and write all your
WPF in
code, building up the element tree from
application.
This article will use the first approach.
Interoperation Basics
There are two basic techniques for
interoperation between WPF and Win32/MFC code:
-
Host WPF in Win32/MFC: With
this technique, developers can use the
advanced graphics capabilities of WPF within
the framework of standard Win32/MFC
applications.
-
Host Win32/MFC in WPF: With
this technique, developers can use an
existing custom Win32/MFC control in the
context of other WPF content, and pass data
across the boundaries.
This article will be based on the first
technique.
Getting Started
Those are all the basic ground rules you should
know; it'd time to get to the code. Here, I will
show you WPF
content created in XAML and C#, and
the generated DLL will be referenced in VC++.
The idea is to create a simple Date & Time
setting tool with the animated clock part
implemented as a WPF
content. The rest of the
program still will be coded in MFC.
Project Division
This demo program is constructed by two parts;
one is MFCHostWPF (coded in VC++/MFC) and the
other is WPFcontrols (coded in XAML and C#). The
MFCHostWPF project will take the DLL generated
by WPFcontrols project as an external reference.
Adding WPF as a Reference Library
in an MFC Project
Hosting WPF Content in an MFC
Application
Adding
WPF-Related Code to an MFC
Application
For your information, the gcnew keyword
is used to create an instance of managed type;
this will create the instance on the garbage
collected heap. All memory allocated by gcnew will
be managed automatically by the garbage
collector and developers will not need to worry
about freeing them up.
To host
WPF content, the key is
on the System::Windows::Interop::HwndSource class.
This class will take care of wrapping the
WPF
content in a Win32 window, so that the WPF
content can be incorporated into your user
interface (UI) as a child window. Communication
with the
WPF content
object is done by using the reference stored in
the static fields.
They're declared as static to prevent them from
being inadvertently garbage collected.
ref class Globals
{
public:
static System::Windows::Interop::HwndSource^ gHwndSource;
static WPFControls::AnimClock^ gwcClock;
};
HWND hwndWPF; //The hwnd associated with the hosted WPF page
To create an HwndSource, first create an HwndSourceParameters structure
and populate it with the following parameters:
-
Class, window, and styles
-
Initial position of the
window
-
Initial size of the window
-
The parent window
Once you have populated the HwndSourceParameters
structure, pass it to the
HwndSource(HwndSourceParameters) constructor for
the HwndSource.
Then, you create your WPF clock
class by calling its constructor WPFControls::AnimClock().
You also initialize the date & time by calling
its ChangeDateTime() method.
Finally, you assign the reference
of the WPF clock object to the HwndSource object RootVisual property
and return the HWND of the HwndSource by calling Handle.ToPointer().
HWND GetHwnd(HWND parent, int x, int y, int width, int height)
{
System::Windows::Interop::HwndSourceParameters^ sourceParams =
gcnew System::Windows::Interop::HwndSourceParameters
("MFCWPFApp");
sourceParams->PositionX = x;
sourceParams->PositionY = y;
sourceParams->Height = height;
sourceParams->Width = width;
sourceParams->ParentWindow = IntPtr(parent);
sourceParams->WindowStyle = WS_VISIBLE | WS_CHILD;
Globals::gHwndSource =
gcnew System::Windows::Interop::HwndSource(*sourceParams);
DateTime tm = DateTime::Now;
Globals::gwcClock = gcnew WPFControls::AnimClock();
Globals::gwcClock->ChangeDateTime(tm.Year,tm.Month,tm.Day,
tm.Hour,tm.Minute,tm.Second);
FrameworkElement^ myPage = Globals::gwcClock;
Globals::gHwndSource->RootVisual = myPage;
return (HWND) Globals::gHwndSource->Handle.ToPointer();
}
So, whenver the user makes date
or time changes, your MFC code will call RefereshWPFControl() to
refresh the WPF clock.
void RefreshWPFControl()
{
FrameworkElement^ page;
DateTime tm = DateTime::Now;
Globals::gwcClock->ChangeDateTime(tm.Year,tm.Month,tm.Day,
tm.Hour,tm.Minute,tm.Second);
page = Globals::gwcClock;
Globals::gHwndSource->RootVisual = page;
return;
}
Now that you have all your needed
functions set, the last tucancode.net is to find a place
in your
MFC dialog code to call the HwndSource
instance creation function. There are a few
possible locations to call it; one good place is
in the OnCreate event
handler.
Handle the WM_CREATE notification
Call the GetHwnd() function
in the OnCreate event handler when the
application first starts.
int CMFCHostWPFDlg::OnCreate(LPCREATESTRUCT lpCreateStruct)
{
if (CDialog::OnCreate(lpCreateStruct) == -1)
return -1;
hwndWPF = GetHwnd(this->GetSafeHwnd(), 20, 28, 205, 130);
return 0;
}
Conclusion
As you can see now, by applying WPF in Win32/MFC
applications you can add lots of "perks" to the
overall user experience while the program logics
remain coded in the Win32/MFC style.
.zip"> MFCHostWPF-Bin.zip
MFC_WPF-Src.zip"> MFCHostWPF-Src.zip
News:
1
UCanCode Advance E-XD++
CAD Drawing and Printing Solution
Source Code Solution for C/C++, .NET V2024 is released!
2
UCanCode Advance E-XD++
HMI & SCADA Source Code Solution for C/C++, .NET V2024 is released!
3
UCanCode
Advance E-XD++ GIS SVG Drawing and Printing Solution
Source Code Solution for C/C++, .NET V2024 is released!
Contact UCanCode Software
To buy the source code or learn more about with:
Next-->
Promotional personalized database
document printing Solution
|
|