none
MDI子フォームを最前面に表示するには → VB.Net 2005 RRS feed

  • 質問

  • MDI を使ったアプリケーションを開発してます。
    現在開いている子フォームについては
    親のMDIフォームには 「ウインドウ (&W)」 を メニューバーに しており MdiWindowListItem の指定をしています。
    ですからウインドウの切り替え操作はできるのですが・・・・ 下記の状態をなんとかなりませんか?

    MDIの子フォーム(①メインパネル)がすでにアクティブな状態で別な大きなウインドウフォームを使っていて 別な②フォームを開いたとします。
    しかしユーザーが①を操作して最前面にすると 当然②は後ろに隠れてしまいます。

    そこで MDI(親フォーム) にて再度メニューバーから ②を開くよう操作したとき 同じ画面は開かず 最前面に表示 できないでしょうか?

    下記はそのソースです。
    __________________________________________________________________________

        Private Sub MDIParent1_Load(ByVal sender As Object, ByVal e As System.EventArgs) Handles Me.Load
    
    '途中 省略
    
    
            Fm.MdiParent = Me
    
            Fm.Show()
    
        End Sub
    
        Private Sub アンテナToolStripMenuItem_Click(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles アンテナToolStripMenuItem.Click
            Dim Fm As New MyQSL.UI.アンテナマスター
    
            Dim activeChild As Form = Me.ActiveMdiChild
    
            If FindChildForm(Fm.Name) = True Then
                Fm.Activate()
                Fm.Focus()
                Exit Sub
            End If
    
                Fm.MdiParent = Me
                Fm.Show()
    
        End Sub
    
    
        'MDIの子フォームを検索する
        Private Function FindChildForm(ByVal P_fm_Name As String) As Boolean
            Dim FmChildren As Form() = Me.MdiChildren
    
            FindChildForm = False
            For Each FmChild As Form In FmChildren
                If FmChild.Name = P_fm_Name Then
                    Return True
                End If
            Next
        End Function

    上記では②はメニューバーのアイテムをクリックすると開きます。
    子ウインドウを検索し すでに同じ②のウインドウが開いている場合は何もせず。
    といったところです。

    参考になりそうな情報を試しましたがうまくゆきません。
     参考: http://dobon.net/vb/dotnet/form/topmost.html

    2009年7月28日 13:28

回答

  • インスタンスを理解されていないように思います。

    Dim
    Fm As New MyQSL.UI.アンテナマスター

    上記で新しく作成されたFmをActivate()しても、既存のMyQSL.UI.アンテナマスターはActivate()されません。
    つまり、Fmと既存のMyQSL.UI.アンテナマスターのインスタンスは別物だということです。

    例えばFindChildFormの戻り値を、見つかれば見つかったインスタンスを返し、見つからなければnullを返すようにします。
    アンテナToolStripMenuItem_Clickイベントプロシージャにおいて、その戻り値がnullでなければ、その戻り値であるインスタンスをActivateします。その戻り値がnullであれば、新しくMyQSL.UI.アンテナマスターのインスタンスを作成し、Showします。


    ★良い回答には回答済みマークを付けよう! わんくま同盟 MVP - Visual C# http://blogs.wankuma.com/trapemiya/
    • 回答としてマーク MYNOBU 2009年7月28日 16:45
    2009年7月28日 15:22
    モデレータ
  • どうもありがとうございました。 上記ソースに問題があればご返信ください。 
    よろしくおねがいします。

    ・If GetFM Is Nothing Then の後の Else ブロックに Exit Sub が置かれていますが、不要では?
     End If の後に実行されるとまずい処理があるのであれば、確かに Exit Sub で抜けないといけないのですが、ご提示されたソースからは必要性がないように見受けられました。

    ・Name で比較するために新しいインスタンスが作られていませんか?
     "MyQSL.UI.アンテナマスター" はフォームクラスの名前です。これのプロパティを参照すると、Visual Basic 特有の機能である既定のインスタンスが作られます。


    既定のインスタンスについて実験するためにこんなコードを書きました。
    新しい Windows フォームプロジェクトで Form1 と Form2 を作り、Form1 に Button1 を作って、そこに Click イベントを割り当てています。

    Public Class Form1
    
        Private Sub Button1_Click(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles Button1.Click
            Dim Fm As New Form2
            Fm.Show()
    
            If Fm.Name = Form2.Name Then
                MsgBox("一緒のフォーム")
            End If
            ' 以下おまけ
            If TypeOf Fm Is Form2 Then
                MsgBox("一緒のフォーム")
            End If
            Dim t As [Type]
            t = GetType(Form2)
            If Fm.GetType().Equals(t) Then
                MsgBox("一緒のフォーム")
            End If
        End Sub
    End Class
    
    Public Class Form2
        Public Sub New()
    
            ' この呼び出しは、Windows フォーム デザイナで必要です。
            InitializeComponent()
    
            ' InitializeComponent() 呼び出しの後で初期化を追加します。
            MsgBox("Newされました")
    
        End Sub
    End Class
    Public Sub New() はそのクラスのインスタンスが作成されるときに最初に呼び出されるコンストラクタと呼ばれるものです。
    ここでデバッグ実行し、Button1 をクリックすると、"Newされました"というメッセージが 2 回表示されることが確認できます。既定のインスタンスを参照した段階でインスタンスが作成されています。


    今回のシナリオで既定のインスタンスに頼らない方法として、 TypeOf FmChlild Is MyQSL.UI.アンテナマスター とベタで書くか、GetType() を組み合わせて引数に渡せるようにするかといった方法が考えられます。(上記コードでおまけとして書いたものが相当)
    解決した場合は、参考になった返信に「回答としてマーク」のボタンを利用して、回答に設定しましょう(複数に設定できます)。
    2009年7月28日 22:30
    モデレータ

すべての返信

  • まず、「何」を期待して、「どうなった」のでしょうか?
    私が読み取ったところでいくと、「既に開いているフォームがあればそれを最前面に表示すること」を期待して、「新しくフォームが開いてしまった」ということになりますが、問題ないでしょうか?

    FindChildForm の処理で Name と比較するのではなく、 TypeOf FmChild Is MySQL.UI.アンテナ といったように、型が同じかどうか判定してはいかがでしょうか。
    また、一致した場合は、その FmChildForm に対して Activate メソッドを実行すれば良いのではないでしょうか?

    例えば、Form3 というクラス(フォーム)を探して、それをアクティブにするなら下記のような感じでしょうか。

    Dim FmChildren As Form() = Me.MdiChildren
    For Each FmChild As Form In FmChildren
        If TypeOf FmChild Is Form3 Then
            FmChild.Activate()
            Exit Sub
        End If
    Next


    # 私見ですが、MySQL.UI 名前空間というのが気になりました…。MySQL のフォームではないですよね、恐らく。


    解決した場合は、参考になった返信に「回答としてマーク」のボタンを利用して、回答に設定しましょう(複数に設定できます)。
    2009年7月28日 14:25
    モデレータ
  • ご返答ありがとうございます。

    >>まず、「何」を期待して、「どうなった」のでしょうか?
    >>私が読み取ったところでいくと、「既に開いているフォームがあればそれを最前面に表示すること」を期待して、「新しくフォームが開いてしまった」ということになりますが、
    >>問題ないでしょうか?

    まさに実現したいことはそのとおりです。

    現状でも子フォームの検索はうまく動作しています。
    フォームの名前でもうまくゆくようです。
    あとでご指摘のソースに変更してみたいと思います。

    ですが、 ソース上の Private Sub アンテナToolStripMenuItem_Click イベント内の
    「FmChild.Activate()」 を実行しているのに 前面へ画面が変移しません。 なぜなのかがわかりません。

    たとえば ②のフォームがすでに開いており ①のフォームが前面にあるとき 親フォームのメニューバーから②のフォームを開くよう指示したときの場合です。
    「FmChild.Activate()」以外に方法はあるのでしょうか??


    >># 私見ですが、MySQL.UI 名前空間というのが気になりました…。MySQL のフォームではないですよね、恐らく。
    プロジェクトの名前を「MyQSL.UI」にしています。
    このプロジェクトのルート名前空間は「MYQSL_APP.MyQSL.UI」 にしています。

    よろしくお願いします。
    2009年7月28日 15:10
  • インスタンスを理解されていないように思います。

    Dim
    Fm As New MyQSL.UI.アンテナマスター

    上記で新しく作成されたFmをActivate()しても、既存のMyQSL.UI.アンテナマスターはActivate()されません。
    つまり、Fmと既存のMyQSL.UI.アンテナマスターのインスタンスは別物だということです。

    例えばFindChildFormの戻り値を、見つかれば見つかったインスタンスを返し、見つからなければnullを返すようにします。
    アンテナToolStripMenuItem_Clickイベントプロシージャにおいて、その戻り値がnullでなければ、その戻り値であるインスタンスをActivateします。その戻り値がnullであれば、新しくMyQSL.UI.アンテナマスターのインスタンスを作成し、Showします。


    ★良い回答には回答済みマークを付けよう! わんくま同盟 MVP - Visual C# http://blogs.wankuma.com/trapemiya/
    • 回答としてマーク MYNOBU 2009年7月28日 16:45
    2009年7月28日 15:22
    モデレータ
  • 補足です。

    例えばFindChildFormの戻り値を、見つかれば見つかったインスタンスを返し、見つからなければnullを返すようにします。

    ここで言う null とは VB における Nothing のことです。



    ですが、 ソース上の Private Sub アンテナToolStripMenuItem_Click イベント内の
    「FmChild.Activate()」 を実行しているのに 前面へ画面が変移しません。 なぜなのかがわかりません。

    既に回答がついていますが、新しく別のものを作ってしまっているからでしょう。
    このシナリオで New キーワードを使うと、同じ種類の新しいもの(インスタンス)を作ることになります。

    # 元々のフォームを1号とすると、New キーワードがついている Fm は2号、3号を作っている。

    >># 私見ですが、MySQL.UI 名前空間というのが気になりました…。MySQL のフォームではないですよね、恐らく。
    プロジェクトの名前を「MyQSL.UI」にしています。
    このプロジェクトのルート名前空間は「MYQSL_APP.MyQSL.UI」 にしています。

    私の見間違いでした、申し訳ありません。


    解決した場合は、参考になった返信に「回答としてマーク」のボタンを利用して、回答に設定しましょう(複数に設定できます)。
    2009年7月28日 15:49
    モデレータ
  • trapemiya さま、Azuleanさま お二方 ご返答 ありがとうございます。

    大変、参考になりました。 インスタンスということが 理解していなかったようです。

    おかげさまで 問題は解決できたように思います。

    ソースは下記のとおりに修正して動作OKでした。

        Private Sub MDIParent1_Load(ByVal sender As Object, ByVal e As System.EventArgs) Handles Me.Load
    
            Dim stCurrentDir As String = My.Application.Info.DirectoryPath '実行パスの取得
    
            Dim CL_Comlib As New ComLIB
    
            Dim Fm As New MyQSL.UI.QSO_LOG
    
    '途中 省略
    
           Fm.MdiParent = Me
    
            Fm.Show()
    
        End Sub
    
    
        ''' <summary>
        ''' アンテナマスターのFORMの処理
        ''' </summary>
        ''' <param name="sender"></param>
        ''' <param name="e"></param>
        ''' <remarks>メニューItemから起動されます。
        '''     2重起動 しない処理が含まれています。
        ''' </remarks>
        Private Sub アンテナToolStripMenuItem_Click(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles アンテナToolStripMenuItem.Click
    
            Dim Fm As MyQSL.UI.アンテナマスター
    
            Dim GetFM As Form
    
            GetFM = FindChildForm(MyQSL.UI.アンテナマスター.Name)
    
            If GetFM Is Nothing Then
                Fm = New MyQSL.UI.アンテナマスター
                Fm.MdiParent = Me
                Fm.Show()
            Else
                GetFM.Activate()
                GetFM.Focus()
                Exit Sub
            End If
    
        End Sub
     
        ''' <summary>
        '''  MDIの子フォームを検索する
        ''' </summary>
        ''' <param name="P_fm_Name"> 探したいフォーム名称</param>
        ''' <returns>見つかったFORMのインスタンス、見つからない場合はNothing</returns>
        ''' <remarks></remarks>
        Private Function FindChildForm(ByVal P_fm_Name As String) As Form
            Dim FmChildren As Form() = Me.MdiChildren
            For Each FmChild As Form In FmChildren
                If FmChild.Name = P_fm_Name Then
                    Return FmChild
                End If
            Next
            Return Nothing
        End Function
    どうもありがとうございました。 上記ソースに問題があれば ご返信ください。 
    よろしくおねがいします。
    2009年7月28日 16:57
  • どうもありがとうございました。 上記ソースに問題があればご返信ください。 
    よろしくおねがいします。

    ・If GetFM Is Nothing Then の後の Else ブロックに Exit Sub が置かれていますが、不要では?
     End If の後に実行されるとまずい処理があるのであれば、確かに Exit Sub で抜けないといけないのですが、ご提示されたソースからは必要性がないように見受けられました。

    ・Name で比較するために新しいインスタンスが作られていませんか?
     "MyQSL.UI.アンテナマスター" はフォームクラスの名前です。これのプロパティを参照すると、Visual Basic 特有の機能である既定のインスタンスが作られます。


    既定のインスタンスについて実験するためにこんなコードを書きました。
    新しい Windows フォームプロジェクトで Form1 と Form2 を作り、Form1 に Button1 を作って、そこに Click イベントを割り当てています。

    Public Class Form1
    
        Private Sub Button1_Click(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles Button1.Click
            Dim Fm As New Form2
            Fm.Show()
    
            If Fm.Name = Form2.Name Then
                MsgBox("一緒のフォーム")
            End If
            ' 以下おまけ
            If TypeOf Fm Is Form2 Then
                MsgBox("一緒のフォーム")
            End If
            Dim t As [Type]
            t = GetType(Form2)
            If Fm.GetType().Equals(t) Then
                MsgBox("一緒のフォーム")
            End If
        End Sub
    End Class
    
    Public Class Form2
        Public Sub New()
    
            ' この呼び出しは、Windows フォーム デザイナで必要です。
            InitializeComponent()
    
            ' InitializeComponent() 呼び出しの後で初期化を追加します。
            MsgBox("Newされました")
    
        End Sub
    End Class
    Public Sub New() はそのクラスのインスタンスが作成されるときに最初に呼び出されるコンストラクタと呼ばれるものです。
    ここでデバッグ実行し、Button1 をクリックすると、"Newされました"というメッセージが 2 回表示されることが確認できます。既定のインスタンスを参照した段階でインスタンスが作成されています。


    今回のシナリオで既定のインスタンスに頼らない方法として、 TypeOf FmChlild Is MyQSL.UI.アンテナマスター とベタで書くか、GetType() を組み合わせて引数に渡せるようにするかといった方法が考えられます。(上記コードでおまけとして書いたものが相当)
    解決した場合は、参考になった返信に「回答としてマーク」のボタンを利用して、回答に設定しましょう(複数に設定できます)。
    2009年7月28日 22:30
    モデレータ
  • Azuleanさん お世話になります。

    "MyQSL.UI.アンテナマスター"のNameプロパティ 参照するだけでコンストラクタが働くとはつゆしらずでした。

    データの型をチェック TYpeOF で回避できました。

    参考 URL : http://blog.livedoor.jp/akf0/archives/51517934.html#more

    下記のソースで 動作を確認しました。 またコンストラクタについても 無駄な実行がされていないことをチェックしました。
    ___________________________________________________________________________________________________________________

      Private Sub MDIParent1_Load(ByVal sender As Object, ByVal e As System.EventArgs) Handles Me.Load
    
            Dim stCurrentDir As String = My.Application.Info.DirectoryPath '実行パスの取得
    
            Dim CL_Comlib As New ComLIB
    
            Dim Fm As New MyQSL.UI.QSO_LOG
     
    '途中省略
      
    
            Fm.MdiParent = Me
    
            Fm.Show()
    
        End Sub
    
    
        ''' <summary>
        ''' アンテナマスターのFORMの処理
        ''' </summary>
        ''' <param name="sender"></param>
        ''' <param name="e"></param>
        ''' <remarks>メニューItemから起動されます。
        '''     2重起動 しない処理が含まれています。
        ''' </remarks>
        Private Sub アンテナToolStripMenuItem_Click(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles アンテナToolStripMenuItem.Click
    
            Dim Fm As MyQSL.UI.アンテナマスター
    
            Dim GetFM As Form
    
            GetFM = FindChildForm(MyQSL.UI.アンテナマスター.GetType())
    
            If GetFM Is Nothing Then
                Fm = New MyQSL.UI.アンテナマスター
                Fm.MdiParent = Me
                Fm.Show()
            Else
                GetFM.Activate()
                GetFM.Focus()
            End If
    
        End Sub
     
    
        ''' <summary>
        '''  MDIの子フォームを検索する
        ''' </summary>
        ''' <param name="P_fm"> 探したいフォームの型(TYPE)</param>
        ''' <returns>見つかったFORMのインスタンス、見つからない場合はNothing</returns>
        ''' <remarks></remarks>
        Private Function FindChildForm(ByVal P_Fm As Type) As Form
            Dim FmChildren As Form() = Me.MdiChildren
       
            For Each FmChild As Form In FmChildren
                If FmChild.GetType.Equals(P_Fm) Then
                    Return FmChild
                End If
            Next
    
            Return Nothing
    
        End Function
    ____________________________________________________________________________________________________


    どうもありがとうございました。 
    2009年7月29日 2:07
  • ちょっと確認させて下さい。

    下記のソースで 動作を確認しました。 またコンストラクタについても 無駄な実行がされていないことをチェックしました。


    GetFM = FindChildForm(MyQSL.UI.アンテナマスター.GetType()) 
    MyQSL.UI.アンテナマスター.GetType() という文は既定のインスタンスを参照しています。
    従って、コンストラクタが 2 度実行されるはずです。「チェックしました」とのことですが、どのように確認したのでしょうか?

    既定のインスタンスを使わずに書くのであれば、GetFM = FindChildForm(GetType(MyQSL.UI.アンテナマスター)) としましょう。
    解決した場合は、参考になった返信に「回答としてマーク」のボタンを利用して、回答に設定しましょう(複数に設定できます)。
    2009年7月29日 14:48
    モデレータ
  • AzuleanMVPユーザーのメダルユーザーのメダルユーザーのメダルユーザーのメダルユーザーのメダルさん 返事がおそくなりまして。

    確認方法についてはデバッグとレースにて フォームのコンストラクタ をチェックしましたが 間違っていたようですね。

    #Region "コンストラクタ定義"
    
        ''' <summary> 
        ''' コンストラクタ
        ''' </summary>
    
        Public Sub New()
    
            '' 継承元のコンストラクタをコール
            MyBase.New()
    
            '' Windows フォームデザイナの初期処理をコール
            InitializeComponent()
    
            G_RET_STATUS = False
        End Sub
    
    #End Region

    上記コードをフォームに貼り付けて実験しました。


    方法はともかく、Azuleanさん のご指摘どおり修正しましたので またよろしくです。

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

    2009年8月1日 10:58
  • 上記コードをフォームに貼り付けて実験しました。
    「MyQSL.UI.アンテナマスター」にコンストラクタを加えて、MyQSL.UI.アンテナマスター.GetType()を呼ぶ実験していたのであれば、ブレークポイントあるいはトレース出力が出るはずですが、どこか認識に食い違いがあるのかな…。


    可能性としては、この時点までに既に既定のインスタンスを参照しているコードがあるとかかな?


    解決した場合は、参考になった返信に「回答としてマーク」のボタンを利用して、回答に設定しましょう(複数に設定できます)。
    2009年8月1日 13:08
    モデレータ