如何正确地在TForm中使用TOleContainer来布局使用Word?

7
我想知道在TForm中嵌入和控制MS Word的建议方法是什么,就布局而言?目前,我的做法是:(1)将两个TPanel放在TForm上。 alBottom TPanel有一个TButton,alClient TPanel有一个alNone TOleContainer。(2)在TMainForm.FormCreate事件处理程序中设置布局。
问题是MS Word喜欢占用其父窗体的所有空间。只有下面显示的第四种方式似乎给出了可接受的布局。根据试错,似乎需要使用子窗体而不是TPanel来托管TOleContainer。(此外,Windows.SetParent似乎是必要的。) 我想知道是否使用子窗体是正确的方法?
PS:Delphi XE,Word 2010,Windows 7
PS:与“托管外部应用程序”相关的网页: Binh Ly's site Deborah's site

如何将另一个应用程序嵌入到 Delphi 表单中

TOleContainer 重温

在 Delphi 中打开 Word 文档?

Delphi 和 Word (SimpChn)

PS:与“面板中的表单(子表单)”相关的网页:

如何在面板内创建透明表单?

Delphi - OleContainer - PowerPoint - AutoPlay

FreePascal/Lazarus MultiDoc

在面板中使用TForm

如何创建一个包含多个“子”窗体的Delphi窗体,这些窗体可以移动/调整大小并显示已激活状态

示例代码

unit uMainForm;

interface

uses
  Windows, Messages, SysUtils, Variants, Classes, Graphics, Controls, Forms,
  Dialogs, OleCtnrs, ExtCtrls, StdCtrls;

type
  TMainForm = class(TForm)
    PanelOle: TPanel;
    PanelBtn: TPanel;
    OleContainer1: TOleContainer;
    Button1: TButton;
    procedure Button1Click(Sender: TObject);
    procedure Button2Click(Sender: TObject);
    procedure FormCreate(Sender: TObject);
  private
    { Private declarations }
  public
    { Public declarations }
  end;

var
  MainForm: TMainForm;

implementation

{$R *.dfm}

procedure TMainForm.FormCreate(Sender: TObject);
var
  OleForm: TForm;
begin
////
//// 1
////
//  OleContainer1.Parent := PanelOle;
//  OleContainer1.Align := alClient;
//
////
//// 2
////
//  Windows.SetParent(OleContainer1.Handle, PanelOle.Handle);
//  OleContainer1.Align := alClient;
//
////
//// 3
////
//  OleForm := TForm.Create(Self);
//  OleForm.Parent := PanelOle;
//  OleForm.Align := alClient;
//  OleForm.Visible := True;
//  OleContainer1.Parent := OleForm;
//  OleContainer1.Align := alClient;
//
////
//// 4 Works!
////
//  OleForm := TForm.Create(Self);
//  Windows.SetParent(OleForm.Handle, PanelOle.Handle);
//  OleForm.Align := alClient;
//  OleForm.Visible := True;
//  OleContainer1.Parent := OleForm;
//  OleContainer1.Align := alClient;
//
////
//// 5
////
//  OleForm := TForm.Create(Self);
//  OleForm.Parent := PanelOle;
//  OleForm.Align := alClient;
//  OleForm.Visible := True;
//  Windows.SetParent(OleContainer1.Handle,OleForm.Handle);
//  OleContainer1.Align := alClient;
//
////
//// 6
////
//  OleForm := TForm.Create(Self);
//  Windows.SetParent(OleForm.Handle, PanelOle.Handle);
//  OleForm.Align := alClient;
//  OleForm.Visible := True;
//  Windows.SetParent(OleContainer1.Handle,OleForm.Handle);
//  OleContainer1.Align := alClient;

end;

procedure TMainForm.Button1Click(Sender: TObject);
var
// Declare the item to be a generic OleVariant to force late binding
  Ds: OleVariant;
  D: OleVariant;
begin
  OleContainer1.CreateObjectFromFile('sample.docx', False);

  OleContainer1.Run;

  OleContainer1.AutoActivate := aaManual;
  OleContainer1.DoVerb(ovShow);  // not in FormCreate, in or after FormShow

  Ds := OleContainer1.OleObject.Application.Documents;
  Caption := IntToStr(Ds.Count);
end;

end.

2
有点奇怪...不需要将另一个表单作为父级,只需不要使用'client'对齐包含ole容器的面板。将其顶部对齐,使用锚等,但不要在其上使用'alClient'。或者,将底部对齐的面板放置在具有ole容器的面板上,即使底部面板成为容器的兄弟也可以。 - Sertac Akyuz
@SertacAkyuz:非常感谢您的深入评论! 您提出的两个建议都有助于防止激活的MSWord的OleContainer扩展并与其他面板和按钮重叠!哇! 我想知道,您能否帮忙评论如何系统地学习您刚刚教授的这些知识? 我的意思是,是否有一些书籍可以推荐? :D - SOUser
@SertacAkyuz:现在,如果我在另一个面板上放置另一个不起作用的按钮,并单击它,则嵌入式Word的菜单将消失。这似乎与“激活/停用”有关,但我不知道该怎么做或寻找。您能否帮忙评论可能的解决方案?感谢您的时间! - SOUser
2
(1) 抱歉,我无法推荐任何东西,这只是一些试错。实际上,如果我知道为什么这有助于解决问题,我会提供答案。(2) 每当您需要重新激活OLE对象时,请调用“Show”动词。如果您不希望其完全停用,请使用非窗口化按钮,例如工具栏按钮或速度按钮。 - Sertac Akyuz
@SertacAkyuz:TOleContainer.DoVerb非常好用!再次感谢您的时间和耐心帮助! - SOUser
我无法回答你的问题,但我喜欢你收集的相关站点列表 :) - Edwin Yip
1个回答

2

子表单是一种正确的方法。我们在生产环境中使用了这种方法,并且它起作用了。我们将我们的“子”表单托管在一个面板中。但是,我们修改了TOleContainer和TOleForm,加入了一个标志,表示是使用父表单还是最顶层的表单:

procedure TOurOleContainer.InitObject;
...
begin
  if FDrawInTopForm then
    DocForm := GetParentForm(Self)
  else
    DocForm := TCustomForm(Parent);
...

我们引入了一个名为FDrawInTopForm的属性。我们还进行了修改:

function GetVCLFrameForm(Form: TCustomForm; DrawInTopForm: Boolean): IVCLFrameForm;
begin
  if Form.OleFormObject = nil then TOleForm.Create(Form, DrawInTopForm); 
  Result := Form.OleFormObject as IVCLFrameForm;
end;

很遗憾,由于与客户的协议,我不能在此发布完整的解决方案。


非常感谢您的回答!正如Sertac Akyuz所评论的那样,似乎没有必要将另一个窗体嵌套到父窗体中以获得合理的布局。您能否帮忙评论一下使用子窗体的优势? - SOUser
你好,不起作用。我创建了两个表单,OLE位于第二个表单上: `EmbeddedForm := TEmbeddedForm.Create(self); EmbeddedForm.Parent := Panel1; EmbeddedForm.Visible := True;` OleContainer1:= TMyOleContainer.Create(EmbeddedForm); OleContainer1.Top := 100; OleContainer1.Width := 600; OleContainer1.Parent := EmbeddedForm; 我的OleContainer: TMyOleContainer = class(Vcl.OleCtnrs.TOleContainer) procedure InitObject; end; procedure TMyOleContainer.InitObject; ... DocForm := TCustomForm(Parent); - gevaraweb
我创建了一个新的副本 Vcl.OleCtnrs.pas,进行了修改并将其放入项目文件夹中。现在它可以正常工作了! - gevaraweb
文件打开速度好像变慢了 :( - gevaraweb

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接