Form1とForm2で、クリックした方が前に出るようにしたい への返答

投稿で使用できる特殊コードの説明。(別タブで開きます。)
本名は入力しないようにしましょう。
投稿した後で削除するときに使うパスワードです。返答があった後は削除できません。
返答する人が目安にします。相手が小学生か社会人かで返答の仕方も変わります。
最初の投稿が質問の場合、質問者が解決時にチェックしてください。(以降も追加書き込み・返信は可能です。)
※「過去ログ」について書くときはその過去ログのURLも書いてください。

以下の返答は逆順(新しい順)に並んでいます。

投稿者 Kojiro  (社会人) 投稿日時 2020/3/12 15:06:03
丁寧なご説明、まことにありがとうございます。
Form3での実験は、またやってみます。今回はForm1が
start upで閉じられれば、Form2を閉じたいので、まあ
記述はいいのかなと思っています。
2015/2016/2017で変わったのですか。
参考図書のお勧めがあれば、是非ご紹介ください。
まことに、重ねて、お礼申し上げます。
投稿者 魔界の仮面弁士  (社会人) 投稿日時 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 まで遡る場合は、さらなる書き換えが必要になります)
投稿者 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を使っていますが、前のコードの場合、バージョンはいくつでしょうか?
投稿者 魔界の仮面弁士  (社会人) 投稿日時 2020/3/12 11:55:51
> Form1を閉じますとFomr2も閉じて、便利です。ありがとうございます。

それは単に、アプリケーションのプロパティで、
[スタートアップ フォーム]が Form1 になっていて、
アプリケーション自体が終了しているだけではありませんか?

Owner 指定での親子関係が無い場合は、呼び出し元を閉じたからと言って
呼ばれた側のフォームが自動的に閉じるわけではありません。
投稿者 魔界の仮面弁士  (社会人) 投稿日時 2020/3/12 11:51:12
> ReadOnlyプロパティにはgetを指定する必要があるというエラーと、その関連でしょうが、

「自動実装プロパティ」構文を利用できない、
古いバージョンの 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がステートメントをプロパティ本体内部に記述することはできませんのエラーです。
投稿者 魔界の仮面弁士  (社会人) 投稿日時 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 に頼る必要もなくなります。

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
投稿者 shu  (社会人) 投稿日時 2020/3/9 13:22:20
切り替わらない(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の前に来るようには、できないでしょうか?