Show/Hide Toolbars

XSharp

Navigation: X# 文档 > 将应用程序从 VO 迁移到 X#

示例 5: OCX - 电子邮件客户端示例

Scroll Prev Top Next More

 

本示例展示了如何迁移使用 ActiveX/OCX 控件的应用程序。

我们使用的是 Visual Objects 的电子邮件示例,您可以在 Examples 文件夹的子文件夹 Email 中找到该示例。

我们在这里遇到的问题是 X# Runtime(以及 Vulcan rumtime)不支持 ActiveX 控件。

因此,让我们尝试解决这个问题。

首先运行 VOXporter 并从 AEF 创建一个 Visual Studio 解决方案

在 Visual Studio 中编译并运行。

我们将收到两条信息:

 

Email1

 

第一条信息显示了本例中最大的问题。第二条信息是 Xporter 插入的,用来警告我们原始代码向 VO Gui 类中的一个类添加了一个方法。

让我们快速解决这些问题,以便编译应用程序。稍后我们将添加 OCX:

点击警告。您将看到 XPorter 添加了一个从 Toolbar 继承而来的 CLASS ToolBar_external_class。原始代码试图将 ShowButtonmenu 方法添加到现有的工具栏类中。

我们可以通过添加扩展方法或子类化工具栏类来解决这个问题,我们在 VOPAD 示例中也看到了这个问题。

就像在 VOPAD 示例中一样,我更喜欢扩展方法。

更改类名和方法声明。我们将创建 2 个重载,因为 symTb 参数是可选的:

STATIC CLASS ToolBarExtensions
  STATIC METHOD ShowButtonMenu(SELF tbSelf as Toolbar, nButtonID as LONG, oButtonMenu as Menu) AS VOID
     tbSelf:ShowButtonMenu(nButtonID, oButtonMenu, #MAINTOOLBAR)
    RETURN
STATIC METHOD ShowButtonMenu(SELF tbSelf as Toolbar, nButtonID as LONG, oButtonMenu as Menu,symTb as Symbol) AS VOID
 

删除 Default() 一行,并用 tbSelf 替换原始 ShowButtonMenu 主体中的 SELF

我们还需要对调用该方法的代码进行一些修改。这是因为代码在访问窗口类的工具栏时调用了 ShowButtonMenu。该工具栏访问是无类型的,因此返回 USUAL。
因此,请找到包含 SELF:ToolBar:ShowButtonMenu 的两行,并将其更改为 ((Toolbar) SELF:ToolBar):ShowButtonMenu。您不能使用 Window 类的 oToolbar 字段,因为 DataWindow 类将从其框架窗口返回工具栏,而不是自己的工具栏。
在改进后的 VO SDK 中,我们将通过强类型属性(如 Window:Toolbar)来解决这个问题。
也许您会想把扩展方法添加到 USUAL 类型中,这样您就不必在调用 ShowButtonMenu 的代码中添加转换了。
这样可以编译,但不幸的是,在运行时会出现问题。X# 编译器(就像 Vulcan 和 VO 编译器一样)知道 USUAL 类型是特殊的,因此不会尝试发出方法调用,而会产生调用 Send() 的代码来调用其方法。Vulcan 运行时不会处理 Send() 函数内部的扩展方法。

稍后,当我们按下工具栏上的 “回复 ”按钮时,就可以确认这是否有效。这将弹出菜单,其中包括 “回复发件人 ”和 “回复所有人”。 现在是修复 ActiveX/OCX 问题的时候了

单击有关 OleControl 的错误。

为了快速解决这个问题,我们可以修改代码,让 webbrowser 继承 MultiLineEdit。这样,我们就有了一个可以正常工作的控件。我们稍后将实现 OCX。 为此,请访问 Webbrowser.PRG 类并更改 INHERIT 子句。现在写的是 INHERIT OleControl。将其改为 INHERIT MultiLineEdit。

再次编译后会出现其他错误。其中 2 个提到了 cOleMethod 类型。双击该错误,转到该代码。

您将看到 Webbrowser 类的 Quit 方法。这段代码使用了 VO OLE 类中的一个内部类和内部方法。暂时注释掉该方法的内容。

再次编译后,你会发现只剩下一些错误。其中一些错误与 VOPAD 示例中的错误相同,需要我们将字体属性更改为 ControlFont。纠正这个错误。

一个错误是 “+”运算符不正确:在这一行中

cTemp +=  +"; "+ cEntry

这是原始 VO 代码中的一个明显错误,VO 编译器从未发现。删除双引号前的 +

最后一个错误来自 Webbrowser 类的构造函数。它调用了 OleControl 中的 CreateEmbedding 方法。这个方法在 MultiLineEdit 类中并不存在,因此我们暂时将其注释掉。我们稍后再处理 Webbrowser。

注释掉对 SELF:CreateEmbedding() 的调用后,其余代码的编译应该没有问题。

现在应该可以运行应用程序了。

如果您尝试打开地址簿,会出现运行时错误,因为它使用了依赖于 Cato3Cnt.dll 的数据库浏览器控件。将 Cavo28\Redist 文件夹中的 cato3*.dll 和 msvcrt.dll 复制到输出文件夹即可解决这个问题。

重新编译并运行示例。现在,在 Webbrowser 类的 Display 方法(如果您使用的是 VO 2.8 SP4 中的电子邮件示例,则为 DisplayHtml)中将出现错误。
该方法获取电子邮件内容,将其写入磁盘并调用 Webbrowser 控件的 Navigate 方法(后期绑定,使用 VO 的 Send() 函数)。这样做是行不通的。
由于我们已经更改了 Web 浏览器控件,使其成为一个多行编辑器,因此我们可以改变这种行为。我们可以简单地将电子邮件文本赋值给多行编辑器的 TextValue 属性,而不是将其写入磁盘。因此,请注释掉 Display 方法的主体(不要扔掉代码,我们稍后会用到它),并替换为

SELF:TextValue := cText

之后,示例运行就不会有问题了。您还可以显示电子邮件。当然,它不会正确显示 HTML,但这是下一步的工作。

如何在代码中添加 ActiveX

Vulcan 中与 VO 兼容的图形用户界面类不支持 ActiveX 控件。不过,Windows 窗体对 ActiveX 控件有很好的支持。

我们将使用 Windows 窗体的 ActiveX 支持,在示例中添加 ActiveX 控件。

这里有两种可能性:

1.用 Windows 窗体窗口替换整个电子邮件显示窗口

2.使用一个技巧,使用 Windows 窗体显示 ActiveX 控件,并将该控件合并到我们的 VO GUI 应用程序中

第一种解决方案是目前最容易理解的,但我们必须创建一个全新的窗口,还必须更改调用代码。

您可以根据自己的应用程序做出选择。

 

在本例中,我们将选择第二种方法。

创建一个 Windows 窗体窗口来显示电子邮件

在此方法中,我们使用 Windows.Forms.Form 窗口作为 ActiveX 控件的 “主机”。

我们将实例化该窗口,并获取该控件的窗口句柄,然后将该窗口句柄与我们的 VO GUI 窗口链接起来。

为此,您必须采取以下步骤:

右键单击解决方案资源管理器中的项目图标,选择 “添加新项目”。

这时会出现一个可能的新项目列表。选择 Windows 窗体表单图标,给它起一个有意义的名字,如 “EmailDisplayForm.prg”,然后单击 “添加”。

这将打开 “表单设计器 ”窗口。

打开工具箱。网络浏览器控件将不在其中。

右键单击工具箱中的空白区域,然后选择 “选择项目...”。这将弹出一个对话框,您可以在其中控制工具箱的内容。

选择 “COM 组件 ”选项卡并向下滚动,直到看到 Microsoft Web 浏览器控件:
 
Email3

勾选控件前面的复选框,然后按 “确定”。这将把 ActiveX 添加到工具箱中:
 

Email4

如果对控件的位置不满意,可以将其拖到工具箱中的其他位置。

现在将控件从工具箱拖到表单上。无需调整控件大小或移动控件。

Visual Studio 将为我们的项目添加两个引用。它们是

oAxSHDocVw, 包含实际 ActiveX 控件代码的类型库

oSHDocVw, 类型库,包含支持自动化服务器接口和类的代码

表单编辑器将为表单添加一个名为 axWebBrowser1 的字段。

转到属性窗口,更改 Modifiers 字段,将其从 “Private”改为 “Public”(Export)。
这将使该字段可以在 webBrowserHost 类之外访问

保存代码并关闭表单

现在进入 Webbrowser 类

在文件顶部添加以下 using 子句:

  using Email
  using AxShDocVw
  using ShDocVw

第一个命名空间是生成 webBrowserHost 窗口的命名空间。第二个命名空间是生成 ActiveX 的命名空间,第三个命名空间是我们需要的其他类型(如枚举和事件)的命名空间。

添加以下 2 个字段(我想没必要详细说明):

  EXPORT oHost as webBrowserHost
  EXPORT oWebBrowser as AxWebBrowser

转到 Webbrowser 类的构造函数,添加以下代码行(代替之前注释掉的 CreateEmbedding()

SELF:oHost := webBrowserHost{}           // 创建主机窗口,不显示 .NET 窗口!
SELF:oWebBrowser := SELF:oHost:axWebBrowser1   // 获取表单上的 ActiveX
SetParent(oWebBrowser:Handle, self:Handle())   // 使用 Windows API “窃取 ”其句柄并链接到 MLE
SELF:oWebBrowser:Visible := TRUE           // 使浏览器可见
SELF:Okay := TRUE

然后添加以下方法,以确保 ActiveX 与其所有者 MultiLineEdit 具有相同的高度,并确保其被正确销毁、

METHOD Resize(oEvent)
LOCAL oDim as Dimension
SUPER:Resize(oEvent)
oDim := SELF:Size
IF oDim:Width > 0
  SELF:oWebBrowser:SuspendLayout()
  SELF:oWebBrowser:Location := System.Drawing.Point{0,0}
  SELF:oWebBrowser:Size := System.Drawing.Size{oDim:Width,oDim:Height}
  SELF:oWebBrowser:ResumeLayout()
ENDIF
RETURN NIL
 
METHOD Destroy()
SUPER:Destroy()
SELF:oWebBrowser:Dispose()
SELF:oHost:Dispose()
RETURN NIL

我们需要 “恢复 ”以前的行为,在浏览器窗口中显示 HTML,而我们之前已经注释掉了。
因此,进入 WebBrowser:Display() 方法(VO 2.8 SP4 的 DisplayHtml),恢复旧代码并更改

Send(SELF, #Navigate, cFileName)

into

SELF:oWebBrowser:Navigate(cFileName)

因此,您可以将其改为早期绑定方法调用

要完成这项工作,请浏览 Webbrowser 类的源代码,找到调用 Navigate 的行,例如

Send( SELF, #Navigate, "#top" )

并将其改为早期绑定方法调用:

SELF:oWebBrowser:Navigate("#top" )

并查找类似的行:

Send( SELF, #ExecWB, OLECMDID_PRINT, OLECMDEXECOPT_DODEFAULT, NIL, NIL )

并将其更改为使用类型库中的枚举进行早期绑定方法调用。同时删除 NIL 值:

SELF:oWebBrowser:ExecWB(OLECMDID.OLECMDID_PRINT, OLECMDEXECOPT.OLECMDEXECOPT_DODEFAULT )

这样就结束了。现在一切正常,包括打印预览和打印功能。

当然,您现在也可以使用 ActiveX 事件并对其做出响应。
你必须用 .Net 的方法来做。类似这样

SELF:oWebBrowser:NavigateComplete2 += NavigateComplete2

然后执行

METHOD NavigateComplete2(sender AS OBJECT, e AS DWebBrowserEvents2_NavigateComplete2Event) AS VOID
      SELF:Owner:StatusBar:SetText("Showing file:" +e:uRL:ToString())

您可以在 XSharp 示例文件夹中找到 “Code before ”和 “Code after”。