--- lang: ja-jp breaks: true --- # WinForm Popup コントロールを閉じずに ダイアログボックス を表示したい 2022-04-09 > ToolStripDropDown クラス > https://docs.microsoft.com/ja-jp/dotnet/api/system.windows.forms.toolstripdropdown > Simple Popup Control > https://www.codeproject.com/Articles/17502/Simple-Popup-Control ## Windows標準のメッセージボックスであれば、ポップアップは閉じない。 ![](https://i.imgur.com/YuyL21h.png) :::info `ColorPicker`等のコモンダイアログも閉じずに動作した。 ::: ## 作成したForm画面を`ShowDialog`すると閉じてしまう。 ![](https://i.imgur.com/a6GQjmG.png) ## 色々やってみたが、`ShowDialog` の間、Popupコントロールの `OnClosing` イベントで `e.Cancel = true` とする方法でしか実現出来なかった。。 > ToolStripDropDown loses mouse autoclose > https://stackoverflow.com/questions/16150737/toolstripdropdown-loses-mouse-autoclose > 「Nope, I didn't a better solution. Simply avoid showing popup in popups. 」 :::info Windows標準のメッセージボックスは、OSに組み込まれたもので、ソースコードが公開されていない。 何かしら特殊な処理を行っているのではないかと思うが、実際にどうやって処理しているのかは私のレベルではわからない。。。 ::: #### `ShowDialog` のパラメータには `GetActiveWindow()` で取得したウインドウを指定する必要がある。 ```csharp= [DllImport(ExternDll.User32, ExactSpelling = true, CharSet = CharSet.Auto)] [ResourceExposure(ResourceScope.Process)] public static extern IntPtr GetActiveWindow(); IntPtr handle = IntPtr.Zero; handle = GetActiveWindow(); NativeWindow owner = null; try { owner = new NativeWindow(); owner.AssignHandle(handle); using (var dialog = new MessageDialog()) { dialog.StartPosition = FormStartPosition.CenterParent; dialog.ShowDialog(owner); // 必須 } } finally { owner?.ReleaseHandle(); } ``` #### `Popup.cs` 変更のポイント ```csharp= bool m_isOpened = false; bool m_isKillFocus = false; // UIスレッド上でモーダルウインドウ表示の有無を検知 static Popup() { Application.EnterThreadModal += Application_EnterThreadModal; Application.LeaveThreadModal += Application_LeaveThreadModal; } // フラグでモーダルの状態を管理 static bool s_isApplication_EnterThreadModal = false; private static void Application_EnterThreadModal(object sender, EventArgs e) { s_isApplication_EnterThreadModal = true; } private static void Application_LeaveThreadModal(object sender, EventArgs e) { s_isApplication_EnterThreadModal = false; } // プログラムからVisibleプロパティに値をセットしたことを管理 // `Visible`に`true`をセットすると、`OnOpening`、`OnOpened`が呼び出される。 bool m_isSetVisible = false; private void SetVisible(bool visible) { m_isSetVisible = true; base.Visible = visible; } protected override void OnOpened(EventArgs e) { m_isOpened = true; m_isKillFocus = false; // ポップアップがオープンした後のダイアログにのみ反応するようにここで初期化する。 s_isApplication_EnterThreadModal = false; if (m_isSetVisible) { // プログラムから呼び出した場合は何もしない。 base.OnOpened(e); m_isSetVisible = false; return; } ・・・ base.OnOpened(e); } protected override void OnClosing(ToolStripDropDownClosingEventArgs e) { if (s_isApplication_EnterThreadModal) { // モーダル状態であれば、ポップアップを閉じないように制御。 if (e.CloseReason != ToolStripDropDownCloseReason.CloseCalled) { e.Cancel = true; } } base.OnClosing(e); } protected override void OnClosed(ToolStripDropDownClosedEventArgs e) { ・・・ m_isOpened = false; if (m_ownerPopup != null) { m_ownerPopup.m_isChildPopupOpened = false; // `e.Cancel = true;`を実行すると、その後のポップアップの挙動が // 不安定となるので、`Visible = true` を呼び出して少しでも安定化させる。 // これにより、`OnOpening`、`OnOpened`が再度呼び出されてしまうので注意。 m_ownerPopup.SetVisible(true); } base.OnClosed(e); // 以下、`e.Cancel = true;`を実行すると、その後のポップアップの挙動が // 不安定となるので、自力で閉じる動作を制御。 if (m_childPopup != null) { if (m_childPopup.m_isOpened) { m_childPopup.Close(e.CloseReason); } } if (m_ownerPopup != null) { if (m_ownerPopup.m_isOpened) { if (e.CloseReason == ToolStripDropDownCloseReason.AppClicked) { m_ownerPopup.Close(e.CloseReason); } else if (e.CloseReason == ToolStripDropDownCloseReason.AppFocusChange) { if (m_ownerPopup.m_isKillFocus) { m_ownerPopup.Close(e.CloseReason); } } } } } public const int WM_KILLFOCUS = 0x0008; protected override void WndProc(ref Message m) { ・・・ if (m.Msg == WM_KILLFOCUS) { m_isKillFocus = true; } } ``` :::warning 上記のコードでは正常に動作しないパターンがあり、解決に至らなかったため、没となった。 ::: ## WPF の `Window` で作成したダイアログであれば、ポップアップが閉じられることなく表示できた ![](https://i.imgur.com/kz5ay0L.png) ## WPF の `Window` でも、`WindowsFormsHost` コントロールを使用するとポップアップが閉じてしまう。 ## しょうがないので、ポップアップからダイアログを出したい場合は、ダイアログの内容をポップアップに内包してポップアップとして表示することで妥協する。 ###### tags: `WinForm` `Popup` `ToolStripDropDown` `ポップアップコントロール` `WPF`