パネルの上にテキストボックスを配置し値取得

タグの編集
投稿者 エピ  (社会人) 投稿日時 2023/11/27 10:12:22
Private Sub Button1_Click(sender As Object, e As EventArgs) Handles Button1.Click
        MsgBox(TextBox1.Text).
 End Sub
↑これは問題なく表示されるのですが、以下のようにするとオブジェクト参照がオブジェクト インスタンスに設定されていません。
となってしまいます。テキストボックスをパネルから外し実行すると問題なく表示されます。
Private Sub Button1_Click(sender As Object, e As EventArgs) Handles Button1.Click
        Dim AAA As String = "TextBox1"
        MsgBox(Me.Controls(AAA).Text)
End Sub
ご教授願えないでしょうか よろしくお願いします。
投稿者 魔界の仮面弁士  (社会人) 投稿日時 2023/11/27 11:25:11
該当する名前が無ければ、NullReferenceException になります。

今回の場合は、Me.Controls(AAA).Text ではなく、
Me.Panel1.Cotrols(AAA).Text と書く必要があります。

どのパネル上にあるのか分からない場合に、
子階層、子孫階層まで辿って検索する場合は、
 Me.Controls.Find(AAA, True)(0).Text
と書くこともできます。
結果が配列で返されるのは、同名コントロールが複数存在する可能性があるためです。
投稿者 エピ  (社会人) 投稿日時 2023/11/27 13:55:18
魔界の仮面弁士さんありがとうございます。
これは問題なく表示されます。
With Form1.Panel1
       MsgBox(.Controls(AcName).Text)
End With

以下のような形で表示させたいのですができますでしょうか
With DirectCast(CallByName(My.Forms, FrmName, CallType.Get), Form)
       MsgBox(.Panel1.Controls(AAA).Text) 'Panel1はFormのメンバーではありませんとなります。
End With
何度もすみません
投稿者 魔界の仮面弁士  (社会人) 投稿日時 2023/11/27 16:04:32
CallByName や Controls(名前) による動的取得はパフォーマンスが悪いので、
何度も繰り返し呼び出すのであれば、事前に Dictionary や List などに、
対象のコントロール一覧をキャッシュしておいた方が良いですよ。
一回しか呼び出さないのであれば問題無いですけれども。

> 以下のような形で表示させたいのですができますでしょうか
今回のケースでは、
 Me.Controls("Panel1").Controls(AcName).Text
 DirectCast(CallByName(My.Forms, FrmName, CallType.Get), Form).Controls.Find(AcName)(0).Text
 DirectCast(CallByName(My.Forms, FrmName, CallType.Get), Form).Controls(”Panel1").Controls(AcName).Text 
といった感じでアクセスできるかと。

ただし、コントロール名に頼った実装は、画面構成が変化したときに、
呼び出す側に追加の改修が必要になってしまうというデメリットがあります。
名前文字列の指定が間違っていても、コンパイル時には検出できませんし、
パフォーマンスも落ちますので、あまり多用はしない方が良いでしょうね。


> With DirectCast(CallByName(My.Forms, FrmName, CallType.Get), Form)
>        MsgBox(.Panel1.Controls(AAA).Text) 'Panel1はFormのメンバーではありませんとなります。
> End With
Panel1 が Form1 のメンバーであるからといって、
Panel1 が Form のメンバーということにはなりません。

Me.Panel1 という書き方をした場合、この Panel1 フィールドは、
System.Windows.Forms.Form クラスのメンバーなのではなく、
それを継承した「エピさん独自の Form1 クラス」のメンバーです。

ですから、.Panel1 にアクセスさせたいのであれば、
DirectCast(~, Form) ではなく、
DirectCast(~, Form1) と書かねばなりません。

もし、複数のフォームで共通的に扱えるようにしたいというのであれば、
「対象となるフォームすべてに、Panel を返すメンバーがある状態」を保証する必要があります。

たとえば、Panel を返す共通のインターフェイスを作っておきます。
Interface IHoge
    ReadOnly Property ContainerPanel() As Panel 'Function でも可 
End Interface


次にこのインターフェイスを、各フォームに Implements します。
各フォームでは、この ContainerPanel プロパティを実装して、
「Return Me.Panel1」なり「Return Me.Panel2」なりを返すようにします。

あとは、今までエラーになっていた
 DirectCast(CallByName(……), Form).Panel1
の代わりに
 DirectCast(CallByName(……), IHoge).ContainerPanel
のように書けば OK です。


この方法は追加のインターフェイスが必要な分、一見面倒なようですが、
画面デザインの変更があったときに、呼び出し側の改修が不要になるというメリットがあります。

たとえば、Form1 と Form3 は Panel1 しかないのに対し、
Form2 だけは、選択している RadioButton によって、
表示するコンテナを Panel1 / Panel2 とで切り替えたい…という改修が入ったとします。

その場合、Form2 における ContainerPanel の実装を
 Return If(Radio1.Checked, Panel1, Panel2)
に変更するだけで済みます。呼び出し側のコードは
 DirectCast(CallByName(……), IHoge).ContainerPanel
のまま、何も変える必要がありません。


とはいえ、フォーム上のコントロールを、
フォーム外から直接読み書きしている時点で、
あまり望ましい実装とは言えないのですけれども。
投稿者 エピ  (社会人) 投稿日時 2023/11/27 16:38:52
魔界の仮面弁士さん ありがとうございます。

色々と方法がありますね。勉強になりました
これら全部試してみたいと思います。