本文将以一个实例来说明如何将WPF技术集成在基于MFC的应用程序中这种技术的主要目的是为了增强基于Win/MFC的应用程序的在图形方面的表现能力本文所提供的演示程序将在MFC对话框架应用程序中显示一个WPF动画时钟界面如图所示
本文需要的开发工具和开发库
为了建立和运行demo程序我们需要安装如下的组件
Visual Studio 或Visual Studio
NET Framework 或NET Framework
所需要的平台
demo程序必须在下面支持NET Framework的操作系统平台上运行
Windows Vista
Windows XP SP
Windows Server SP
如果你是一名C++开发人员并且想使用WPF技术来增强自己的基于Win/MFC应用程序本文非常适合你为了能从本文学习到更多的知识我们需要熟悉VC++/CLi MFC XAML和C#但是如果我们不了解C#或XAML我们仍然可以通过引用现成的DLL来使用WPF程序
二什么是WPF
WPF是Windows Presentation Foundation的缩写它是微软NET Framework的一个子系统这种技术允许开发人员高效地创建可视化的应用程序并改善用户体验由于WPF的发布使用Windows程序在富控件的设计开发上更如虎添翼WPF主要关注一套应用程序服务用户接口D和D矢量图动画数据绑字音频并提供一个在UI和商业逻辑之间的非常清晰的界限
WPF API是被管制的代码但是大多数Win/MFC程序是非管制的C++代码按着惯例WPF API不能通过非常制程序调用然后通过使用VC编译器的/clr选项我们可以建立一个由管制和非管制混合而面的系统在这个系统中我们可以使管制和非管制代码无缝地混合在一起
有一个要注意的是在C++工程中不允许编译XAML文件因此我们必须建立一个包含在本例中所需要的所有XAML而的C# DLL然后在C++工程中包含这个Dll引用
有两种基本的技术可以融合WPF和Win/MFC代码
将WPF放到Win/MFC程序中使用这种技术开发人员可以使用WPF的高级图形能力来渲染Win/MFC应用程序
将Win/MFC放到WPF中使用这种技术开发人员可以在WPF中使用已经存在的
Win/MFC控件并通过一定的机制来传递数据
在本文中将采用第一种方法
三 融合Win/MFC和WPF程序
上面部分所说的是本例的基本规则下面在本节中将介绍如何编写具体的实现代码在本例中我首先会演示使用XAML和C#建立WPF内容并在VC++中引用这个DLL
本例的目的是建立一个用于设置日期和时间的设置工具其中动画部分使用WPF实现程序的其他部分仍然使用MFC实现
这个演示程序由两部分组成一个是MFCHostMPF(由VC++/MFC代码组成)另一个是WPFControls(由XAML和C#代码组成)这个MFCHostWPF工程将由WPFControls工程所产生的DLL作为一个外部的引用如图所示
向MFC工程中加入一个WPF引用如图和图所示
三在MFC应用程序中加入和WPF相关的代码
先提一下gcnew关键字被用于建立一个管制类型的实例在本例中将建立一个垃圾回收集合栈的实例所有被gcnew分配的内存空间将被垃圾回收器自动管理而开发人员并不需要为什么时间释放它们而操心
为了使用WPF程序关键是System::Windows::Interop::HwndSource类这个类将在Win窗口中使用WPF程序因此WPF程序可以作为MFC窗口的子窗口放到UI上而在WPF对象和Win窗口之间的通讯要通过引用C++程序中被存储的静态字段这些静态字段的代码如下
ref class Globals { public: static System::Windows::Interop::HwndSource^ gHwndSource; static WPFControls::AnimClock^ gwcClock; };
HWND hwndWPF; // 和WPF相关的hwnd
为了建立一个HwndSource首先需要建立一个HwndSourceParameters结构这个结构需要如下的参数
类窗口窗口类型
窗口的初始位置
窗口的初始尺寸
父窗口
一但我们将HwndSourceParameters结构编写完就可以将这个结构到HwndSource的构造方法HwndSource(HwndSourceParameters)中
最后我们将WPF时钟的引用赋值给HwndSource对象的RootVisual属性并通过调用HandleToPointer()返回HwndSource的HWND代码如下
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(tmYeartmMonthtmDay tmHourtmMinutetmSecond); FrameworkElement^ myPage = Globals::gwcClock; Globals::gHwndSource>RootVisual = myPage; return (HWND) Globals::gHwndSource>HandleToPointer(); }
因此无论用户如何变化时钟我们的MFC代码都会调用RefereshWPFControl()来刷新WPF时钟
void RefreshWPFControl() { FrameworkElement^ page; DateTime tm = DateTime::Now; Globals::gwcClock>ChangeDateTime(tmYeartmMonthtmDay tmHourtmMinutetmSecond); page = Globals::gwcClock; Globals::gHwndSource>RootVisual = page; return; }
现在我们已经有了大部分我们需要的功能了而最后的任务是在MFC对话框代码中找个地方调用HwndSource实现创建函数当然有很多地方可以做这个工作但是OnCreate也许是最好的位置在OnCreate事件句柄中调用GetHwnd()函数的代码如下
int CMFCHostWPFDlg::OnCreate(LPCREATESTRUCT lpCreateStruct) { if (CDialog::OnCreate(lpCreateStruct) == ) return ; hwndWPF = GetHwnd(this>GetSafeHwnd() ); return ; }
四结论
我们现在可以看到通过集成WPF和Win/MFC应用程序可以为我们增添很多非常酷的特性来加强用户体验本文所提供的实例只是抛砖引玉读者可以使用这项技术做出更复杂的应用程序来