OleContainerのフォーカス

解決


TKM  2007-04-05 18:05:03  No: 25643  IP: 192.*.*.*

OleContainerでExcelを表示させています。
表示直後の動作に問題はなく、編集などもできます。

が、一度フォーカスがOleContainerからはずれると
OleContainerに戻れなくなります。
また、ツールバーも消えます。
正確には、OleContainer上で右クリック-編集とやれば
編集できます。
この時点でツールバーも復活します。

常にOleContainerを編集状態にしておくことは、
できないのでしょうか?

編集 削除
Mr.XRAY  URL  2007-04-05 22:16:36  No: 25644  IP: 192.*.*.*

OleContainerにはどのような手順でExcelを表示させたのでしょう.
また,DelhiのバージョンとWindowsのバージョンは?
Excelのバージョンは?

編集 削除
TKM  2007-04-06 09:06:13  No: 25645  IP: 192.*.*.*

すみません。
環境を書くのを忘れてました。

Delphi5+WindowsXP+Excel2000です。

Excelを表示させている部分は、
//ExcelファイルをOleContainerオブジェクトとする
OleContainer.CreateObjectFromFile('C:\text.xls',False);
//OLEオブジェクトを表示
OleContainer.Visible:=True;
OleContainer.DoVerb(ovShow);
OleContainer.SetFocus;
こんな感じです。
Mr.XRAYさんのHPを参考にさせて頂きました。

UIActiveとかUIDeactivateあたりが怪しいと思われるのですが
このあたりがよくわかっていません・・・

編集 削除
TKM  2007-04-06 10:38:29  No: 25646  IP: 192.*.*.*

その後いろいろと調べていると、
OleCtnrs.pasの
procedure TOleContainer.CMUIDeactivate(var Message: TMessage);
begin
  if (GetParentForm(Self).ActiveOleControl = Self) and 
      (FOleInPlaceObject <> nil) then
      Exit;   ←追加
      //FOleInPlaceObject.UIDeactivate;  ←削除
end;

このようにすると見た目は問題なさそうですが、、、
Activeのときに動作がちょっとおかしくなる。
しかも、コンポーネントのソースは直接さわりたくないので。。。

何か良い方法はない物でしょうか?

編集 削除
TKM  2007-04-06 15:52:39  No: 25647  IP: 192.*.*.*

コンポーネントを直接変更せずに
今回のプロジェクトのみに適用する方法はわかりました。

{宣言部}
type
   TOleContainer = Class (OleCtnrs.TOleContainer)
   private
      procedure CMUIDeactivate(var Message: TMessage); message CM_UIDEACTIVATE;
   end;
...
...

{実行部}
procedure TOleContainer.CMUIDeactivate(var Message: TMessage);
begin
  if (GetParentForm(Self).ActiveOleControl = Self) then exit; 
end;

後は、この状態で表示されているExcelをクリックしたときに
Excelを編集できる状態になればよいのですが・・・
現在では、ダブルクリックするなど一度Excelのセルを
編集状態にするとExcel側にフォーカスが移動します。

表示されているExcelをクリックしてもOleContainerのイベントが
発生しないようなのでお手上げです。。。
もうちょっとなんですが。

編集 削除
clone好きやねん  2007-04-06 16:55:06  No: 25648  IP: 192.*.*.*

type
  TOleContainerEx = class(TOleContainer)
  private
    function  ShowToolBar: HRESULT;
    procedure CMUIDeactivate(var Message: TMessage); message CM_UIDeactivate;
    constructor CreateClone(Origin: TControl);
  end;

constructor TOleContainerEx.CreateClone(Origin: TControl);
var
  BinStream: TStream;
begin
  Create(Origin.Owner);
  BinStream := TMemoryStream.Create;
  try
    with TWriter.Create(BinStream, $800) do try
      Root := Origin.Owner;
      WriteSignature;
      WriteComponent(Origin);
      WriteListEnd;
    finally
      Free;
    end;
    with TReader.Create(BinStream, $800) do try
      Root := Origin.Owner;
      Parent := TControl(Origin).Parent;
      Origin.Free;
      Position := 0;
      BeginReferences;
      try
        ReadSignature;
        ReadComponent(Self);
        FixupReferences;
        AutoActivate := aaGetFocus;     // クリックでActive
      finally
        EndReferences;
      end;
    finally
      Free;
    end;
  finally
    BinStream.Free;
  end;
end;

procedure TOleContainerEx.CMUIDeactivate(var Message: TMessage);
begin
  exit;  // ナンもせーへん
end;

function TOleContainerEx.ShowToolBar: HRESULT;
var
  View: IOleDocumentView;
  Doc: IOleDocument;
begin
  result := ERROR;
  if Self.OleObjectInterface.QueryInterface(IOleDocument, Doc) <> 0 then Exit;
  if Doc = nil then Exit;
  if Doc.CreateView(Self, nil, 0, View) <> 0 then Exit;
  View.UIActivate(TRUE);
  result := NOERROR;
end;

procedure TForm1.FormCreate(Sender: TObject);
begin
  TOleContainerEx.CreateClone(OleContainer1);
  OleContainer1.CreateObjectFromFile(ExtractFilePath(Application.EXEName) + 'ほにゃ.xls', False);
  OleContainer1.Visible := True;
  OleContainer1.DoVerb(ovShow);
  OleContainer1.SetFocus;
  TOleContainerEx(OleContainer1).ShowToolBar;    // ExcelToolbar隠れとったら表示
end;

編集 削除
TKM  2007-04-06 17:49:25  No: 25649  IP: 192.*.*.*

clone好きやねんさん回答ありがとうございます。

試してみましたが、ダメでした。
マウスのクリック等は受け付けるのでちょっと前進です。
太字とか斜体とかは、効きます。
が、肝心のキーボード入力を受け付けてくれません。

同一フォームにOleContainerが乗ったPanel1と
TMemoなどが乗ったPanel2があり
一度、Panel2にフォーカスが当たると
Panel1に戻ったときにセルを編集しようとすると
ダブルクリックが必要です。
そのままキーボードをたたくとTMemoに文字が・・・

AutoActivate := aaGetFocus  

はクリックでActiveのはずなのでセルのクリックでも
アクティブになるかと思ったのですが・・・
どうも、OleContainerがクリックに反応してなさそうです。

せっかく教えて下さったのですが、
他によい手はない物でしょうか?

編集 削除
Mr.XRAY  URL  2007-04-07 00:51:01  No: 25650  IP: 192.*.*.*

FormにOleContainerだけであれば、常にFocusがあることになりますが、
他のコントロールを配置した場合は、わかりません。

OleContainerの場合、サーバが自分自身のウィンドウを持っている時には
必ずしもActiveプロパティが編集可能な状態にできるとは限らないからです。
ただし、外部のButton等からExcelのセルに値を代入したり、取得することは
は可能です。

それでも、Excelであれば、Excelのオブジェクトを
GetActiveOleObject
CreateOleObject
等で取得して、そのオブジェクトに対して操作することになります。
ネット上のサンプルがそうなっているのはそのためだと考えていい
と思います。

編集 削除
TKM  2007-04-09 12:56:58  No: 25651  IP: 192.*.*.*

Mr.XRAYさん回答ありがとうございます。

やはり、OleContainerを使うときは、
単独フォームにして他のフォームとは
分けた方よさそうですね。

セルをダブルクリックすると編集状態にできるので
セルのクリックで同じ動作ができれば・・・
と思ったのですが、難しそうですねぇ。
しかし、ダブルクリックしたときは
内部的にどうなってるんだろう。。。

編集 削除
TKM  2007-04-09 16:10:43  No: 25652  IP: 192.*.*.*

TExcelWorkSheetのSelectionChangeイベントに
下記のように書くとマウスでセルを変更した場合に
Excelにフォーカスを渡すことができました。
が、同じセルを選択するとイベントが発生しない・・・

何か良いタイミングは、無いものでしょうか?

var WinHnd   :   Integer;
begin

   // エクセルのハンドルを検索
   WinHnd := FindWindow('XLMAIN', nil);
   if  WinHnd > 0 then begin
       SetForegroundWindow(WinHnd);
   end;

編集 削除
TKM  2007-04-09 16:45:55  No: 25653  IP: 192.*.*.*

clone好きやねんさんに教えて頂いたクローンにイベントを追加
マウスがコントロールに入ったときにエクセルをアクティブにする。

{宣言部}
   TOleContainerEx = class(TOleContainer)
   private
      function    ShowToolBar: HRESULT;
      procedure   CMUIDeactivate(var Message: TMessage); message CM_UIDeactivate;
      procedure   CMMouseEnter(var Msg: TMessage); message CM_MOUSEENTER;  // <-追加
      constructor CreateClone(Origin: TControl);
   end;

{実行部}
・・・
procedure TOleContainerEx.CMMouseEnter(var Msg: TMessage);
var WinHnd   :   Integer;
begin
   // エクセルのハンドルを検索
   WinHnd := FindWindow('XLMAIN', nil);
   if WinHnd > 0 then begin
      SetForegroundWindow(WinHnd);
   end;
end;

・・・

とりあえず、これで何とか思うようになりました。
clone好きやねんさん、Mr.XRAYさんありがとうございました。

編集 削除