トップ回答者
Windows10タブレットモードのモードレスダイアログの動作仕様

質問
-
回答
-
推奨されない方法で実装していたのですね。
修正する際は、スレッドに属したウィンドウ上で動作するようにしたいと思います。
どうもありがとうございました。
- 回答としてマーク tanaka.jugi 2017年5月24日 9:48
すべての返信
-
Windows フォーム アプリケーションでよろしかったでしょうか?
私の Windows 10 (x64) の環境でも再現しました。
モードレスウィンドウ B が閉じられたタイミングで、モードレスウィンドウ A を Active にするとよいと思います。
namespace WindowsFormsApplication1 { // モードレスA public partial class ModelessA : Form { public ModelessA() { InitializeComponent(); } private void button1_Click(object sender, EventArgs e) { ModelessB frmB = new ModelessB(); frmB.Show(); } } // モードレスB public partial class ModelessB : Form { public ModelessB() { InitializeComponent(); } private void ModelessB_FormClosed(object sender, FormClosedEventArgs e) { Application.OpenForms["ModelessA"].Activate(); } } }
-
ご返答有難うございます。
>Windows フォーム アプリケーションでよろしかったでしょうか?
はい、Windowsフォームアプリケーションです。
ご提示いただいた方法を試してみます。
- 編集済み tanaka.jugi 2017年5月15日 23:49
-
確認しましたところ、先ほどWindowsフォームアプリケーションと申し上げましたが、
WPFで開発していました。
親クラスはFormではなく、Window(System.Windows.Window)です。
この場合以下のようなWin32 APIを用いて最前面に表示すればよいということでしょうか?
[DllImport("user32.dll")] private static extern bool SetForegroundWindow(IntPtr hWnd);
- 編集済み tanaka.jugi 2017年5月16日 1:38
-
すみません。WPF アプリケーションですね。
試してみたところ、Win32 API の SetForegroundWindow を使っても、必要なタイミングで モードレス A のウィンドウを最前面に表示することができるようです。
上記の Window フォーム アプリケーションのコードに対応する書き方ですと、
モードレス B のウィンドウの閉じられたときに発生するイベント Closed で下記のように、戻るべきウィンドウ(モードレス A)の Activate を呼ぶことで表示することもできるようです。private void Window_Closed(object sender, EventArgs e) { var window = Application.Current.Windows.OfType<ModelessA>().FirstOrDefault(); // ModelessA の部分は戻るべきウィンドウのクラス名に書き換えてください if (window != null) { window.Activate(); } }
- 編集済み kenjinoteMVP 2017年5月16日 2:06
- 回答の候補に設定 立花楓Microsoft employee, Moderator 2017年5月16日 4:23
-
ご教授有難うございます。
上記のソースを参考に修正したところ、モードレスBが閉じるタイミングでモードレスAが表示されました。
しかしその後モードレスダイアログAが前に出たままでモードレスBを呼び出しても前に出てきません。
モードレスBのshowDialogの直後にActivateを呼んでも前に出てくれませんでした。
どのように回避すべきでしょうか?
<操作>
モードレスAの起動
→モードレスBの表示 window.showDialog();
→モードレスBが最前面に来る
→モードレスBのクローズ (Window_Closedメソッドの呼び出し)
→モードレスA window.Activate()
→モードレスAが最前面にくる
→再度モードレスBを表示
→モードレスAが最前面のままで、モードレスBが後ろに隠れている
- 編集済み tanaka.jugi 2017年5月17日 4:25
-
モードレスBのshowDialogの直後にActivateを呼んでも前に出てくれませんでした。
再現性は確認していませんが、念のためにおたずねします。
「モードレス B の showDialog」とありますが、記載間違いでしょうか?
ShowDialog だとモーダルになるためです。
ソースコードをもう一度確認しましたところ、該当するウィンドウは、「showDialog」で呼び出されていました。
それにも関わらず、動作上は、モーダルではなく、モードレスウィンドウの動作になっていました。
以下のようなソースコードになっています。
PreviewWindowがWindowを継承したクラスです。
・Queueに積んで実行しています。
・showDialogの前に、previewWindowにはOwnerを設定していません。
protected static readonly Thread PreviewThread = new Thread(new ThreadStart(previewWindowConsumer)); private static readonly Queue<Tuple<Camera, TakePictureArgument>> CommandQueue = new Queue<Tuple<Camera, TakePictureArgument>>(); ..... static public void previewWindowConsumer() { while (true) { lock (CommandQueue) { while (CommandQueue.Count > 0) { showPreviewWindow(CommandQueue.Dequeue()); } Monitor.Wait(CommandQueue); } } } static private void showPreviewWindow(Tuple<Camera, TakePictureArgument> data) { Camera self = data.Item1; TakePictureArgument arg = data.Item2; var previewWindow = new PreviewWindow(); previewWindow.ShowDialog(); var bitmap = previewWindow.GetCapturedBitmap(); ..... } }
-
WPF に限らず、Windows ではウィンドウはスレッドに属すると考えていただいたほうがよいです。
スレッドが異なる者同士でウィンドウの操作・関係についても期待通りにならないことがあります。
例えば、異なるスレッドの Window を Owner に指定しようとしても例外が起きるなどといった実害もあります。可能であればウィンドウを表示する仕組みの再設計を考えたほうがよいとは思います。
現状のままでの問題の解消策については何ともコメントできません。
通常はやらないことであり、避けるべき実装手法であるととらえているため、解決策があるかどうか含めてわからないということからです。
あるいは、タブレットモードを使う人は少ないとみなしてあきらめるかですが…。- 編集済み AzuleanMVP, Moderator 2017年5月24日 8:22
- 回答の候補に設定 立花楓Microsoft employee, Moderator 2017年5月25日 0:54
-
推奨されない方法で実装していたのですね。
修正する際は、スレッドに属したウィンドウ上で動作するようにしたいと思います。
どうもありがとうございました。
- 回答としてマーク tanaka.jugi 2017年5月24日 9:48