OleContainerでExcelを表示させています。
表示直後の動作に問題はなく、編集などもできます。
が、一度フォーカスがOleContainerからはずれると
OleContainerに戻れなくなります。
また、ツールバーも消えます。
正確には、OleContainer上で右クリック-編集とやれば
編集できます。
この時点でツールバーも復活します。
常にOleContainerを編集状態にしておくことは、
できないのでしょうか?
OleContainerにはどのような手順でExcelを表示させたのでしょう.
また,DelhiのバージョンとWindowsのバージョンは?
Excelのバージョンは?
すみません。
環境を書くのを忘れてました。
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あたりが怪しいと思われるのですが
このあたりがよくわかっていません・・・
その後いろいろと調べていると、
OleCtnrs.pasの
procedure TOleContainer.CMUIDeactivate(var Message: TMessage);
begin
if (GetParentForm(Self).ActiveOleControl = Self) and
(FOleInPlaceObject <> nil) then
Exit; ←追加
//FOleInPlaceObject.UIDeactivate; ←削除
end;
このようにすると見た目は問題なさそうですが、、、
Activeのときに動作がちょっとおかしくなる。
しかも、コンポーネントのソースは直接さわりたくないので。。。
何か良い方法はない物でしょうか?
コンポーネントを直接変更せずに
今回のプロジェクトのみに適用する方法はわかりました。
{宣言部}
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のイベントが
発生しないようなのでお手上げです。。。
もうちょっとなんですが。
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;
clone好きやねんさん回答ありがとうございます。
試してみましたが、ダメでした。
マウスのクリック等は受け付けるのでちょっと前進です。
太字とか斜体とかは、効きます。
が、肝心のキーボード入力を受け付けてくれません。
同一フォームにOleContainerが乗ったPanel1と
TMemoなどが乗ったPanel2があり
一度、Panel2にフォーカスが当たると
Panel1に戻ったときにセルを編集しようとすると
ダブルクリックが必要です。
そのままキーボードをたたくとTMemoに文字が・・・
AutoActivate := aaGetFocus
はクリックでActiveのはずなのでセルのクリックでも
アクティブになるかと思ったのですが・・・
どうも、OleContainerがクリックに反応してなさそうです。
せっかく教えて下さったのですが、
他によい手はない物でしょうか?
FormにOleContainerだけであれば、常にFocusがあることになりますが、
他のコントロールを配置した場合は、わかりません。
OleContainerの場合、サーバが自分自身のウィンドウを持っている時には
必ずしもActiveプロパティが編集可能な状態にできるとは限らないからです。
ただし、外部のButton等からExcelのセルに値を代入したり、取得することは
は可能です。
それでも、Excelであれば、Excelのオブジェクトを
GetActiveOleObject
CreateOleObject
等で取得して、そのオブジェクトに対して操作することになります。
ネット上のサンプルがそうなっているのはそのためだと考えていい
と思います。
Mr.XRAYさん回答ありがとうございます。
やはり、OleContainerを使うときは、
単独フォームにして他のフォームとは
分けた方よさそうですね。
セルをダブルクリックすると編集状態にできるので
セルのクリックで同じ動作ができれば・・・
と思ったのですが、難しそうですねぇ。
しかし、ダブルクリックしたときは
内部的にどうなってるんだろう。。。
TExcelWorkSheetのSelectionChangeイベントに
下記のように書くとマウスでセルを変更した場合に
Excelにフォーカスを渡すことができました。
が、同じセルを選択するとイベントが発生しない・・・
何か良いタイミングは、無いものでしょうか?
var WinHnd : Integer;
begin
// エクセルのハンドルを検索
WinHnd := FindWindow('XLMAIN', nil);
if WinHnd > 0 then begin
SetForegroundWindow(WinHnd);
end;
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さんありがとうございました。
ツイート | ![]() |