トップ回答者
MDI子フォームを最前面に表示するには → VB.Net 2005

質問
-
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
回答
-
インスタンスを理解されていないように思います。
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
-
どうもありがとうございました。 上記ソースに問題があればご返信ください。
よろしくおねがいします。
・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() を組み合わせて引数に渡せるようにするかといった方法が考えられます。(上記コードでおまけとして書いたものが相当)
解決した場合は、参考になった返信に「回答としてマーク」のボタンを利用して、回答に設定しましょう(複数に設定できます)。- 編集済み AzuleanMVP, Moderator 2009年7月28日 22:40
- 回答としてマーク MYNOBU 2009年7月29日 1:59
すべての返信
-
まず、「何」を期待して、「どうなった」のでしょうか?
私が読み取ったところでいくと、「既に開いているフォームがあればそれを最前面に表示すること」を期待して、「新しくフォームが開いてしまった」ということになりますが、問題ないでしょうか?
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 のフォームではないですよね、恐らく。
解決した場合は、参考になった返信に「回答としてマーク」のボタンを利用して、回答に設定しましょう(複数に設定できます)。 -
ご返答ありがとうございます。
>>まず、「何」を期待して、「どうなった」のでしょうか?
>>私が読み取ったところでいくと、「既に開いているフォームがあればそれを最前面に表示すること」を期待して、「新しくフォームが開いてしまった」ということになりますが、
>>問題ないでしょうか?
まさに実現したいことはそのとおりです。
現状でも子フォームの検索はうまく動作しています。
フォームの名前でもうまくゆくようです。
あとでご指摘のソースに変更してみたいと思います。
ですが、 ソース上の Private Sub アンテナToolStripMenuItem_Click イベント内の
「FmChild.Activate()」 を実行しているのに 前面へ画面が変移しません。 なぜなのかがわかりません。
たとえば ②のフォームがすでに開いており ①のフォームが前面にあるとき 親フォームのメニューバーから②のフォームを開くよう指示したときの場合です。
「FmChild.Activate()」以外に方法はあるのでしょうか??
>># 私見ですが、MySQL.UI 名前空間というのが気になりました…。MySQL のフォームではないですよね、恐らく。
プロジェクトの名前を「MyQSL.UI」にしています。
このプロジェクトのルート名前空間は「MYQSL_APP.MyQSL.UI」 にしています。
よろしくお願いします。 -
インスタンスを理解されていないように思います。
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
-
補足です。
例えば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」 にしています。私の見間違いでした、申し訳ありません。
解決した場合は、参考になった返信に「回答としてマーク」のボタンを利用して、回答に設定しましょう(複数に設定できます)。 -
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.MdiChildrenFor Each FmChild As Form In FmChildren
どうもありがとうございました。 上記ソースに問題があれば ご返信ください。
If FmChild.Name = P_fm_Name Then
Return FmChild
End If
Next
Return Nothing
End Function
よろしくおねがいします。 -
どうもありがとうございました。 上記ソースに問題があればご返信ください。
よろしくおねがいします。
・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() を組み合わせて引数に渡せるようにするかといった方法が考えられます。(上記コードでおまけとして書いたものが相当)
解決した場合は、参考になった返信に「回答としてマーク」のボタンを利用して、回答に設定しましょう(複数に設定できます)。- 編集済み AzuleanMVP, Moderator 2009年7月28日 22:40
- 回答としてマーク MYNOBU 2009年7月29日 1:59
-
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
____________________________________________________________________________________________________
どうもありがとうございました。 -
ちょっと確認させて下さい。
下記のソースで 動作を確認しました。 またコンストラクタについても 無駄な実行がされていないことをチェックしました。
MyQSL.UI.アンテナマスター.GetType() という文は既定のインスタンスを参照しています。
GetFM = FindChildForm(MyQSL.UI.アンテナマスター.GetType())
従って、コンストラクタが 2 度実行されるはずです。「チェックしました」とのことですが、どのように確認したのでしょうか?
既定のインスタンスを使わずに書くのであれば、GetFM = FindChildForm(GetType(MyQSL.UI.アンテナマスター)) としましょう。
解決した場合は、参考になった返信に「回答としてマーク」のボタンを利用して、回答に設定しましょう(複数に設定できます)。- 編集済み AzuleanMVP, Moderator 2009年7月29日 14:57
-
AzuleanMVP
さん 返事がおそくなりまして。
確認方法についてはデバッグとレースにて フォームのコンストラクタ をチェックしましたが 間違っていたようですね。#Region "コンストラクタ定義" ''' <summary> ''' コンストラクタ ''' </summary> Public Sub New() '' 継承元のコンストラクタをコール MyBase.New() '' Windows フォームデザイナの初期処理をコール InitializeComponent() G_RET_STATUS = False End Sub #End Region
上記コードをフォームに貼り付けて実験しました。
方法はともかく、Azuleanさん のご指摘どおり修正しましたので またよろしくです。
ありがとうございました。