🎄teratailクリスマスプレゼントキャンペーン2024🎄』開催中!

\teratail特別グッズやAmazonギフトカード最大2,000円分が当たる!/

詳細はこちら
C#

C#はマルチパラダイムプログラミング言語の1つで、命令形・宣言型・関数型・ジェネリック型・コンポーネント指向・オブジェクティブ指向のプログラミング開発すべてに対応しています。

Visual Studio

Microsoft Visual StudioはMicrosoftによる統合開発環境(IDE)です。多種多様なプログラミング言語に対応しています。

.NET Framework

.NET Framework は、Microsoft Windowsのオペレーティングシステムのために開発されたソフトウェア開発環境/実行環境です。多くのプログラミング言語をサポートしています。

Q&A

解決済

2回答

7733閲覧

Microsoft.Office.Interop.Excelで生成したExcelが呼び出し元アプリの背面で表示されるようにしたい

Masahiro-Ito

総合スコア24

C#

C#はマルチパラダイムプログラミング言語の1つで、命令形・宣言型・関数型・ジェネリック型・コンポーネント指向・オブジェクティブ指向のプログラミング開発すべてに対応しています。

Visual Studio

Microsoft Visual StudioはMicrosoftによる統合開発環境(IDE)です。多種多様なプログラミング言語に対応しています。

.NET Framework

.NET Framework は、Microsoft Windowsのオペレーティングシステムのために開発されたソフトウェア開発環境/実行環境です。多くのプログラミング言語をサポートしています。

1グッド

0クリップ

投稿2019/12/19 06:52

前提・実現したいこと

C#を使って、Excelを制御するプログラムを作っています。
Microsoft.Office.Interop.Excel名前空間のApplicationクラスを使っています。

呼び出し元となるC#プログラムにUIがあり、生成したExcelが呼び出し元プログラムの背面に表示されるようにしたいです。
タスクバーでExcelのアイコンをクリックしても、呼び出し元プログラムの前面には出てきて欲しくないです。
イメージ的には、Excelの前面に表示したプロパティウインドウで何か入力し背面のExcelに反映させる、のようなUIを考えています。
恐れ入りますが、何か解決策はあるでしょうか。

なお、他のアプリケーションは、操作されれば通常のアプリ通りに、前面に表示されるようにしたいので、フォームのTopMostプロパティをTrueに設定するというのは、やめたいと思っています。

試したこと

Microsoft.Office.Interop.Excel名前空間で生成したExcelインスタンスのWindowActivateイベントで、イベント検知時に呼び出し元アプリケーションをアクティブにする、ということを試したのですが、このイベントはExcel内でのアクティブなWindowが切り替わった時に発生するもので、タスクバーのExcelをクリックしても発生しませんでした。そのため、この方法は断念しました。

C#

1private void Form1_Load(object sender, EventArgs e) 2{ 3 // Excelアプリケーションを生成し、イベントハンドラを割り当てる 4 var application = new Microsoft.Office.Interop.Excel.Application(); 5 application.WindowActivate += this.Application_WindowActivate; 6 application.WorkbookActivate += this.Application_WorkbookActivate; 7 application.Visible = true; 8} 9 10private void Application_WorkbookActivate(Workbook Wb) 11{ 12 // Excelアプリケーションの中でアクティブなブックが切り替わった時にイベント発生する 13 System.Diagnostics.Debug.Print($"{DateTime.Now.ToString()} Application_WorkbookActivate Wb.FullName:{Wb.FullName}"); 14} 15 16private void Application_WindowActivate(Workbook Wb, Window Wn) 17{ 18 // Excelアプリケーションの中でアクティブなウインドウが切り替わった時にイベント発生する 19 System.Diagnostics.Debug.Print($"{DateTime.Now.ToString()} Application_WindowActivate Wb.FullName:{Wb.FullName}"); 20}

補足情報(FW/ツールのバージョンなど)

今回のテスト環境は以下の通りです。

  • Visual Studio 2019
  • C#
  • .NET Framework 4.5
  • Microsoft Excel 2010(たまたまテスト環境が2010でした)
  • Windows 10
atata0319👍を押しています

気になる質問をクリップする

クリップした質問は、後からいつでもMYページで確認できます。

またクリップした質問に回答があった際、通知やメールを受け取ることができます。

バッドをするには、ログインかつ

こちらの条件を満たす必要があります。

guest

回答2

0

Excelの前面に表示したいフォームのオーナーをExcelのハンドルを割り当てたNativeWindowにすることで解決しました。
自身のオーナーをExcelのハンドルを割り当てたNativeWindowにすることでフォームは常にExcelの前面に表示されます。

以下のサンプルコードは、前面に表示したいフォームのオーナーをExcelのハンドルを割り当てたNativeWindowにするものです。

こんな風に表示されるようになります。
Excelをクリックしても、Formは前面表示されたままです。
イメージ説明

C#

1public partial class Form1 : Form 2{ 3 4 private void Form1_Load(object sender, EventArgs e) 5 { 6 7 // Excel生成 8 var app = new Microsoft.Office.Interop.Excel.Application 9 { 10 Visible = true 11 }; 12 13 // NativeWindowにExcelアプリケーションのハンドルを割り当てる 14 var nw = new NativeWindow(); 15 nw.AssignHandle((IntPtr)app.Hwnd); 16 17 // ハンドルが割り当てられたNativeWindowをオーナーにしてFormを開く 18 // ここで生成するForm は自作ユーザーフォームでも可 19 var form = new Form(); 20 form.Show(nw); 21 22 // 前面に表示したフォームが閉じられた時に自身を閉じるイベントハンドラ 23 form.FormClosed += this.Form_FormClosed; 24 25 } 26 27 private void Form_FormClosed(object sender, FormClosedEventArgs e) 28 { 29 this.Close(); 30 } 31 32 private void Form1_Shown(object sender, EventArgs e) 33 { 34 35 ((Form)sender).Visible = false; 36 37 } 38 39} 40

投稿2019/12/23 06:16

編集2019/12/25 04:54
Masahiro-Ito

総合スコア24

バッドをするには、ログインかつ

こちらの条件を満たす必要があります。

TN8001

2019/12/23 07:51

SetParentも試しましたが、 > (MDIの子フォームのように表示される) の状態で早々にあきらめてました^^; Excel終了で自アプリも終わってしまうようですが、それでいいならシンプルで良さそうですね。
Masahiro-Ito

2019/12/23 13:13

> この形式なら、NativeWindowでもできるかもしれませんね すみません。NativeWindow がよく分からないのですが、先に投稿したソースコードのForm1はExcelの子ウインドウになるだけなのでNativeWindowでも可、という意味でしょうか。 (初心者?でもないのですが、やり始めた人みたいな返答で申し訳ございません)
imihito

2019/12/23 14:25 編集

SetParent以降を下記のコードのようにするイメージです。 ```cs NativeWindow nw = new NativeWindow(); nw.AssignHandle((IntPtr)app.Hwnd); var form2 = new Form2(); form2.Show(nw); ``` NativeWindowは本来は継承用のクラスみたいですが、そのままでもAssignHandleすれば`Form.Show()`の引数として使えるので「(Excelのハンドルを示す)NativeWindowの子フォームとしてForm2を開く」ができるのかな?と。 また参考情報ですが、手元のExcel2016(=SDI)で試したところ、SetParent・NativeWindowのどちらでも思ったように動作しませんでした(フォームが消えてしまう)。 NativeWindowの方は`frm.Visible = false;`の代わりに `frm.WindowState = FormWindowState.Minimized;`とすれば、とりあえず動作しました。
Masahiro-Ito

2019/12/24 11:26

SDIのExcel2016で動かないのはよろしくないですね。2010で動いて満足していました。NativeWindow試してみます。
Masahiro-Ito

2019/12/25 04:30 編集

> また参考情報ですが、手元のExcel2016(=SDI)で試したところ、SetParent・NativeWindowの > どちらでも思ったように動作しませんでした(フォームが消えてしまう)。 同じ症状か分かりませんが、SetParent関数でExcelをユーザーフォームの親に設定すると、この処理とは全然関係ない別のフォームが表示されなくなるという現象を確認しました。サンプルで載せたコードだと、フォームの数が少ないからか問題なく動くのですが、普通のソフトの1機能として組み込むと変な動作をします。 SetParent関数でユーザーフォームの親にExcelアプリケーションを設定する、というのはやってはいけない処理なのかもしれませんね。
Masahiro-Ito

2019/12/25 05:02

自己解決欄にサンプルコードを投稿しました (自己解決ではないですが...。) ありがとうございます。
guest

0

ベストアンサー

windows - C# 外部プログラムを起動させた際に、特定のフォームよりも前面に表示する方法 - スタック・オーバーフロー
こちらの逆ってことでしょうか。

渡すハンドルを逆にしたところ、メモ帳ではフォームの背面になりました。(2つメモ帳を開くと古いほうは解除されてしまいました)

投稿2019/12/19 14:59

TN8001

総合スコア9855

バッドをするには、ログインかつ

こちらの条件を満たす必要があります。

TN8001

2019/12/19 15:11

リンク先のコメントでExcelはダメだったとの言及がありました。
Masahiro-Ito

2019/12/20 01:14

ご回答ありがとうございます。 Excelでも試してみる価値はありそうですね。やってみます。
Masahiro-Ito

2019/12/20 04:13

ありがとうございます。 お教え頂いた方法でMicrosoft.Office.Interop.Excelで生成したExcelが常に背面表示されるようにできました。 実行したコードは以下の通りです。先のご回答のリンク先にあるAPI宣言やメソッドが定義されていることが前提です。 // Excel生成 var app = new Microsoft.Office.Interop.Excel.Application { Visible = true }; var p = System.Diagnostics.Process.GetCurrentProcess(); if (p.MainWindowHandle != IntPtr.Zero) { // p は app.Hwndより前面 SetWindowLongPtr(new System.Runtime.InteropServices.HandleRef(p, p.MainWindowHandle), GWL_HWNDPARENT, (IntPtr)app.Hwnd); }
TN8001

2019/12/20 04:29

できましたか! 先にコメントを見ていたら回答を控えていたかもしれないので、 ぼやっとしてて良かったです^^;
Masahiro-Ito

2019/12/23 05:40

できて良かった、と思ってたのですが、docs.microsoft.comのSetWindowLongPtrA関数の説明を見ると、 GWL_HWNDPARENTでSetWindowLong関数を呼び出すことで親ウインドウを変更しないでください 代わりにSetParent関数を使って下さい と記載されていました。上記コードでも動作するのですが、ちょっと調べてみようと思います。
guest

あなたの回答

tips

太字

斜体

打ち消し線

見出し

引用テキストの挿入

コードの挿入

リンクの挿入

リストの挿入

番号リストの挿入

表の挿入

水平線の挿入

プレビュー

15分調べてもわからないことは
teratailで質問しよう!

ただいまの回答率
85.36%

質問をまとめることで
思考を整理して素早く解決

テンプレート機能で
簡単に質問をまとめる

質問する

関連した質問