I wonder what is the suggested way to embed and control MS Word in a TForm, layout-wise? Currently, I (1) put two TPanel on TForm. The alBottom
TPanel has one TButton, and the alClient
TPanel has a alNone
TOleContainer. (2) set up the layout in TMainForm.FormCreate event handler.
The problem is MS Word likes to take up all the space of its parent form. Only the fourth way as shown below seems to give acceptable layout. Based on trial-and-error, it seems necessary to use a sub-form instead of TPanel to host TOleContainer. (Also, Windows.SetParent seems necessary.) I wonder whether a sub-form is the correct way to go?
PS: Delphi XE, Word 2010, Windows 7
PS: web pages related to "hosting external applications":
How to shell to another app and have it appear in a delphi form
PS: web pages related to "Form in Panel (sub-form)":
how to make a transparent form inside Panel?
Delphi - OleContainer - PowerPoint - AutoPlay
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.
Subform is a correct way to do it. We used this approach in a production environment and it worked. We hosted our "sub" form in a panel. However, we modified TOleContainer and TOleForm with a flag whether to use the parent form, or the topmost form:
procedure TOurOleContainer.InitObject;
...
begin
if FDrawInTopForm then
DocForm := GetParentForm(Self)
else
DocForm := TCustomForm(Parent);
...
Where FDrawInTopForm is a property we introduced. We also modified:
function GetVCLFrameForm(Form: TCustomForm; DrawInTopForm: Boolean): IVCLFrameForm;
begin
if Form.OleFormObject = nil then TOleForm.Create(Form, DrawInTopForm);
Result := Form.OleFormObject as IVCLFrameForm;
end;
Unfortunately, due to agreements with the customer, I cannot post the complete solution here.