none
FolderBrowserDialogを連続してShowDialogすると2回目以降は最前面に表示されない。 RRS feed

  • 質問

  • WinXP SP2とVB2008 ExpressEditionで開発しています。

     

    ユーザーに2つのディレクトリを選択させるためにFolderBrowserDialogを使おうとしています。

    最初のFolderBrowserDialogは最前面に表示されるのですが、その次のFolderBrowserDialogが

    必ず他のウィンドウの背面に隠れてしまいます。

     

          '1回目のディレクトリ選択

                    If Me.FolderBrowserDialog1.ShowDialog = Windows.Forms.DialogResult.OK Then
                        If Not Me.FolderBrowserDialog1.SelectedPath.Contains("C:\xxx") Then
                            System.Windows.Forms.MessageBox.Show("選択できるのは""C:\xxx""以下のディレクトリのみです。", "ディレクトリ選択エラー", MessageBoxButtons.OK, MessageBoxIcon.Error)
                            Exit Sub
                        End If

     

                        Dim strDir As String                         
                        strDir = Me.FolderBrowserDialog1.SelectedPath

     

    '2回目のディレクトリ選択

                        Me.FolderBrowserDialog1.Description = "●●●が入ったフォルダを選択して下さい。"

                            If Me.FolderBrowserDialog1.ShowDialog = Windows.Forms.DialogResult.OK Then
                            Dim strDir2 As String
                                strDir2 = Me.FolderBrowserDialog1.SelectedPath
                            Else
                                '処理コード記述
                            End If
                    Else
                        '処理コード記述
                    End If

     

    簡単ですが、上記のようなコードです。

    FolderBrowserDialogのプロパティにそれらしいのを発見できませんでしたのでお知恵を拝借したく投稿させて頂きました。

     

    よろしくお願いいたします。

    2008年7月3日 10:58

回答

  • モーダルが閉じた後に親ウィンドウがアクティブになるのは、ウィンドウメッセージが送られているからです。

    プロシージャ実行中はそのウィンドウに送られたメッセージはキューに溜まり、プロシージャを抜けた時点で処理されます。

    書かれたコードではプロシージャを抜けていませんので、親ウィンドウが非アクティブの状態でダイアログを表示することになります。

    つまり、非アクティブということは他のウィンドウがアクティブです。

    ダイアログは指定された親ウィンドウの一つ上層に表示されるだけですから、デスクトップの一番上に出てこないというわけです。

     

    解決策としては、親ウィンドウをアクティブにすることです。

    そしてそのタイミングでメッセージを強制的に処理させることです。

    System.Windows.Forms.Application.DoEvents() を調べてください。

     

    同じことが下記についても言えます。

     

    Code Snippet

    Private Sub Test()

        Me.labelInfo.Text = "abc"

        For Index As Integer = 0 To 100000

        Next

    End Sub

     

     

    ラベルのテキストがプロシージャを抜けてから描画されるはずです。

     

    2008年7月3日 11:27
  • 基本的には同じことです。

    そしてLoadであればウィンドウがいないので前面に持ってきようがありませんよね。
    つまりダイアログに対して前面に来るように仕向けるしかありません。
    そうなると最悪APIレベルのことをしなくてはいけません。

     

    と書いておきながら、Vista+2005で試してみましたが現象は出ませんでした。
    もしかしたらですがShowDialog(Me) で試してみてください。
    親ウィンドウを指定していないことが原因かもしれません。
    LoadではウィンドウハンドルがOSレベルでまだ作成されていないと推定され
    Meを指定することによりウィンドウハンドルがその場で強制的に作成されるためと想像できます。

     

    ただし、うまくいっても、OSの状況により現象が発生する可能性は捨てきれません。

    関連して、かつ、別件ですが、
    親フォームを見せたくない特別な理由をのぞいて
    Shown イベントで処理することをおすすめします。

    2008年7月3日 12:52
  • 本筋とずれますが、気になったので書いておきます。

     

     hone さんからの引用

                        If Not Me.FolderBrowserDialog1.SelectedPath.Contains("C:\xxx") Then
                            System.Windows.Forms.MessageBox.Show("選択できるのは""C:\xxx""以下のディレクトリのみです。", "ディレクトリ選択エラー", MessageBoxButtons.OK, MessageBoxIcon.Error)
                            Exit Sub
                        End If

    エラー表示した後、そのフォームは表示されますが、問題ないのでしょうか?

    (Exit SubはLoadイベントの残りの処理を実行しないだけで、アプリケーションは終了されません。)

    2008年7月3日 13:42
    モデレータ
  •  hone さんからの引用

    美しくないですが、Exit Subではなく、Endとして終了させています。

     

    なおさら、Shown イベントに処理を移行してMe.Close()としたほうがよいでしょう。

    コンストラクタやLoadでは自クラスの初期化に徹して、ユーザーにアクションさせるようなUI操作は好ましくありません。

    イベントドリブンでの待機状態に入ったシグナルがShown イベント以降であり、それ以前は準備段階ととらえたほうがよいでしょう。

     

    2008年7月3日 14:36

すべての返信

  • モーダルが閉じた後に親ウィンドウがアクティブになるのは、ウィンドウメッセージが送られているからです。

    プロシージャ実行中はそのウィンドウに送られたメッセージはキューに溜まり、プロシージャを抜けた時点で処理されます。

    書かれたコードではプロシージャを抜けていませんので、親ウィンドウが非アクティブの状態でダイアログを表示することになります。

    つまり、非アクティブということは他のウィンドウがアクティブです。

    ダイアログは指定された親ウィンドウの一つ上層に表示されるだけですから、デスクトップの一番上に出てこないというわけです。

     

    解決策としては、親ウィンドウをアクティブにすることです。

    そしてそのタイミングでメッセージを強制的に処理させることです。

    System.Windows.Forms.Application.DoEvents() を調べてください。

     

    同じことが下記についても言えます。

     

    Code Snippet

    Private Sub Test()

        Me.labelInfo.Text = "abc"

        For Index As Integer = 0 To 100000

        Next

    End Sub

     

     

    ラベルのテキストがプロシージャを抜けてから描画されるはずです。

     

    2008年7月3日 11:27
  • まどかさん。ご回答ありがとうございます。

     

    少々、言葉足らずでしたが、記述したコードはFormのLoad中に書いています。

    アプリケーションを起動する前に、ユーザーに2つのディレクトリを指定して欲しいのです。

     

    「親ウィンドウが非アクティブの状態でダイアログを表示することになる」と、言う事は

    親フォームのLoadが完了していないので、どうしてもやむを得ないのでしょうか…?

    2008年7月3日 11:52
  • 基本的には同じことです。

    そしてLoadであればウィンドウがいないので前面に持ってきようがありませんよね。
    つまりダイアログに対して前面に来るように仕向けるしかありません。
    そうなると最悪APIレベルのことをしなくてはいけません。

     

    と書いておきながら、Vista+2005で試してみましたが現象は出ませんでした。
    もしかしたらですがShowDialog(Me) で試してみてください。
    親ウィンドウを指定していないことが原因かもしれません。
    LoadではウィンドウハンドルがOSレベルでまだ作成されていないと推定され
    Meを指定することによりウィンドウハンドルがその場で強制的に作成されるためと想像できます。

     

    ただし、うまくいっても、OSの状況により現象が発生する可能性は捨てきれません。

    関連して、かつ、別件ですが、
    親フォームを見せたくない特別な理由をのぞいて
    Shown イベントで処理することをおすすめします。

    2008年7月3日 12:52
  • 本筋とずれますが、気になったので書いておきます。

     

     hone さんからの引用

                        If Not Me.FolderBrowserDialog1.SelectedPath.Contains("C:\xxx") Then
                            System.Windows.Forms.MessageBox.Show("選択できるのは""C:\xxx""以下のディレクトリのみです。", "ディレクトリ選択エラー", MessageBoxButtons.OK, MessageBoxIcon.Error)
                            Exit Sub
                        End If

    エラー表示した後、そのフォームは表示されますが、問題ないのでしょうか?

    (Exit SubはLoadイベントの残りの処理を実行しないだけで、アプリケーションは終了されません。)

    2008年7月3日 13:42
    モデレータ
  • まどかさん、ありがとうございます。

     

    すでに帰宅してしまったので、明日ShowDialog(Me) を試してまたご報告したいと思います。

    2008年7月3日 14:16
  • Azuleanさん、ご指摘ありがとうございます。

     

    投稿時のコピペミスです。

    美しくないですが、Exit Subではなく、Endとして終了させています。

    2008年7月3日 14:20
  •  hone さんからの引用

    美しくないですが、Exit Subではなく、Endとして終了させています。

     

    なおさら、Shown イベントに処理を移行してMe.Close()としたほうがよいでしょう。

    コンストラクタやLoadでは自クラスの初期化に徹して、ユーザーにアクションさせるようなUI操作は好ましくありません。

    イベントドリブンでの待機状態に入ったシグナルがShown イベント以降であり、それ以前は準備段階ととらえたほうがよいでしょう。

     

    2008年7月3日 14:36
  • ShowDialog(Me)としたところ、2回目のダイアログもウィンドウの最前面に表示されました!

     

    それから、これまでの処理を見直してLoad時には各種初期化のみを実施し、

    ShownイベントにこれまでLoad内で行っていた処理をするようにソースの見直しをしました。

     

    しばらく悩んでいた事が解決したので、今日は気分良く過ごせます。

    レス下さった、Azuleanさん、まどかさん、本当にありがとうございました。

    2008年7月4日 2:47