这篇文章使用了以下技术:
C++, MFC, .NET Framework, Windows Presentation Foundation
尽管Microsoft .NET Framework在大约五年前就首次公布了,而且2.0版本最近也已经发布了,但是许多C++应用程序仍旧是纯的非托管代码。然而C++的开发人员对.NET Framework的兴趣正在快速地增长。并且,许多未来的Windows® API将基于.NET Framework。当然,这对于WinFX®的组件来说也是正确的,包括Windows Presentation Foundation,Windows Communication Foundation,和Windows Workflow Foundation。
而且,许多C++开发人员愿意使用本机API,而不是在现存的API上进行包装。包装往往被认为是有bug的、慢的以及不灵活的。除此之外,要把像WinFX一样的大量API映射成为供本机代码使用的API,确实困难。
在大多数情况下,为C++应用程序扩展.NET Framework特性比大多数开发人员想象的要简单。Visual C++®包含了一种被称作是C++ Interop的特性,有时简称为IJW,或“It Just Works”。 使用本特性可以把基于.NET的代码无缝地集成到已经存在的C++源代码中。
C++ Interop基于两个主要的特性。首先,你能使用C++编译器的/clr开关将已经存在的C++代码编译成Microsoft®中间语言(MSIL),从而你的代码可以使用.NET特性,如垃圾回收、沙箱安全以及大量.NET Framework基类库中的类型。
另一个特性是混合模式,它也是同等重要的。它是指托管代码和非托管代码的联合。当把C++代码编译成MSIL时,编译器创建了包含MSIL代码的托管对象文件,而不是包含本机汇编代码的普通的非托管对象文件。连接器能把托管对象文件和非托管对象文件同时作为输入。当连接器检测到至少有一个托管输入时,它创建一个同时包含有托管代码和非托管代码的.NET程序集(assembly)。这是它被称为混合模式的原因(见图1)。这个特性对于性能优化尤其重要,因为它允许你最大程度地减少在托管空间和非托管空间的转换次数。
图1 MTS
C++ Interop是你的朋友
我的经验表明,C++ Interop是非常可靠的,尽管它有一些限制,但还是值得把它加入到你的开发工具箱中。要理解这一特性是如何工作的,你应当认识到C++ Interop是.NET Framework的一个设计特性。作为.NET Framework基础规格的通用语言基础架构(CLI),已经支持了这一特性。事实上,CLI的一些方面被定义,仅仅是因为C++ Interop需要它们。
例如,托管元数据支持全局函数。然而C#和大多数面向.NET的其他语言要求所有方法要存在于类的范围内。这一特性的存在是因为应当被映射成基于.NET代码的C++代码可以包含全局函数,为了把C++代码映射成托管代码,元数据需要一个同等的概念。
在设计上,MSIL指令集已经支持C++ Interop。为了把C++代码映射为MSIL代码,MSIL必须支持所有公共数据操作运算符,如布尔、算术以及符点运算符。MSIL也支持对虚拟内存基于指针的存取。这一特性是在托管代码中使用本地类型的关键。如果你的C++代码要存取一个本地对象的一个字段,C++/CLI编译器会生成MSIL代码,把本地对象的地址压到堆栈上,在其上加上字段的偏移,并使用返回地址来存取字段。所有这些成为可能是因为MSIL指令集有通过地址来存取虚拟内存地址的操作。
另一种IL语言的C++特定的特性是CALLI指令。它可被用于通过函数指针调用本地函数。这个特性被用于调用本地类型的虚函数。这很重要,因为COM接口是带有虚函数的本地类型。这种COM互操作的方法,与用于其他语言的其他COM互操作技术相比,具有许多优点。
最后值得一提的是,对使用.NET的所有开发人员最明显的设计影响是组件的文件格式。Java定义了自己的文件格式(.class文件),然而.NET Framework使用标准PE文件格式。因此,基于.NET的组件有很合适的文件扩展DLL和EXE。PE文件仍被使用的事实对于支持混合模式的程序集来说是至关重要的。
何时使用C++ Interop
要评估C++ Interop是否适用于你的项目,你应当了解这个特性在生成你的项目时所带来的影响,对开始者来说,即编译器设置,同时,也应当了解它对于生成过程的输出结果的影响。如果使用/clr选项进行编译,代码严格依赖C运行时库(CRT)的多线程DLL版本。这意味着项目中的所有文件,包括那些不使用/clr编译的文件,必须使用CRT的多线程DLL版本。这对MFC项目同样重要。静态MFC库依赖静态CRT库,它不兼容/clr编译。因此,你必须确认你的MFC项目连接到MFC的DLL版本。
这儿还有一些关于执行时间的话题。初始化CLR占用一定量的时间,JIT编译也有一些附加时间。因此,你也许感觉启动时间稍微变长。然而,对大多数项目而言,这应当不是一个大的问题。
由托管到非托管切换的方法调用,比通常的方法调用花费的时间更长。我已提到过,你可以通过判定你的应用的某些特定部分将被编译成托管代码,来显著地减少托管和非托管代码之间的切换。调用转换__clrcall是另一种可以显著地减少切换次数的重要优化特性,但这一特性超出本文的范围。
在大多数情形下,即时(JIT)编译代码本身并不是性能显著下降的原因。事实上,JIT编译器可以完成一些优化,而C++编译器和连接器却不可能完成。例如,JIT编译器可以针对目标机器的处理器架构优化代码。与C++编译器相反,JIT编译器提供组件间内联。这可能是一个非常有效的优化,因为编译成带有组件间内联的代码可以用于未来的优化。
除了在这儿提到的话题外,还有一些其他相关的领域,我将不再详细地讨论。它们中的大部分可以通过正确地修改编译器和连接器设置,很容易地被解决。最佳的编译器设置并不总是直截了当的。一些设置打开了对C++ Interop编译强制的特性,而一些其他的设置关闭了与/clr编译不兼容的,或者已经被Microsoft中间语言以另一种方法解决了的特性。
在MFC应用中的Windows窗体控件
所有使用/clr选项编译的源文件可以使用.NET Framework基类库中的类型。你可以像你一直使用它们那样,来使用大部分的基类库。有一些情况需要特别注意。在MFC应用中集成Windows窗体控件就是一个例子。
你不能在需要CWnd*时传递System::Windows::Forms::Control^。然而,Windows窗体API和MFC共享公共的基础:好的古老的User32.dll。这两种API都提供了到HWNDs的一些后门。MFC的CWnd类提供一个到HWND的转换操作符,而Windows窗体控件类在命名空间System::Windows::Forms中实现了IWin32Window接口,以暴露出窗口句柄:
public interface IWin32Window {
property IntPtr Handle {
IntPtr get ();
}
};
然而,从一个Window对象中获得窗口句柄通常是不够的。MFC提供一些类来支持在不同场景下处理Windows窗体控件。这些类被定义在afxwinforms.h中。
这些类中最重要的是CWinFormsControl,它被定义在命名空间Microsoft::VisualC::MFC中。这个类允许在一个MFC对话框或CDialog派生类中宿主一个Windows窗体控件。它是带有一个模板参数的模板类,参数被用于传递要宿主的Windows窗体的类型。类型是要稍后实例化的具体类型,或者是具体类型的基类。CWinFormsControl模板很简单。它只有两个有趣的特性:它继承于CWnd,提供了一个自解释方法CreateManagedControl的几种重载形式。
如果你想在MFC对话框或CDialog派生类中宿主一个Windows窗体控件,在你的类中定义一个类型为CWinFormsControl<TWinFormsCtrl>的成员变量,并选择一种CreateManagedControl的重载形式。如果你实现一个CDialog派生类,创建托管控件的好的候选是OnInitDialog。对大多数其他情形,OnCreate(WM_CREATE消息处理)是最好的选择。对于对话框类而言,有一种重载形式尤其有趣。这种重载方式期望在对话框资源中加入一个静态控件作为占位符。放置静态控件,以便它有Windows窗体控件应当有的位置和大小,并且赋给它一个唯一的ID,如图4所示。要实例化被宿主的控件,如图2所示调用CreateManagedControl。此外,你甚至可以使用对话框数据交换(DDX)的一个特别变种,如图3所示。
图4 使用ActiveX控件
注意,在MFC应用中宿主Windows窗体控件的方式,使用了另一种基于Windows窗体和MFC的技术:ActiveX控件。如图5列表所示,System::Windows::Forms::Control实现了一些接口,它们对于OLE和ActiveX控件熟悉的老手们来说很熟悉。在CreateManagedControl中,Windows窗体控件通过gcnew操作符被实例化,并像一个普通ActiveX控件一样被就地激活。
还要注意,CWinFormsControl重载了->操作符,返回被宿主的控件。这允许你通过简单的赋值来设置按钮的Text属性:
m_wfBtn.Text = "Click me!";
这种处理方式不如在属性窗口中修改控件属性那样简洁,但是如果你适应了现存的有限的设计器支持,Windows窗体控制的世界将向你可信的老的MFC对话框开放。
处理控件事件
这儿还要讨论一些其他问题。到目前为止,按钮的单击事件还没有被处理。因为你有一个像ActiveX控件一样被宿主的Windows窗体控件,处理事件有两种选择:由ActiveX控件提供的连接点方式,和Windows窗体控件的事件成员方式。如果Windows窗体控件准备支持基于COM的事件,连接点方案才成为可能。大多数Windows窗体控件并不符合这种情形,所以以.NET方式处理事件是唯一的方法。托管类中的事件是一种特别的类型成员,允许事件处理程序委托(event handler delegate)的注册和注销。以下的代码行展示了如何注册按钮的单击事件:
m_wfBtn.Click +=<单击事件的一个事件处理程序委托>
要提供这样一个委托,需要如下语法的一个函数:
void EventHandler(Object^ sender, EventArgs^ e);
很不幸,在CDialog派生类中实现这样一个方法还不够。
委托目标函数必须是托管类的成员函数,CDialog派生类是一个本地类。Visual C++提供了一个头文件,\Msclr\Event.h,针对这个问题给出了通用解决方案。当你想用本地C++类的方法来处理.NET事件时,可以使用头文件中的定义。正如我稍后要讨论的,当你要集成Windows Presentation Foundation的Visuals特性时,同样可以使用这一头文件。因为这一头文件在如此多的不同的情形下是有用的,所以深入地讨论它提供的解决方案是有意义的。
为了处理Windows窗体控件的事件,你可以实现包含消息处理程序的一个托管类。C++ Interop允许你以优雅的方式去处理,因为托管类可以被嵌入到CDialog派生类中。
class CMyDialog : public CDialog {
ref class nested_managed_class {
void OnWFBtnClick(Object^ sender, EventArgs^ e) {
// 在这儿处理按钮单击事件
}
};
// 其他成员
};
最便利的处理按钮单击事件的方法是,把方法调用移交给CDialog派生类的一个成员函数。最终处理单击事件的方法,可以很容易地存取派生类的其他成员,就像你期望事件处理程序工作的那样。包含委托目标的托管类最终成为了一个简单的代理类型。尽管这样一个代理类型和它的代理事件处理程序可以由定义在Event.h中的宏来创制,但是让我们首先看看如何进行手工定义(见图6)。
如你所见,委托代理有一个带有CMyDialog*参数的构造函数。要把方法调用移交给CDialog派生类,这样一个构造函数是必要的。这一委托代理类型可以用于本对话框类中的所有Windows窗体控件的所有事件处理程序。
因为本地类中不能有指向垃圾回收对象的句柄作为字段,所以在派生类中增加一个delegate_proxy_type的成员变量是不可能的。使用gcroot模板,你可以绕过这一限制。因此,一个类型为gcroot<delegate_proxy_type>的成员变量是必须的。然而,还有一个问题:委托代理类型的gcrooted引用是如何被正确地初始化的?当需要delegate_proxy_type的句柄时,调用一个单独的方法来处理。
运用图6的代码,如下例所示,委托能被容易地注册:
virtual BOOL OnInitDialog() {
CDialog::OnInitDialog();
this->m_wfBtn->Click +=
gcnew System::EventHandler(get_proxy(this),
&delegate_proxy_type::OnWFBtnClick);
return TRUE;
}
写所有这些代码的工作量非常大,而这些仅仅是为了在CDialog派生类中处理Windows窗体控件的事件。Event.h包含了宏和模板,使得你可以用很少的代码行完成同样的甚至更多的功能,如图7所示。
每个新版本的MFC至少有一个新的映射(map)。如你所见,目前版本引入了委托映射。宏BEGIN_DELEGATE_MAP定义了包含有委托目标的delegate_proxy_type。每一个EVENT_DELEGATE_ENTRY行在托管类中增加了一个目标方法。END_DELEGATE_MAP只是结束了托管类。最后,MAKE_DELEGATE实例化一个指向delegate_proxy_type的目标方法的委托。实际上,这些宏做的事情比我刚才讨论的还要更多。它们甚至为以下场景做好了准备,在托管类发生的事件要被发送给一个不再存在的本地对象。
作为对话框的控件
另一个简单但有效的助手是CWinFormsDialog模板。你可以使用它以模态或非模态对话框的形式来展示一个Windows窗体控件。这个类相当简单。它扩展CDialog来提供MFC对话框的标准行为。
除此以外,它使用一个CWinFormsControl成员变量来宿主Windows窗体控件,用被宿主控件的Text属性值来初始化对话框的标题,设置对话框的初始大小,以便控件恰好嵌入其中,并确保当父窗口大小在改变时,调整被宿主控件的大小。
下面的一行代码实例化一个宿主YourWinFormsDlgControl的临时对话框,并以一个模态对话框形式显示它:
CWinFormsDialog<YourWinFormsDlgControl>().DoModal();
然而,你应当避免这样来使用,因为这样无法使用DoModal的对话框返回值。对于更实际的情况,考虑从CWinFormsDialog<YourWinFormsDlgControl>中派生你自己的类。这允许你重载OnInitDialog,设置被嵌入的Windows窗体控件的属性,或处理被宿主控件的事件。我之前描述的Event.h中的宏在这种情况下也是有用的。要获得更多信息,参见msdn2.microsoft.com/ahdd1h97.htm。
Windows窗体视图
最后,你可以以视图的方式宿主Windows窗体控件。CWinFormsView是这种情形下你要的小助手。不像CWinFormsDialog一样,它并不是一个模板类。然而,也有很多相似之处。它们都把实际的控件宿主留到了CWinFormsControl模板中,并且它们都处理被宿主控件的大小调整。
为了用一个MFC对话框来宿主Windows窗体控件,你必须从CWinFormsView中继承你的视图类。以下代码展示了一个简单的基于CWinFormsView的视图类的声明:
class CMyWinFormsBasedView : public CWinFormsView
{
CMyWinFormsBasedView ();
DECLARE_DYNCREATE(CMyWinFormsBasedView)
};
如果你的视图类是由向导生成的,你应当确保不仅在你的视图类定义的基类列表中,而且在它的实现中,改换成CWinFormsView。你可能要改变宏的参数,如IMPLEMENT_DYNAMIC和BEGIN_MESSAGE_MAP宏。要实例化被宿主的Windows窗体控件,CWinFormsView(你的视图基类)的构造函数得到一个指向Windows窗体控件的System::Type对象的句柄。视图类的实现也非常简单,如下所示:
IMPLEMENT_DYNCREATE(CMyWinFormsBasedView, CWinFormsView)
CMyWinFormsBasedView:: CMyWinFormsBasedView ()
: CWinFormsView(WinFormsViewControl::typeid) {}
初看起来,这段代码对于实际情况来说太简单,但是在一些情况下,这确实就是你所需要的全部。视图类充当了位于视图控件中的真实实现的简单代理。
一些视图的实现重载了MFC CView基类中的虚函数。要让本地视图充当一个简单的代理,应当在本地视图中重载这些方法,并把这些调用移交给视图中Windows窗体控件的对等方法。对CView中的三个最重要的重载来说,基类CWinFormsView已经实现了这样的移交。这三个方法是OnInitialUpdate、OnUpdate以及OnActivateView。传递基于定义在Mfcmifc80.dll程序集中的托管接口Microsoft::VisualC::MFC::IView。为了重载这些方法,在视图的Windows窗体类中实现这一接口,如图8所示。
除了重载虚函数以外,视图还实现了命令处理程序。在本地视图类中实现一个命令处理程序并把它传递给Windows窗体控件是可能的,但是因为这是一种常见的场景,所以MFC提供了一个更为便利的解决方案。在这种场景下,Mfcmifc80.dll程序集包含了更多的托管助手类型。要从MFC的命令传送基础结构中接收命令消息,Windows窗体控件必须实现Microsoft::VisualC::MFC::ICommandTarget接口:
interface class ICommandTarget
{
void Initialize(ICommandSource^ commandSource);
};
当实现ICommandTarget的视图被创建时,在那个视图上,ICommandTarget::Initialize被调用,一个新命令源对象的句柄被当作参数传递。这个参数的类型是ICommandSource^。参数所指的对象是一个许多处理程序的容器。这些处理程序对于MFC开发人员来说,听起来应当很熟悉:它们是当一个命令发出时被调用的命令处理程序;控制控件的UI是否使能、选中,或用单选按钮装饰,以及在命令UI中显示什么样的文字的命令UI处理程序。
要注册一个处理程序,像AddCommandHandler和AddCommandUIHandler一样的方法,可以在命令源上被调用。这些方法期望两个参数:指代命令ID的无符号整数,和被用于传递控件处理程序函数的委托。因为命令处理程序和命令UI处理程序有着不同的签名,在命名空间Microsoft::VisualC::MFC中定义了两个不同的委托类型:
delegate void CommandHandler(unsigned int);
delegate void CommandUIHandler(unsigned int,
Microsoft::VisualC::MFC::ICommandUI^);
图9展示了如何为ID_EDIT_PASTE命令注册一个命令处理程序。
当视图要处理一个命令时,委托被用于调用一个视图的成员函数。命令ID被作为参数来传递。如果每个命令有自己私有的处理程序函数,这个参数并不重要,但是一个简单的方法可以被用于多个命令的处理程序函数。AddCommand[UI]RangeHandler允许你为多个命令注册一个委托。
命令UI处理程序委托还有一个类型为ICommandUI^的参数。使用这个参数传递的句柄是MFC CCmdUI类的包装器,它允许你控制如菜单项和工具条按钮等界面元素的表现和可用性。
除了在ICommandSource::Initialize中增加命令处理程序外,在Windows窗体控件的成员变量中存储ICommandTarget句柄也是很有用的。它给你提供了以后增加和移除命令处理程序的选择,你甚至可以通过ICommandTarget::SendMessage或ICommandTarget::PostMessage来同步或异步发出命令事件。
通向Avalon之桥
因为Windows Presentation Foundation还没有最终发布,当前的MFC版本并没有在CWnds和CDialogs中集成Windows Presentation Foundation的Visuals提供助手类和模板。然而,这并不意味着在MFC应用中集成Visuals是不可能的。事实上,即使没有这些小助手类,仍然只需要几行代码就能在CWnd或CDialog中宿主一个Visual。
这种集成的关键特性来自于Windows Presentation Foundation自身。Windows.PresentationCore.dll程序集提供了一个强有力的类HwndSource,它位于命名空间System::Windows::Interop中。这个类提供了HWND和Visual之间的桥。要宿主的USER32窗口对象看来像一个有通常HWND的子窗口。使用它的RootVisual属性,你可以进入Windows Presentation Foundation的新世界。(注意,这儿的信息基于Windows Presentation Foundation的一个预发布版本,也许在最终发布时会改变。)
HwndSource构造函数期望HwndSourceParameters的值类型作为参数。如图10所示,这一值类型包含了与调用Win32函数CreateWindowEx所传递参数相似的信息。图11展示了在OnInitDialog实现中如何使用HwndSource。
总结
通过使用C++ Interop,托管代码可以被无缝地集成到现存的C++源代码中。集成是Visual C++本来就具有的、被支持的特性,对Windows窗体的MFC支持可以被认为是对此强有力的证据。为了简化这种集成,提供了几种助手类和模板。集成层并不复杂,也不难于使用。
还有一件事情,是把这些特性无缝地集成到Visual Studio的向导中。在大多数的场景中,因为很多实现将被遗留到被宿主的Windows窗体控件中,所以这并不是一个大的问题,而Windows窗体控件在Visual Studio中得到了很好的支持。当Windows Presentation Foundation发布时,为了在MFC应用中集成Visuals,提供对等支持的可能性是很大的。即使没有这种支持,在MFC应用中宿主Visuals仍然是很容易的。
Marcus Heege works as a course author and trainer for DevelopMentor and provides consulting for different IT companies. He regularly posts his thoughts about C++/CLI and .NET in his blog at www.heege.net.
From the May 2006 issue of MSDN Magazine.
图2 创建一个被宿主控件
using Microsoft::VisualC::MFC;
using System::Windows::Forms;
class CMyDialog : public CDialog
{
public:
CMyDialog() : CDialog(CMyDialog::IDD, NULL) {}
enum { IDD = IDD_MYDIALOG };
private:
CWinFormsControl<Button> m_wfBtn;
public:
BOOL OnInitDialog() {
CDialog::OnInitDialog();
return this->m_wfBtn.CreateManagedControl(
WS_VISIBLE | WS_CHILD, IDC_WFBTN, this));
}
}
图3 宿主一个有对话框数据交换(DDX)的控件
using Microsoft::VisualC::MFC;
using System::Windows::Forms;
class CMyDialog : public CDialog
{
public:
CMyDialog(CWnd* pParent = NULL): CDialog(CMyDialog::IDD, pParent) {}
enum { IDD = IDD_MYDIALOG };
private:
CWinFormsControl<Button> m_wfBtn;
protected:
virtual void DoDataExchange(CDataExchange* pDX) {
DDX_ManagedControl(pDX, IDC_WFBTN, this->m_wfBtn);
}
};
图5 接口
System::Windows::Forms::Control
|
IOleObject
|
IOleControl
|
IOleInPlaceObject
|
IOleInPlaceActiveObject
|
IOleWindow
|
IOleViewObject
|
IOleViewObject2
|
IPersist
|
IPersistStreamInit
|
IPersistPropertyBag
|
IPersistStorage
|
IQuickActivate
|
图6 CMyDialog
class CMyDialog : public CDialog
{
public:
ref class delegate_proxy_type;
gcroot<delegate_proxy_type^> m_gc_managed_native_delegate_proxy;
delegate_proxy_type^ get_proxy(CMyDialog* pNativeTarget) {
if((delegate_proxy_type^)m_gc_managed_native_delegate_proxy ==
nullptr)
{
m_gc_managed_native_delegate_proxy =
gcnew delegate_proxy_type(pNativeTarget);
}
return (delegate_proxy_type^)m_gc_managed_native_delegate_proxy;
}
ref class delegate_proxy_type {
CMyDialog* m_p_native_target;
public:
delegate_proxy_type(CMyDialog* pNativeTarget) :
m_p_native_target(pNativeTarget) {}
void OnWFBtnClick(Object^ sender, EventArgs^ e) {
this->m_p_native_target->OnWFBtnClick(sender, e);
}
};
void OnWFBtnClick(Object^ sender, EventArgs^ e) {
this->m_wfBtn->Text = "Thanks for clicking";
}
// other members
};
图7 简化CMyDialog
class CMyDialog : public CDialog
{
public:
CMyDialog(CWnd* pParent = NULL): CDialog(CMyDialog::IDD, pParent) {}
enum { IDD = IDD_MYDIALOG };
private:
CWinFormsControl<Button> m_wfBtn;
protected:
virtual void DoDataExchange(CDataExchange* pDX) {
DDX_ManagedControl(pDX, IDC_WFBTN, this->m_wfBtn);
}
public:
BEGIN_DELEGATE_MAP(CMyDialog)
EVENT_DELEGATE_ENTRY(OnWFBtnClick, Object^, EventArgs^)
END_DELEGATE_MAP()
void OnWFBtnClick(Object^ sender, EventArgs^ e) {
this->m_wfBtn->Text = "Thanks for clicking";
}
public:
virtual BOOL OnInitDialog(){
CDialog::OnInitDialog();
this->m_wfBtn->Click += MAKE_DELEGATE(System::EventHandler,
OnWFBtnClick);
return TRUE;
}
};
图8 重载IView
#using <mfcmifc80.dll>
using Microsoft::VisualC::MFC::IView;
public ref class MyWinFormsViewControl :
public System::Windows::Forms::UserControl,
public IView
{
...
protected: // IView implementation
virtual void OnInitialUpdate() = IView::OnInitialUpdate {
// implementation
}
virtual void OnUpdate() = IView::OnUpdate {
// implementation
}
virtual void OnActivateView(bool activate) = IView::OnActivateView {
// implementation
}
};
图9 注册命令处理程序
public ref class MyWinFormsViewControl :
public System::Windows::Forms::UserControl,
public ICommandTarget
{
...
protected: // ICommandTarget implementation
virtual void RegisterCmdHandlers(ICommandSource^ cmdSrc) =
ICommandTarget::Initialize
{
cmdSrc->AddCommandHandler(ID_EDIT_PASTE,
gcnew CommandHandler(this, &WinFormsView::OnEditPaste));
cmdSrc->AddCommandUIHandler(ID_EDIT_PASTE,
gcnew CommandUIHandler(this,
&WinFormsView::OnUpdateEditPaste));
// register further command handlers ...
}
void OnEditPaste(unsigned int)
{
// implement command handler
}
void OnUpdateEditPaste(unsigned int, ICommandUI^ cmdUI)
{
cmdUI->Enabled = ...; // your logic here
}
};
图10 CreateWindowEx和HwndSourceParameters
CreateWindowEx Arguments
|
HwndSourceParameters Properties
|
LPCTSTR lpClassName
|
N/A
|
N/A
|
WindowClassStyle
|
LPCTSTR lpWindowName
|
WindowName
|
DWORD dwStyle
|
WindowStyle
|
DWORD dwExStyle
|
ExtendedWindowStyle
|
int x
|
PositionX
|
int y
|
PositionY
|
int nWidth
|
Width
|
int nHeight
|
Height
|
HWND hWndParent
|
ParentWindow
|
HMENU hMenu
|
N/A
|
HINSTANCE hInstance
|
N/A
|
图11 使用HwndSource
BOOL CWPFDemoDialog::OnInitDialog()
{
__super::OnInitDialog();
CRect rectClient;
this->GetClientRect(&rectClient);
System::Windows::Interop::HwndSourceParameters hwsPars;
hwsPars.ParentWindow = System::IntPtr(this->m_hWnd);
hwsPars.WindowStyle = WS_CHILD | WS_VISIBLE;
hwsPars.PositionX = 0;
hwsPars.PositionY = 0;
hwsPars.Width = rectClient.Width();
hwsPars.Height = rectClient.Height();
System::Windows::Interop::HwndSource^ hws;
hws = gcnew System::Windows::Interop::HwndSource(hwsPars);
using System::Windows::Controls::MonthCalendar;
MonthCalendar^ mc = gcnew MonthCalendar;
hws->RootVisual = mc;
return TRUE;
}
词汇表
assignment
|
赋值
|
Common Language Infrastructure
|
通用语言基础构造
|
command-routing infrastructure
|
命令传送基础结构
|
deregistration
|
撤销登记
|
event handler delegates
|
事件处理程序委托
|
fields
|
字段
|
forward
|
移交
|
helper
|
助手
|
instantiates
|
实例化
|
interoperability
|
互操作性
|
in-place activated
|
就地激活
|
just-in-time
|
实时
|
map
|
映射
|
mix and match
|
混合搭配
|
native code
|
本地代码
|
placeholder
|
占位符
|
template class
|
模板类
|
Windows Forms
|
Windows 窗体
|
posted on 2008-08-15 21:06
cpsprogramer 阅读(2817)
评论(1) 编辑 收藏 引用 所属分类:
VC++