Form1とForm2で、クリックした方が前に出るようにしたい への返答
投稿で使用できる特殊コードの説明。(別タブで開きます。)
以下の返答は逆順(新しい順)に並んでいます。
投稿者 魔界の仮面弁士  (社会人)
投稿日時
2020/3/12 12:46:22
> Form1を閉じるとForm2も閉じます。
Form1 がスタートアップフォームだったからでしょう。
では実験。
Form3 を追加して、Form3 の Load イベントに「Form1.Show()」と言うコードを書いてください。
次に、アプリケーションが Form3 から開始されるよう、
プロジェクトのプロパティの「アプリケーション」タブを開き、
以下のように設定します。
スタートアップ フォーム: Form1 → Form3 に変更
シャットダウンモード:スタートアップ フォームが閉じる時
この状態で実行すると Form3 と Form1 が同時に現れるので、
Form1 のボタンから Form2 を呼び出してみます。
この場合、スタートアップフォームである Form3 を閉じると、
全てのフォームが閉じられ、アプリケーションごと終了するはずです。
一方、Form1 と Form2 はそれぞれ独立して閉じられるようになっているはずです。
Form1 を閉じたからと言って、Form2 や Form3 が閉じられることはありませんし、
Form2 を閉じたからと言って、Form1 や Form3 が閉じられることも無いはずです。
Owner 指定しない状態で、別フォームを連動して閉じさせたいなら、
FormClosing/FormClosed イベントのタイミングで
相手側のフォームの Close() メソッドを呼び出すコードを
記述する必要があります。
> childForm = Nothing
> は書いていませんが
Nothing 代入は、フォームを閉じる命令ではありません。
不要になった参照を捨てているだけです。
Form2 を閉じた後に、Form1 の Button1 をもう一度クリックしてみてください。
破棄された Form2 インスタンスを childForm.Show() しようとして、
ObjectDisposedException の例外が発生することになるはずです。
それを避けるため、Form2 が閉じられたことを FormClosed イベントで監視し、
以前のインスタンスを解放しているということです。
説明ついでに、Form2 にボタンを追加して、「Me.Hide()」と書いてみてください。
これは Me.Visible = True と同義であり、フォームを非表示にする命令です。
Form2 が非表示になった場合、見た目上は閉じられたのと変わりありませんが、
本当に閉じられたわけでは無いため、FormClosed イベントは発生しません。
閉じられていないという事は、オブジェクトも破棄されていない状態ですから、
元の childForm インスタンスを Show() しなおしたとしても、上記のような
ObjectDisposedException 例外は発生せず、単に「再表示」されるだけとなります。
> 当方、2013を使っていますが、前のコードの場合、バージョンはいくつでしょうか?
ReadOnly な自動実装プロパティは、VB2015 からサポートされました。
すなわち、VB2015/2017/2019 向けに書かれたコードであったという事です。
「自動実装プロパティ」自体は 2010 から搭載されたものなのですが、
この時点では ReadOnly や WriteOnly にすることはできませんでした。
再提示した方の実装であれば、VB2005 以上に対応できます。
(VB.NET 2002/2003 まで遡る場合は、さらなる書き換えが必要になります)
Form1 がスタートアップフォームだったからでしょう。
では実験。
Form3 を追加して、Form3 の Load イベントに「Form1.Show()」と言うコードを書いてください。
次に、アプリケーションが Form3 から開始されるよう、
プロジェクトのプロパティの「アプリケーション」タブを開き、
以下のように設定します。
スタートアップ フォーム: Form1 → Form3 に変更
シャットダウンモード:スタートアップ フォームが閉じる時
この状態で実行すると Form3 と Form1 が同時に現れるので、
Form1 のボタンから Form2 を呼び出してみます。
この場合、スタートアップフォームである Form3 を閉じると、
全てのフォームが閉じられ、アプリケーションごと終了するはずです。
一方、Form1 と Form2 はそれぞれ独立して閉じられるようになっているはずです。
Form1 を閉じたからと言って、Form2 や Form3 が閉じられることはありませんし、
Form2 を閉じたからと言って、Form1 や Form3 が閉じられることも無いはずです。
Owner 指定しない状態で、別フォームを連動して閉じさせたいなら、
FormClosing/FormClosed イベントのタイミングで
相手側のフォームの Close() メソッドを呼び出すコードを
記述する必要があります。
> childForm = Nothing
> は書いていませんが
Nothing 代入は、フォームを閉じる命令ではありません。
不要になった参照を捨てているだけです。
Form2 を閉じた後に、Form1 の Button1 をもう一度クリックしてみてください。
破棄された Form2 インスタンスを childForm.Show() しようとして、
ObjectDisposedException の例外が発生することになるはずです。
それを避けるため、Form2 が閉じられたことを FormClosed イベントで監視し、
以前のインスタンスを解放しているということです。
説明ついでに、Form2 にボタンを追加して、「Me.Hide()」と書いてみてください。
これは Me.Visible = True と同義であり、フォームを非表示にする命令です。
Form2 が非表示になった場合、見た目上は閉じられたのと変わりありませんが、
本当に閉じられたわけでは無いため、FormClosed イベントは発生しません。
閉じられていないという事は、オブジェクトも破棄されていない状態ですから、
元の childForm インスタンスを Show() しなおしたとしても、上記のような
ObjectDisposedException 例外は発生せず、単に「再表示」されるだけとなります。
> 当方、2013を使っていますが、前のコードの場合、バージョンはいくつでしょうか?
ReadOnly な自動実装プロパティは、VB2015 からサポートされました。
すなわち、VB2015/2017/2019 向けに書かれたコードであったという事です。
「自動実装プロパティ」自体は 2010 から搭載されたものなのですが、
この時点では ReadOnly や WriteOnly にすることはできませんでした。
再提示した方の実装であれば、VB2005 以上に対応できます。
(VB.NET 2002/2003 まで遡る場合は、さらなる書き換えが必要になります)
投稿者 Kojiro  (社会人)
投稿日時
2020/3/12 12:02:56
解決しました。・・・ありがとうございます。
Form1の最後のコード
Private Sub childForm_FormClosed(sender As Object, e As FormClosedEventArgs) Handles childForm.FormClosed
childForm = Nothing
End Sub
は書いていませんが、Form1を閉じるとForm2も閉じます。
当方、2013を使っていますが、前のコードの場合、バージョンはいくつでしょうか?
Form1の最後のコード
Private Sub childForm_FormClosed(sender As Object, e As FormClosedEventArgs) Handles childForm.FormClosed
childForm = Nothing
End Sub
は書いていませんが、Form1を閉じるとForm2も閉じます。
当方、2013を使っていますが、前のコードの場合、バージョンはいくつでしょうか?
投稿者 魔界の仮面弁士  (社会人)
投稿日時
2020/3/12 11:55:51
> Form1を閉じますとFomr2も閉じて、便利です。ありがとうございます。
それは単に、アプリケーションのプロパティで、
[スタートアップ フォーム]が Form1 になっていて、
アプリケーション自体が終了しているだけではありませんか?
Owner 指定での親子関係が無い場合は、呼び出し元を閉じたからと言って
呼ばれた側のフォームが自動的に閉じるわけではありません。
それは単に、アプリケーションのプロパティで、
[スタートアップ フォーム]が Form1 になっていて、
アプリケーション自体が終了しているだけではありませんか?
Owner 指定での親子関係が無い場合は、呼び出し元を閉じたからと言って
呼ばれた側のフォームが自動的に閉じるわけではありません。
投稿者 魔界の仮面弁士  (社会人)
投稿日時
2020/3/12 11:51:12
> ReadOnlyプロパティにはgetを指定する必要があるというエラーと、その関連でしょうが、
「自動実装プロパティ」構文を利用できない、
古いバージョンの Visual Basic をお使いのようですね…😅
AnswerChangedEventArgs クラスを下記のように差し替えてみてください。
「自動実装プロパティ」構文を利用できない、
古いバージョンの Visual Basic をお使いのようですね…😅
AnswerChangedEventArgs クラスを下記のように差し替えてみてください。
Public Class AnswerChangedEventArgs
Inherits EventArgs
Private _answer As String
Public Sub New(answer As String)
_answer = answer
End Sub
Public ReadOnly Property Answer As String
Get
Return _answer
End Get
End Property
End Class
投稿者 Kojiro  (社会人)
投稿日時
2020/3/12 11:50:26
それ以外は、Form1とForm2の関連性は、クリックした方が前に出ますし、Form1を閉じますとFomr2も閉じて、便利です。ありがとうございます。
投稿者 Kojiro  (社会人)
投稿日時
2020/3/12 11:44:38
お返事、ありがとうございます。
おっしゃるように、作りました。
Public ReadOnly Property Answer As String
Public Sub New(answer As String)
Me.Answer = answer
End Sub
のAnswerで、ReadOnlyプロパティにはgetを指定する必要があるというエラーと、その関連でしょうが、Publicがステートメントをプロパティ本体内部に記述することはできませんのエラーです。
おっしゃるように、作りました。
Public ReadOnly Property Answer As String
Public Sub New(answer As String)
Me.Answer = answer
End Sub
のAnswerで、ReadOnlyプロパティにはgetを指定する必要があるというエラーと、その関連でしょうが、Publicがステートメントをプロパティ本体内部に記述することはできませんのエラーです。
投稿者 魔界の仮面弁士  (社会人)
投稿日時
2020/3/10 14:00:00
Form1 と Form2 の前後関係を切り替えられるようにしたいのであれば、
Owner を指定してはいけません。Owner は Nothing のままにしておいてください。
Owner プロパティ(あるいは Show メソッドの引数)にセットしたウィンドウは、
モードレス ダイアログの親ウィンドウとして管理され、前後関係(ZOrder)が固定される仕様です。
そして、Owner ウィンドウが閉じられたり非表示になったりすれば、
子フォームも一緒に閉じられたり非表示になります。
ということで、Owner を通じてデータのやり取りを行うことは避けてください。
そもそも他フォームのコントロールを直接操作するのは、本来は望ましくありません。
> DirectCast(Me.Owner, Form1).txt_pic.Text = txt_ans.Text
たとえば上記の場合、右辺は「自フォームの txt_ans」への操作なので問題無いのですが、
左辺は「他フォームの txt_pic」を操作していることになるため、避けるべきです。
互いのフォームを操作しあうような実装は「密結合」となり、
画面仕様の変更に弱くなります。
慣れないうちは手間に感じるかもしれませんが、疎結合を原則とし、
「フォームのコントロールを読み書きできるのは、そのフォーム自身のみ」
の方針で設計した方が、画面仕様の変更などに追従しやすくなります。
つまりコントロールそのものを操作させるのではなく、
コントロールの値を読み書きするためのメンバー(プロパティ・メソッド・イベント等)を
作成し、それを通じて読み書きする設計にするのが望ましいということです。
Form2 の txt_ans.Text を読み書きするコードは Form2 に置き、
Form1 の txt_pic.Text を読み書きするコードは Form1 に置くようにします。
たとえばこんな感じ。これなら、Owner に頼る必要もなくなります。
Owner を指定してはいけません。Owner は Nothing のままにしておいてください。
Owner プロパティ(あるいは Show メソッドの引数)にセットしたウィンドウは、
モードレス ダイアログの親ウィンドウとして管理され、前後関係(ZOrder)が固定される仕様です。
そして、Owner ウィンドウが閉じられたり非表示になったりすれば、
子フォームも一緒に閉じられたり非表示になります。
ということで、Owner を通じてデータのやり取りを行うことは避けてください。
そもそも他フォームのコントロールを直接操作するのは、本来は望ましくありません。
> DirectCast(Me.Owner, Form1).txt_pic.Text = txt_ans.Text
たとえば上記の場合、右辺は「自フォームの txt_ans」への操作なので問題無いのですが、
左辺は「他フォームの txt_pic」を操作していることになるため、避けるべきです。
互いのフォームを操作しあうような実装は「密結合」となり、
画面仕様の変更に弱くなります。
慣れないうちは手間に感じるかもしれませんが、疎結合を原則とし、
「フォームのコントロールを読み書きできるのは、そのフォーム自身のみ」
の方針で設計した方が、画面仕様の変更などに追従しやすくなります。
つまりコントロールそのものを操作させるのではなく、
コントロールの値を読み書きするためのメンバー(プロパティ・メソッド・イベント等)を
作成し、それを通じて読み書きする設計にするのが望ましいということです。
Form2 の txt_ans.Text を読み書きするコードは Form2 に置き、
Form1 の txt_pic.Text を読み書きするコードは Form1 に置くようにします。
たとえばこんな感じ。これなら、Owner に頼る必要もなくなります。
Public Class Form1
'Form2 のイベントを捕らえる
Private WithEvents childForm As Form2 = Nothing
'Button1 を押すと、Form2 を表示する
Private Sub Button1_Click(sender As Object, e As EventArgs) Handles Button1.Click
If childForm Is Nothing Then
childForm = New Form2()
End If
'childForm.Show(Me) 'これだと親子関係になってしまう
childForm.Show() 'これならどちらのフォームも手前にできる
End Sub
'Form2 から変更通知が飛んで来たら、自フォームの txt_pic に反映させる
Private Sub childForm_AnswerChanged(sender As Object, e As Form2.AnswerChangedEventArgs) Handles childForm.AnswerChanged
txt_pic.Text = e.Answer
End Sub
Private Sub childForm_FormClosed(sender As Object, e As FormClosedEventArgs) Handles childForm.FormClosed
childForm = Nothing
End Sub
End Class
Public Class Form2
'変更通知用の「イベント」を自作しておく
Public Event AnswerChanged As EventHandler(Of AnswerChangedEventArgs)
'EventArgs クラスを継承して、受け渡したい情報を「プロパティ」として盛り込む
Public Class AnswerChangedEventArgs
Inherits EventArgs
Public ReadOnly Property Answer As String
Public Sub New(answer As String)
Me.Answer = answer
End Sub
End Class
' txt_ans が書き換わったらすぐに、変更を通知したい場合
'
Private Sub txt_ans_TextChanged(sender As Object, e As EventArgs) Handles txt_ans.TextChanged
' 自フォームの txt_ans の内容を持ったイベント引数を用意する
Dim arg As New AnswerChangedEventArgs(txt_ans.Text)
' その情報を引数に渡して、自作の変更通知イベントを発生させる
RaiseEvent AnswerChanged(Me, arg)
End Sub
'Button1 が押された時に、変更を通知したい場合
'
'Private Sub Button1_Click(sender As Object, e As EventArgs) Handles Button1.Click
' RaiseEvent AnswerChanged(Me, New AnswerChangedEventArgs(txt_ans.Text))
'End Sub
End Class
投稿者 Kojiro  (社会人)
投稿日時
2020/3/10 10:18:36
残念ながら、Form2からForm1への値の入力をOwnerを使ってます。
Form2: DirectCast(Me.Owner, Form1).txt_pic.Text = txt_ans.Text
Form2: DirectCast(Me.Owner, Form1).txt_pic.Text = txt_ans.Text
投稿者 shu  (社会人)
投稿日時
2020/3/9 13:22:20
切り替わらない(Ownerを指定している)
切り替わる(Ownerを指定していない)
_f1 = New Form2()
_f1.Owner = Me
_f1.Show()
切り替わる(Ownerを指定していない)
_f2 = New Form2()
_f2.Show()
投稿者 Kojiro  (社会人)
投稿日時
2020/3/9 13:08:56
お世話になります。
Form2をForm1から、発生させて、showさせた時、Form1とForm2が重なります。
その時、Form1の前にForm2が来ますが、Form1のどこかをマウスでクリックしたとき、Form1がForm2の前に来るようには、できないでしょうか?
Form2をForm1から、発生させて、showさせた時、Form1とForm2が重なります。
その時、Form1の前にForm2が来ますが、Form1のどこかをマウスでクリックしたとき、Form1がForm2の前に来るようには、できないでしょうか?
Form3での実験は、またやってみます。今回はForm1が
start upで閉じられれば、Form2を閉じたいので、まあ
記述はいいのかなと思っています。
2015/2016/2017で変わったのですか。
参考図書のお勧めがあれば、是非ご紹介ください。
まことに、重ねて、お礼申し上げます。