none
Windows10タブレットモードのモードレスダイアログの動作仕様 RRS feed

  • 質問

  • C#にてWindowsアプリケーションを開発しています。(非ストアアプリ)

    Windows10タブレットモードの動作中に、
    モードレスウィンドウA(呼び出し元)からモードレスウィンドウBを呼んだ後で、
    ウィンドウBを閉じると、スタート画面が出てきてしまいます。

    こちらは呼び出し元のモードレスウィンドウAが出てきてくれることを期待しています。

    本件はWindows10タブレットモードの仕様と考えているのですが、認識は合っていますか?
    また、呼び出し元のウィンドウに戻る方法はありますでしょうか?
    ウィンドウBをモーダルに変えるしかないのでしょうか?


    2017年5月15日 5:53

回答

  • 推奨されない方法で実装していたのですね。

    修正する際は、スレッドに属したウィンドウ上で動作するようにしたいと思います。

    どうもありがとうございました。

    • 回答としてマーク tanaka.jugi 2017年5月24日 9:48
    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();
            }
        }
    }
    2017年5月15日 11:35
  • ご返答有難うございます。

    >Windows フォーム アプリケーションでよろしかったでしょうか?

    はい、Windowsフォームアプリケーションです。

    ご提示いただいた方法を試してみます。


    2017年5月15日 23:49
  • 確認しましたところ、先ほどWindowsフォームアプリケーションと申し上げましたが、

    WPFで開発していました。

    親クラスはFormではなく、Window(System.Windows.Window)です。

    この場合以下のようなWin32 APIを用いて最前面に表示すればよいということでしょうか?

    [DllImport("user32.dll")]
    private static extern bool SetForegroundWindow(IntPtr hWnd);

    2017年5月16日 1:22
  • すみません。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();
        }
    }
    2017年5月16日 2:01
  • ご教授有難うございます。

    上記のソースを参考に修正したところ、モードレスBが閉じるタイミングでモードレスAが表示されました。

    しかしその後モードレスダイアログAが前に出たままでモードレスBを呼び出しても前に出てきません。

    モードレスBのshowDialogの直後にActivateを呼んでも前に出てくれませんでした。

    どのように回避すべきでしょうか?


    <操作>

    モードレスAの起動

    →モードレスBの表示 window.showDialog();

    →モードレスBが最前面に来る

    →モードレスBのクローズ (Window_Closedメソッドの呼び出し)

    →モードレスA window.Activate()

    →モードレスAが最前面にくる

    →再度モードレスBを表示

    モードレスAが最前面のままで、モードレスBが後ろに隠れている



    2017年5月17日 4:25
  • モードレスBのshowDialogの直後にActivateを呼んでも前に出てくれませんでした。

    再現性は確認していませんが、念のためにおたずねします。

    「モードレス B の showDialog」とありますが、記載間違いでしょうか?
    ShowDialog だとモーダルになるためです。

    2017年5月17日 13:40
    モデレータ
  • モードレス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();
                   .....
             }
            }
    2017年5月24日 6:47
  • WPF に限らず、Windows ではウィンドウはスレッドに属すると考えていただいたほうがよいです。
    スレッドが異なる者同士でウィンドウの操作・関係についても期待通りにならないことがあります。
    例えば、異なるスレッドの Window を Owner に指定しようとしても例外が起きるなどといった実害もあります。

    可能であればウィンドウを表示する仕組みの再設計を考えたほうがよいとは思います。
    現状のままでの問題の解消策については何ともコメントできません。
    通常はやらないことであり、避けるべき実装手法であるととらえているため、解決策があるかどうか含めてわからないということからです。


    あるいは、タブレットモードを使う人は少ないとみなしてあきらめるかですが…。
    2017年5月24日 8:12
    モデレータ
  • 推奨されない方法で実装していたのですね。

    修正する際は、スレッドに属したウィンドウ上で動作するようにしたいと思います。

    どうもありがとうございました。

    • 回答としてマーク tanaka.jugi 2017年5月24日 9:48
    2017年5月24日 9:48