べつclassのSubでtextBox1.textを取得したい

タグの編集
投稿者 イヨ  (社会人) 投稿日時 2021/1/13 09:19:20
いつも大変お世話になっております。
しばらく考えましたがよくわからず質問します。。

 Class Form2に設置されているButton1_Clickしたら
 Class Form2に設置されているtextBox1.textを取得して
 べつclassのSubでtextBox1.textを取得してG_Dataに代入したいです。
下記がその該当のコードです。
  
MsgBox(g_Data)は空白で表示されます。
宜しくお願いいたします。
 

'-------------------------------------------
Public Class Form2
    Public Sub Button1_Click(sender As Object, e As EventArgs) Handles Button1.Click
        'インスタンスの作成(ClsHuman型)
        Dim human1 As New BarcodeReader
        'メソッドの呼び出し
        human1.DataReceived()
    End Sub
End Class

'-------------------------------------------
 Public Sub DataReceived()
        Dim frm2 = New Form2
        g_Data = frm2.TextBox1.Text
        MsgBox(g_Data)
    End Sub
End Class
'-------------------------------------------






投稿者 (削除されました)  () 投稿日時 2021/1/13 10:26:01
(削除されました)
投稿者 魔界の仮面弁士  (社会人) 投稿日時 2021/1/13 12:27:38
DataReceived という名前から、System.IO.Ports.SerialPort クラスのように
別スレッドで動作するライブラリかと初見で想像してしまいました。

別スレッドの場合は、呼び出し方を変えなければなりませんが、
現在のコードを見る限りでは、そういった実装にはしておらず、
単純に、呼び出し元と同一スレッドで動作するクラスと思って良さそうですね。


> Dim frm2 = New Form2
> g_Data = frm2.TextBox1.Text

New するということは、そこで新たに Form2 画面をもう一枚生成するということを意味します。
しかも Visible = True や Show が無いので、非表示のフォームということになり、
ここで得られる Text の値は、デザイン時の初期値になってしまうでしょう。

一応確認しておきたいのですが、今回の g_Data というのは何でしょうか?
BarcodeReader のインスタンスフィールドなのか、Form2 の公開プロパティなのか、
あるいはそれらの Shared メンバーであるのか、それとも Module で定義されたグローバル変数なのか…。


> べつclassのSubでtextBox1.textを取得してG_Dataに代入したいです。
質問文中で大文字小文字の使い分けが混在しているのは、意図的なものでしょうか。

ひとまず、g_Data と G_Data は同じ物のことで、
TextBox1.Text と textBox1.text も同じものだと仮定して回答します。



> 'インスタンスの作成(ClsHuman型)
> Dim human1 As New BarcodeReader
コメントとコードが合っていないようですよ?
上記の場合、変数の型もインスタンスの型も BarcodeReader 型であり、
ClsHuman型にはなっていません。

コメントの通り ClsHuman 型だというのなら、
   Dim human1 As New BarcodeReader
ではなく、
   Dim human1 As New ClsHuman()
   Dim human1 As ClsHuman = New BarcodeReader()
   Dim human1 As BarcodeReader = New ClsHuman()
などであるべきでしょう。


で…BarcodeReader クラスに対して、メソッド呼び出し時点の固定値を
受け渡しできれば、String 型の引数を追加するだけで十分だと思います。
例えばこんな感じ。
Partial Public Class Form2
    Public Sub Button1_Click(sender As Object, e As EventArgs) Handles Button1.Click
        Dim human1 As New BarcodeReader()
        human1.DataReceived(TextBox1.Text)
    End Sub
End Class

Public NotInheritable Class BarcodeReader
    Public Sub DataReceived(arg As String)
        Dim g_Data As String = arg
        MsgBox(g_Data)
    End Sub
End Class



あるいは「DataReceived を呼び出したときの TextBox1.Text の値」を受け渡したいのではなく、
BarcodeReader クラス内の任意のタイミングで、「その時点の TextBox1.Text の最新値」を
受け渡せるようにしたい場合には、上記のように String 値を渡すのではなく、
代わりに「Me」もしくは「Me.TextBox1」ないしは「TextBox1.Text の値を返すデリゲート」などを
受け渡してみてください。幾つか実装案を書いておきます。


(案1) BarcodeReader のコンストラクタで、対象のオブジェクトを渡す
Dim human1 As New BarcodeReader(TextBox1)
' Class BarcodeReader 
'     Public Sub New(txt As TextBox) 
''' g_Data = txt.Text 

Dim human1 As New BarcodeReader(New Func(Of String)(Function() TextBox1.Text))
' Public NotInheritable Class BarcodeReader 
'     Public Sub New(getText As Func(Of String)) 
''' g_Data = getText.Invoke() 



(案2) BarcodeReader にプロパティあるいはフィールドを用意し、そこに処理のためのデリゲートやラムダ式を渡す
human1.RequireText = Function() TextBox1.Text
' Public NotInheritable Class BarcodeReader 
'     Public Property RequireText As Func(Of String) 
''' g_Data = RequireText.Invoke() 



(案3) メソッドの引数としてコントロールを渡す
human1.DataReceived(Me.TextBox1)
' Public NotInheritable Class BarcodeReader 
'     Public Sub DataReceived(owner As Control) 
''' g_Data = owner.Text 

human1.DataReceived(Me)
' Class BarcodeReader 
'     ' この方法だと、TextBox1 の Modifiers プロパティを Private にできなくなるので 
'     ' 実際は Form2 ではなく、自作 Interface を渡した方が望ましいと思います 
'     Public Sub DataReceived(owner As Form2) 
''' g_Data = owner.TextBox1.Text 




(案4) BarcodeReader が Form2 に値を取りに行くのではなく、イベントかデリゲートを通じて
 Form2 側が「BarcodeReader からの値要求イベント」に応じる形にする(コールバック方式)
Partial Public Class Form2
    Private WithEvents human As BarcodeReader
    Public Sub Button1_Click(sender As Object, e As EventArgs) Handles Button1.Click
        human = New BarcodeReader()
        human.DataReceived()
    End Sub
    Private Sub human_Require(sender As Object, e As BarcodeReader.RequireEventArgs) Handles human.Require
        e.Data = TextBox1.Text
    End Sub
End Class

' Form2 以外からも呼び出せるようになるので、より汎用的な実装になる 
Public NotInheritable Class BarcodeReader
    Public Event Require As EventHandler(Of RequireEventArgs)

    Public Sub DataReceived()
        Dim arg As New RequireEventArgs()
        RaiseEvent Require(Me, arg)
        Dim g_Data As String = arg.Data
        MsgBox(g_Data)
    End Sub

    Public Class RequireEventArgs
        Inherits EventArgs
        Public Property Data As String
    End Class
End Class
投稿者 イヨ  (社会人) 投稿日時 2021/1/13 17:19:51
魔界の仮面弁士 様
お世話になっております。

>しかし現在のコードを見る限りでは、そういう実装になっているわけではなく、
>呼び出し元と同一スレッドで動作するクラスと思って良いのですよね?
自分はVBま初心者なのでよくわかりませんがソリューションエクスプローラーウインドウをみてみると

BarcodeReader.vb
Public Class BarcodeReader  
Public Sub DataReceived()
   
Form2.vb
Public Class Form2
Public Sub Button1_Click(sender As Object, e As EventArgs) Handles Button1.Click
とありました。


>New するということは、そこで新たに Form2 画面をもう一枚生成するということを意味します。
>しかも Visible = True や Show が無いので、非表示のフォームということになり、
>ここで得られる Text の値は、デザイン時の初期値になってしまうでしょう。

なるほど!初期値になってしまうんですね。どうりで空白なわけです。


>ところで、g_Data というのは何でしょうか?
>BarcodeReader のインスタンスフィールドなのか、Form2 の公開プロパティなのか、
>あるいはそれらの Shared メンバーであるのか、それとも Module で定義されたグローバル変数なのか…。

いまバーコードをスキャナーで読み取るプログラムがあって
自分は手打ちで読み込ませるものに書き直したりしています。
g_Dataは読み取った文字を格納する変数です。

自分が書いたのもではないのですが
Public Class BarcodeReaderの下に
    ''' <summary>受信データ</summary>
    Private Shared g_Data As String = ""
とかいてありました。




>質問文中で大文字小文字の使い分けが混在しているのは、意図的なものでしょうか?
>ひとまず、g_Data と G_Data は同じ物のことで、
>TextBox1.Text と textBox1.text も同じものだと仮定して回答します。
すいません。まぎらわしくて。同じです。

>上記の場合、変数の型もインスタンスの型も BarcodeReader 型であり、
>ClsHuman型にはなっていません。コメントの通り ClsHuman 型だというのなら、
>Dim human1 As New BarcodeReaderではなく、
>Dim human1 As New ClsHuman()
>Dim human1 As ClsHuman = New BarcodeReader()
>Dim human1 As BarcodeReader = New ClsHuman()などであるべきでしょう。

これは自分のコメントが間違っていました。
正しくはBarcodeReader 型です。申し訳ないです。

>BarcodeReader クラスが自作クラスなのか外部クラスなのか分かりませんが、
>メソッド呼び出し時点の固定値が得られれば良いだけであれば、下記で十分だと思います。
書いていただきましたコードに置き換えたところ
違う箇所がエラーになってしまいました。
コードは下記になります。
'データ受信ハンドラ設定
AddHandler g_SerialPort.DataReceived, AddressOf DataReceived
のDataReceived部分です。

どうやら Public Sub DataReceived()→を Public Sub DataReceived(arg As String)としたらそうなってしまいました。
イベントハンドラーになにか影響があったりするのでしょうか・・
右も左もわからず、、、言葉足らずになってしまいすいません。
沢山案を見せて頂き、ありがとうございます。
渡し方いろいろあるのですね。
対象のオブジェクトやメゾッドの引数で渡したり、、理解できるように勉強しようとおもいます。
投稿者 魔界の仮面弁士  (社会人) 投稿日時 2021/1/13 18:24:14
> 書いていただきましたコードに置き換えたところ
> 違う箇所がエラーになってしまいました。
そんなコード、私は提示していませんよ? 混ぜちゃダメです。


> Public Sub Button1_Click
普通に作ると、イベントハンドラーは Private Sub になるはずなのですが、
これは意図的に Public に変更されていますね。

Public にするということは、その処理を別のクラスから呼び出すことを意味しますが、
イベント ハンドラーを外部から直接呼び出すのは、あまりよくないコーディング手法なので
本来は Private のままで設計した方が望ましいです。


> なるほど!初期値になってしまうんですね。どうりで空白なわけです。

下記のようなコードを実行すると、Button1 を押すたびに、
新しい Form2 が表示される事象を確認できると思います。

Public Class Form1
    Private Sub Button1_Click(sender As Object, e As EventArgs) Handles Button1.Click
        Dim f As New Form2()
        f.Text = "Form2 - " & Now.ToString()
        f.Show(Me)
    End Sub
End Class



> Public Class BarcodeReaderの下に
>     ''' <summary>受信データ</summary>
>     Private Shared g_Data As String = ""

え……Shared 付きにしているのですか!?

インスタンス メンバーではなく、共有メンバーになっているということは、
BarcodeReader クラスのインスタンスがいくつあったとしても、
g_Data は一つ分しか存在しないことを意味します。


たとえば、TextBox というクラスのことを考えてみてください。
Form1 に貼った TextBox1, TextBox2 と
Form2 に貼った TextBox1, TextBox2 は、それぞれ独立しており、
それぞれの Text プロパティは、別々の文字列を扱えますよね。
これはつまり、TextBox クラスのインスタンスが 4 つ存在している状態にあたりますし、
Text プロパティも、それぞれのインスタンスごとに存在しているということになります。

それに対して、Shared で宣言された g_Data の場合、
 Dim x As New BarcodeReader()
 Dim y As New BarcodeReader()
のように、複数のインスタンスがあったとしても、g_Data 変数は一つしか存在できません。

それぞれのインスタンスごとに別々の g_Data として管理したいのであれば、Shared を外して
 Private g_Data As String = ""
とするべきですし、逆に、Shared として単一データとして管理したいのであれば、
BarcodeReader クラスが複数のインスタンスを持てないよう、
いわゆる「シングルトン」なクラスとして設計されるべきでしょう。

何故、このような設計にしてあるのか、作成者にコードの意図を聞いてみたいところですが…
質問の本筋からは離れてしまうので、とりあえず横に置いておきます。


さて現状、g_Data は Private となっているようですから、別のクラスから
 BarcodeReader.g_Data = "新しい値"
のようにして書き換えられるようにはなっておらず、
BarcodeReader クラスの内部からしか編集できない仕様ですよね。

ではその g_Data は、BarcodeReader クラス内の
どのタイミングで書き換えられるようにしたいのでしょうか?


呼び出し元で New したときに初期値をセットして、
それ以降は編集しないパターンの場合は、こう書きます。

Public Class Form2
    Public Sub Button1_Click(sender As Object, e As EventArgs) Handles Button1.Click
        Dim human1 As New BarcodeReader(TextBox1.Text)
        human1.DataReceived()
    End Sub
End Class

Public Class BarcodeReader
    ''' <summary>受信データ</summary> 
    Private Shared g_Data As String = ""
    Public Sub DataReceived()
        MsgBox(g_Data)
    End Sub
    Public Sub New()
    End Sub
    Public Sub New(s As String)
        g_Data = s
    End Sub
End Class



New したときには、g_Data に対して特に何もせず、
DataReceived を呼び出したときに書き換えたいパターンでは、こう書けます。

Public Class Form2
    Public Sub Button1_Click(sender As Object, e As EventArgs) Handles Button1.Click
        Dim human1 As New BarcodeReader()
        human1.DataReceived(TextBox1.Text)
    End Sub
End Class

Public Class BarcodeReader
    ''' <summary>受信データ</summary> 
    Private Shared g_Data As String = ""
    Public Sub DataReceived(s As String)
        g_Data = s
        MsgBox(g_Data)
    End Sub
End Class



固定値を渡すのではなく、コールバックさせたい場合はこうです。
ほぼ、先程の再掲ですが。

Public Class Form2
    Private WithEvents human As New BarcodeReader()
    Public Sub Button1_Click(sender As Object, e As EventArgs) Handles Button1.Click
        human.DataReceived()
    End Sub
    Private Sub human_RequireData(sender As Object, e As BarcodeReader.RequireEventArgs) Handles human.RequireData
        e.Data = TextBox1.Text
    End Sub
End Class

Public Class BarcodeReader
    ''' <summary>受信データ</summary> 
    Private Shared g_Data As String = ""

    ''' <summary>データ受信用イベント</summary> 
    Public Event RequireData As EventHandler(Of RequireEventArgs)

    ''' <summary>イベントを通じてデータを受信する</summary> 
    Private Sub ReceiveText()
        Dim e As New RequireEventArgs()
        RaiseEvent RequireData(Me, e)
        g_Data = e.Data
    End Sub

    Public Sub DataReceived()
        ReceiveText()
        MsgBox(g_Data)
    End Sub

    ''' <summary>データ受信イベント引数</summary> 
    Public Class RequireEventArgs
        Inherits EventArgs
        Public Property Data As String
    End Class
End Class
投稿者 魔界の仮面弁士  (社会人) 投稿日時 2021/1/13 18:40:08
> AddHandler g_SerialPort.DataReceived, AddressOf DataReceived

ここで使っている g_SerialPort の宣言はどうなっていますか?

もし、最初の回答の冒頭で述べた、
System.IO.Ports 名前空間の SerialPort クラスだとしたら、話が色々変わってきます。

SerialPort の DataReceived イベントに渡されるための Sub DataReceived は、
引数を 2 つもち、かつ、それぞれの引数のデータ型が
 Sub DataReceivedHandler(sender As Object, e As SerialDataReceivedEventArgs)
になっていなければなりません。

引数定義さえ正しければ、この Sub に Shared はあってもなくても良いですし、
スコープも Private でも Public でも構いません。

ただし、SerialPort クラスというものは独立したスレッドで処理されるものであるため、
上記の Sub DataReceivedHandler プロシージャーの中からは
フォームや TextBox への直接アクセスが一切禁止されます。
(最初の質問の冒頭で懸念していたのが、この点です)


一方、最初の質問にあった BarcodeReader クラスの方の Sub DataReceived() であれば、
そうした制限が無いので、この場所で TextBox の値を読み書きしても問題ありません。
Form とは別クラスなので、データの受け渡し方を考える必要はありますけれどね。
投稿者 イヨ  (社会人) 投稿日時 2021/1/14 10:16:01
>下記のようなコードを実行すると、Button1 を押すたびに、
>新しい Form2 が表示される事象を確認できると思います。
なるほどそういうことだったのですね
いっぱいウインドウが押すたびにでてきました!
なんだか昔のpcの固まった時みたいですね( ´∀` )

>それに対して、Shared で宣言された g_Data の場合、
>複数のインスタンスがあったとしても、g_Data 変数は一つしか存在できません。
勉強になります。いろいろ使い道がありそうです。

>何故、このような設計にしてあるのか、作成者にコードの意図を聞いてみたいところですが…
私もいろいろ聞いてみたいのですが、いまは違うところにいらっしゃるようで聞けないのです、、><
今回の件はマストではありませんが、そんな機能があったら便利みたいな感覚でやってます。



書いて頂きましたコードを入れたらSub DataReceived内でg_Dataが書き換わりました。
ありがとうございます。

g_SerialPortは下記のように書いてありSerialPort型でした。
Public Class BarcodeReader
    ''' <summary>受信データ</summary>
    Private Shared g_Data As String = ""
    ''' <summary>受信データ</summary>
    Private Shared g_DataHash As Hashtable = Nothing
    ''' <summary>シリアルポートオブジェクト</summary>
    Private Shared g_SerialPort As SerialPort = Nothing



Public Sub DataReceived()はこのように書き換えました。
もとのこのコードはバーコードスキャンがトリガーとなっているようで
読み取ったデーターをg_Dataにわたしているみたいです。
自分はこのトリガーをForm2 button click に置き換えてg_Dataをtextboxで横から書き換えたら
それに準じたデーターが表示されるとおもいましたが
やっていくうちに結構複雑なのかなと思い始めました。。

  Public Sub DataReceived()
        'MsgBox(g_Data)
        Try
            If USE_SCANNER Then
                g_Data &= g_SerialPort.ReadExisting()
            Else
                g_Data = "30013001C0100"
            End If
        Catch ex As Exception
            MessageBox.Show("バーコードの読み込みに失敗しました" & vbCrLf & ex.Message, "エラー", MessageBoxButtons.OK, MessageBoxIcon.Error)
            '受信バッファはクリア
            g_Data = ""
            Return
        End Try
        If g_Data.Length <> 8 Then
            g_Data = ""
            Return
        End If
        ''''データを分解する
        ScanDataDisassemble(g_Data)
        ''イベント通知
        RaiseEvent BarcodeReceived(g_Data)
        ' 受信バッファはクリア
        g_Data = ""
        Return
    End Sub
    Public Sub New()
    End Sub
    Public Sub New(s As String)
        g_Data = s
    End Sub
End Class


ちなみに   AddHandler g_SerialPort.DataReceived, AddressOf DataReceived の
部分は下記の中にかいてありました。

Public Function Open() As Boolean
        Dim Result As Boolean = False
        Try
            If USE_SCANNER Then

                g_SerialPort = New SerialPort()
                g_SerialPort.PortName = SysValue.ComPortName
                'g_SerialPort.Encoding = System.Text.Encoding.GetEncoding("Shift-JIS")
                g_SerialPort.Encoding = System.Text.Encoding.GetEncoding("utf-8")
                'ポートオープン
                g_SerialPort.Open()
                'RTS出力 (送信要求)
                g_SerialPort.RtsEnable = True
                'DTR出力 (端末レディ)
                g_SerialPort.DtrEnable = True
                'パリティー設定(偶数)
                g_SerialPort.Parity = Parity.Even

                ''データ受信ハンドラ設定
                AddHandler g_SerialPort.DataReceived, AddressOf DataReceived
            End If
            g_DataHash = New Hashtable()
            Result = True

        Catch ex As Exception
            MessageBox.Show("オープンエラー" & vbCrLf & ex.Message, "エラー", MessageBoxButtons.OK, MessageBoxIcon.Error)
            Result = False
        End Try
        Open = Result
    End Function








投稿者 イヨ  (社会人) 投稿日時 2021/1/14 17:38:27
いつも大変お世話になっております。
魔界の仮面弁士さんが書いてくださいましたコードをもとにいじっていたら
ちゃんと手打ちで動くようになりました!
手打ちしたg_Dataは書き換わっていまして、画面のバーコード表示結果はラグで動いていなかったみたいに見えていましたがちゃんとうごいていました。
これからもしっかり勉強を続けます
助かりました(^^)/
ありがとうございました!