bearblog
Focus on MSDN Mag Translation
 

 

MSDN Mag 2006.05.

原文出处:Mix And Match: Integrate Windows Forms Into Your MFC Applications Through C++ Interop

翻译:Steven Xiong

翻译时间:2006516

 

 

混合匹配

通过C++ InteropWindows窗体集成到MFC应用程序中

Marcus Heege

 

这篇文章讨论:

  • C++ Interop如何工作
  • 在项目中何时使用C++ Interop
  • MFC应用中使用Windows窗体控件
  • Windows窗体转到WPF视图

 

这篇文章使用了以下技术:

C++, MFC, .NET Framework, Windows Presentation Foundation

 

尽管Microsoft .NET Framework在大约五年前就首次公布了,而且2.0版本最近也已经发布了,但是许多C++应用程序仍旧是纯的非托管代码。然而C++的开发人员对.NET Framework的兴趣正在快速地增长。并且,许多未来的Windows® API将基于.NET Framework。当然,这对于WinFX®的组件来说也是正确的,包括Windows Presentation FoundationWindows 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的组件有很合适的文件扩展DLLEXEPE文件仍被使用的事实对于支持混合模式的程序集来说是至关重要的。

 

何时使用C++ Interop

要评估C++ Interop是否适用于你的项目,你应当了解这个特性在生成你的项目时所带来的影响,对开始者来说,即编译器设置,同时,也应当了解它对于生成过程的输出结果的影响。如果使用/clr选项进行编译,代码严格依赖C运行时库(CRT)的多线程DLL版本。这意味着项目中的所有文件,包括那些不使用/clr编译的文件,必须使用CRT的多线程DLL版本。这对MFC项目同样重要。静态MFC库依赖静态CRT库,它不兼容/clr编译。因此,你必须确认你的MFC项目连接到MFCDLL版本。

这儿还有一些关于执行时间的话题。初始化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窗体APIMFC共享公共的基础:好的古老的User32.dll。这两种API都提供了到HWNDs的一些后门。MFCCWnd类提供一个到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。对大多数其他情形,OnCreateWM_CREATE消息处理)是最好的选择。对于对话框类而言,有一种重载形式尤其有趣。这种重载方式期望在对话框资源中加入一个静态控件作为占位符。放置静态控件,以便它有Windows窗体控件应当有的位置和大小,并且赋给它一个唯一的ID,如4所示。要实例化被宿主的控件,如2所示调用CreateManagedControl。此外,你甚至可以使用对话框数据交换(DDX)的一个特别变种,如3所示。

 


4
使用ActiveX控件

注意,在MFC应用中宿主Windows窗体控件的方式,使用了另一种基于Windows窗体和MFC的技术:ActiveX控件。如5列表所示,System::Windows::Forms::Control实现了一些接口,它们对于OLEActiveX控件熟悉的老手们来说很熟悉。在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 FoundationVisuals特性时,同样可以使用这一头文件。因为这一头文件在如此多的不同的情形下是有用的,所以深入地讨论它提供的解决方案是有意义的。

为了处理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_DYNAMICBEGIN_MESSAGE_MAP宏。要实例化被宿主的Windows窗体控件,CWinFormsView(你的视图基类)的构造函数得到一个指向Windows窗体控件的System::Type对象的句柄。视图类的实现也非常简单,如下所示:

IMPLEMENT_DYNCREATE(CMyWinFormsBasedView, CWinFormsView)

 

CMyWinFormsBasedView:: CMyWinFormsBasedView ()

    : CWinFormsView(WinFormsViewControl::typeid) {}

初看起来,这段代码对于实际情况来说太简单,但是在一些情况下,这确实就是你所需要的全部。视图类充当了位于视图控件中的真实实现的简单代理。

一些视图的实现重载了MFC CView基类中的虚函数。要让本地视图充当一个简单的代理,应当在本地视图中重载这些方法,并把这些调用移交给视图中Windows窗体控件的对等方法。对CView中的三个最重要的重载来说,基类CWinFormsView已经实现了这样的移交。这三个方法是OnInitialUpdateOnUpdate以及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处理程序。

要注册一个处理程序,像AddCommandHandlerAddCommandUIHandler一样的方法,可以在命令源上被调用。这些方法期望两个参数:指代命令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::SendMessageICommandTarget::PostMessage来同步或异步发出命令事件。

 

 

通向Avalon之桥

因为Windows Presentation Foundation还没有最终发布,当前的MFC版本并没有在CWndsCDialogs中集成Windows Presentation FoundationVisuals提供助手类和模板。然而,这并不意味着在MFC应用中集成Visuals是不可能的。事实上,即使没有这些小助手类,仍然只需要几行代码就能在CWndCDialog中宿主一个Visual

这种集成的关键特性来自于Windows Presentation Foundation自身。Windows.PresentationCore.dll程序集提供了一个强有力的类HwndSource,它位于命名空间System::Windows::Interop中。这个类提供了HWNDVisual之间的桥。要宿主的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 CreateWindowExHwndSourceParameters

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 窗体

 

Revision

V1.1Windows Forms的翻译由“Windows表单”修改为“Windows 窗体”(517日)

posted on 2006-05-16 18:47  Steven Xiong  阅读(5126)  评论(2编辑  收藏  举报