Блокирование документов при использовании TOleContainer
Автор Administrator   
11.03.2009 г.

Автор: Дамир, Королевство Delphi

Всем известно, что при использовании OleContainerа блокируются ранее открытые документы, и невозможно с ними ничего сделать.

Недавно встал вопрос: хорошо бы иметь возможность как-то разблокировать эти документы. Я по привычке полез в VCL и вот что нашел: У объекта TOleContainer имеется внутренняя переменная FDocView, указывающая на интерфейс IOleDocumentView и инициализирующаяся в начале активизации OleContainerа:
TOleContainer = class(TCustomControl, IUnknown, IOleClientSite,
  IOleInPlaceSite, IAdviseSink, IOleDocumentSite, IOleUIObjInfo)
  private
 
    FDocView: IOleDocumentView;

В свою очередь, у интерфейса IOleDocumentView есть метод Show(fShow: BOOL):

IOleDocumentView = interface(IUnknown)
    ['{b722bcc6-4e68-101b-a2bc-00aa00404770}']
 
    function Show(fShow: BOOL):HResult; stdcall;

Для "оживления" заблокированных документов, достаточно вызвать этот метод с параметром "false". Добавляем процедуру (или функцию с возможностью обработки ошибок, кому как больше нравится) ShowView(fShow : boolean) в TOleContainer:

public
    procedure ShowView(fShow : boolean); // Добавляем новую процедуру

и реализацию:

procedure TOleContainer.ShowView(fShow : boolean);
begin
  if Pointer(FDocView) <> nil then
  FDocView.Show(fShow);
end;

Для демонстрации работы создадим MDI приложение. Пусть дочернее окно содержит компонент OleContainer1 (Модуль, где находится новый класс TOleContainer я назвал OleCtnrs_):

unit Unit2;
 
interface
 
uses
  Windows, Messages, SysUtils, Variants, Classes, Graphics, Controls, Forms,
  Dialogs, OleCtnrs_, StdCtrls, ExtCtrls, ExcelXP, ActiveX;
 
type
  TForm2 = class(TForm) //ChildForm
    procedure FormCreate(Sender: TObject);
  private
    OleContainer1: TOleContainer;
  public
    { Public declarations }
    procedure OpenFile(FileName : string);
    procedure DoDocShow;
    procedure DoHideView;
  end;
 
var
  Form2: TForm2;
 
implementation
 
uses Unit1;
 
{$R *.dfm}
 
{Здесь открываем документ}
procedure TForm2.OpenFile(FileName : string);
begin
 OleContainer1.CreateObjectFromFile(FileName, false);
 DoDocShow;
end;
 
{Здесь вызываем ShowView }
procedure TForm2.DoHideView;
begin
 OleContainer1.ShowView(false);
end;
 
{Здесь снова активизируем OleContainer}
procedure TForm2.DoDocShow;
begin
 OleContainer1.DoVerb(ovShow);
end;
 
{Здесь создаем новый объект OleContainer}
procedure TForm2.FormCreate(Sender: TObject);
begin
 OleContainer1 := TOleContainer.Create(Self);
 OleContainer1.Parent := Self;
 OleContainer1.Align := alClient
end;
end.

Далее пишем родительское окно:

unit Unit1;
 
interface
 
uses
  Windows, Messages, SysUtils, Variants, Classes, Graphics, Controls, Forms,
  Dialogs, StdCtrls, ExtCtrls, OleCtnrs;
 
type
  TForm1 = class(TForm) //MDIForm
    GroupBox1: TGroupBox; // (Align := allBottom)TGroupBox, потому, что он не перекрывается дочерним окном.
    Button1: TButton; //Ставим кнопку на GroupBox1
    OpenDialog1: TOpenDialog;
    procedure Button1Click(Sender: TObject);
    {Испробовал всякие события, остановился на этих}
    procedure WMEraseBkgnd(var Message: TWMEraseBkgnd); message WM_ERASEBKGND;
    procedure WMKillFocus(var Message: TWMSetFocus); message WM_KILLFOCUS;
  private
    { Private declarations }
    function GetViewActive : boolean;
 
  public
    { Public declarations }
    property ViewActive : boolean read GetViewActive;
  end;
 
var
  Form1: TForm1;
 
implementation
 
uses Unit2;
 
{$R *.dfm}
 
function TForm1.GetViewActive : boolean;
begin
 result := MDIChildCount <> 0;
end;
 
procedure TForm1.WMKillFocus(var Message: TWMSetFocus);
begin
 if ViewActive then
 begin
  TForm2(MDIChildren[0]).DoDocShow;
 end;
 inherited;
end;
 
 
procedure TForm1.WMEraseBkgnd(var Message: TWMEraseBkgnd);
begin
 
 if ViewActive then
 begin
  TForm2(MDIChildren[0]).DoHideView;
 end;
 inherited;
end;
 
 
procedure TForm1.Button1Click(Sender: TObject);
begin
 if OpenDialog1.Execute then
 begin
  with TForm2.Create(Application) do
  begin
   Visible := true;
   OpenFile(OpenDialog1.FileName);
  end;
 end;
end;
 
end.

Программа работает следующим образом: Открывается, например, документ Excel. Затем запускается программа и открывается другой документ Excel, но уже внутри OleContainerа. При переключении между Excelем и программой появляется возможность работы с этими документами.

Программа работает со скрипом и скрежетом, но позволяет передавать фокус заблокированным документам. Может кому-то и пригодится. Остальные методы интерфейса IOleDocumentView я не исследовал, поэтому, возможно есть другие, более красивые решения.