C# と VB.NET の質問掲示板

ASP.NET、C++/CLI、Java 何でもどうぞ

C# と VB.NET の入門サイト

Re[3]: クラスで取得した値を元画面にリアルタイム反映する手法について


(過去ログ 144 を表示中)

[トピック内 8 記事 (1 - 8 表示)]  << 0 >>

■84296 / inTopicNo.1)  クラスで取得した値を元画面にリアルタイム反映する手法について
  
□投稿者/ ruru (22回)-(2017/06/13(Tue) 13:22:36)

分類:[.NET 全般] 

画面が、AForm、BForm、CForm…とたくさんあります。
クラスは Readerクラスが一つのみ存在します。
ReaderクラスにはLoadメソッドと、Loadメソッドにて作られた設定にて
監視するKansiイベントが存在します。

AForm、BForm、CForm…各々の画面から、ReaderクラスのLoadメソッドを呼び出します。

※Aの画面の例※

Dim hoge1 as new Reader
Dim hoge2 as new Reader
Dim hoge3 as new Reader

hoge1.Load("fuga")
hoge2.Load("fugafuga")
hoge3.Load("fuga")

インスタンスを生成してから、各々監視を始め、
値を取得する都度、取得した値をtextboxに更新して表示したいと考えています。
(textboxも複数存在しますし、名前も不統一です)

しかし、ReaderクラスのKansiメソッドに書く手法が思い浮かばず困っております。

例えばべた書きになってしまいますが、
Kansiイベント内に、
AForm.textbox = "監視データ"
と書けば、実装は出来ますが、これではAFormのtextboxという名前のtextboxでしか利用が出来ません。


元画面では、インスタンスを生成するのみで、
クラスで、元画面のテキストボックスに値を代入する方法は実装する良い方法は
何かないでしょうか。ご教示いただけたら幸いです。

引用返信 編集キー/
■84297 / inTopicNo.2)  Re[1]: クラスで取得した値を元画面にリアルタイム反映する手法について
□投稿者/ shu (1028回)-(2017/06/13(Tue) 14:26:27)
No84296 (ruru さん) に返信

> hoge1.Load("fuga")
> hoge2.Load("fugafuga")
> hoge3.Load("fuga")
hoge1.Load("fuga",TextBox1)
hoge2.Load("fugafuga",TextBox2)
hoge3.Load("fuga",TextBox3)

と出来るようにしておいて
Kansiの中で
Loadで渡したTextBoxに設定するよう実装するとよいと思います。

引用返信 編集キー/
■84299 / inTopicNo.3)  Re[2]: クラスで取得した値を元画面にリアルタイム反映する手法について
□投稿者/ ruru (23回)-(2017/06/13(Tue) 14:51:04)
shu 様
迅速な回答ありがとうございます。
出来ました。

後学の為に教えていただきたいのですが、
今回ご教示いただいたようにtextboxを引数で渡しただけなのに
何故それがA画面のものと判別してくれたのかがわかりません。
(プロパティをのぞくとparentとかにそれっぽいのがありますが・・・)


【追加・実装した箇所】
Load(ByVal filename As String, ByVal textName As TextBox)

textName.Text = "abc"

A画面、B画面、C画面でそれぞれ試しましたが、きちんと対応した画面のみ
反応してくれました。

私の頭では、下記のような固定概念がありました。
A画面.textName.Text = "abc"
B画面.textName.Text = "abc"
C画面.textName.Text = "abc"


フォームを省いても実装出来るという事実はどう解釈したらよいのでしょうか。
そういうものなのでしょうか。
引用返信 編集キー/
■84300 / inTopicNo.4)  Re[1]: クラスで取得した値を元画面にリアルタイム反映する手法について
□投稿者/ 魔界の仮面弁士 (1319回)-(2017/06/13(Tue) 14:59:18)
No84296 (ruru さん) に返信
> クラスは Readerクラスが一つのみ存在します。

Reader クラスのインスタンスが一つしかない状態(Singleton など)を
連想しましたが、そういう意味では無いようですね。



> しかし、ReaderクラスのKansiメソッドに
> Kansiイベント内に、
Kansi はメソッドなのでしょうか。イベントなのでしょうか。


> AForm.textbox = "監視データ"
上記は、
 AForm.TextBox1.Text = "監視データ"
の間違いでしょうか。

この場合デザイン時に、TextBox1 の Modifiers プロパティが
Public, Friend, Protected Friend のいずれかになっている必要がありますね。
Modifiers が Private または Proteced だと使えません。


> (textboxも複数存在しますし、名前も不統一です)

===== 案1:コントロールを引数で渡す =====

'--- Reader クラス ---
Public Class Reader
 Private config As String
 Private target As Control

 Public Sub Load(config As String, target As Control)
  Me.config = config
  Me.target = target
 End Sub

 Public Sub Kansi()
  If target IsNot Nothing Then
   target.Text = "監視データ"
  End If
 End Sub
End Class


'--- AForm クラス ---
reader1.Load("fuga", Me.TextBox1)




===== 案2:イベントで処理する =====

'--- Reader クラス ---
Public Partial Class Reader
 Public Event Notice(caption As String)
 Public Sub Kansi()
  Dim data As String = "監視データ"
  RaiseEvent Notice(data)
 End Sub
End Class


'--- AForm クラス ---
Public Class AForm
 Private WithEvents reader1 As New Reader()
 Private Sub reader1_Notice(caption As String) Handles reader1.Notice
   TextBox1.Text = caption
 End Sub
End Class



===== 案3:拡張プロバイダで処理する =====

'--- Reader クラス ---
Imports System.ComponentModel
<ProvideProperty("IsResultViwer", GetType(TextBox))> _
Public Class Reader
 Inherits Component
 Implements IExtenderProvider
 Private textBoxes As New List(Of TextBox)()
 ''' <summary>拡張プロパティを実装可能なコントロールかどうかを調べる</summary>
 Public Function CanExtend(extendee As Object) As Boolean Implements IExtenderProvider.CanExtend
  Return TypeOf extendee Is TextBox
 End Function
 Public Sub Kansi()
  Dim data As String = "監視データ"
  For Each box In textBoxes
   box.Text = data
  Next
 End Sub

#Region "IsResultViwer 拡張プロパティ"
 <DefaultValue(False)>
 Public Function GetIsResultViwer(target As TextBox) As Boolean
  Return textBoxes.Contains(target)
 End Function
 Public Sub SetIsResultViwer(target As TextBox, value As Boolean)
  If target Is Nothing Then Return
  If value AndAlso Not textBoxes.Contains(target) Then
   textBoxes.Add(target)
  ElseIf Not value AndAlso textBoxes.Contains(target) Then
   textBoxes.Remove(target)
  End If
 End Sub
#End Region
End Class


'--- AForm クラス ---
・Dim reader1 As New Reader() の行は削除します(reader2〜reader4 も同様)
・Load メソッドや Kansi メソッドを呼び出す部分のコードは書き換え不要です。
・ツールボックスに Reader クラスが追加されているので、それをフォーム上に貼り付けます。
 これが元の reader1〜reader4 変数の代わりになります。
・フォームデザイナーで TextBox のプロパティを見ると、「Reader1 の IsResultViewer」という
 メンバーが追加されるので、それを True に変更すれば完了です。
・reader1.Kansi() が呼び出された時点で、デザイン時に指定したテキストボックスに表示されます。
引用返信 編集キー/
■84304 / inTopicNo.5)  Re[2]: クラスで取得した値を元画面にリアルタイム反映する手法について
□投稿者/ ruru (25回)-(2017/06/13(Tue) 16:10:46)
魔界の仮面弁士 様
いつも貴重な情報をありがとうございます。

> Reader クラスのインスタンスが一つしかない状態(Singleton など)を
> 連想しましたが、そういう意味では無いようですね。

紛らわしくて申し訳ございません。
一つクラスがあります。という意味です

>>しかし、ReaderクラスのKansiメソッドに
>>Kansiイベント内に、
> Kansi はメソッドなのでしょうか。イベントなのでしょうか。

混同しておりました。イベントです。


>> AForm.textbox = "監視データ"
> 上記は、
>  AForm.TextBox1.Text = "監視データ"
> の間違いでしょうか。

仰るとおりです。


案を3つも頂きありがとうございます。
今回は案1で実装しようとしていますが、案3が見た瞬間面白そうだったので
ちょっと試してみたのですが、言われたまま行ったところ出来ました。
このような実装方法は全く思い浮かばなかったので、びっくりです。

※ただ、デザイナの場所を変えるたびに何故か下記のエラーになっちゃいます。


誤 Me.ReaderCls1 = New ReaderTestTool.ReaderCls()

正 Me.ReaderCls1 = New Global.ReaderTestTool.ReaderCls()

Globalをつけるとデザイナを動かすまではビルドできますが、動かすとGlobalが消える。うーん。


この先この技術がどこで生かせるのかはまだわかりませんが、
面白い実装方法を教えてくださってありがとうございます。
引用返信 編集キー/
■84306 / inTopicNo.6)  Re[3]: クラスで取得した値を元画面にリアルタイム反映する手法について
□投稿者/ 魔界の仮面弁士 (1321回)-(2017/06/13(Tue) 16:49:29)
No84299 (ruru さん) に返信
> 私の頭では、下記のような固定概念がありました。
> A画面.textName.Text = "abc"
> B画面.textName.Text = "abc"
> C画面.textName.Text = "abc"

ここでいう textName というのは、ただの変数です。


> 今回ご教示いただいたようにtextboxを引数で渡しただけなのに
> 何故それがA画面のものと判別してくれたのかがわかりません。

さて、どこから説明したものか…。


ひとまず、現在のプロジェクトの「ソリューション エクスプローラー」見てください。

そのウィンドウには幾つかのアイコンボタンが並んでいますので、
[全てのファイルを表示]というものを探してクリックしてみてください。

そうすると A画面.vb の下に、A画面.designer.vb というファイルが表示されます。

その A画面.designer.vb をダブルクリックで開くと、
 Friend WithEvents textName As System.Windows.Forms.TextBox
という変数宣言が見つかるかと思います。
コントロールの名前がそのまま変数名になっていますね。

この変数は、Private Sub InitializeComponent() というメソッドの中で
 Me.textName = New System.Windows.Forms.TextBox()
のようにコントロールの実体が割り当てられ、
座標などの各種プロパティが設定された後で、最後に
 Me.Controls.Add(Me.textName)
などとして配置されていることも確認できるかと思います。


これは、New によってテキストボックスの実体が生成され、
それを textName という変数から参照している状態にあたります。

この変数は、A画面自身にとっては「自身のフィールド変数」に過ぎないので、
 textName.Text = "abc"
 Me.textName.Text = "abc"
のどちらでもアクセスできます。


また、このテキストボックスへの参照を、
 Dim txt As TextBox = Me.textName
のように、別の変数 txt にも割り当てれば、
 txt.Text = "xyz"
のように、textName と txt のいずれからもアクセスできるようになります。

そしてローカル変数のかわりに、メソッドの引数に渡した場合も同様である、ということです。


> プロパティをのぞくとparentとかにそれっぽいのがありますが・・・

「どのインスタンスを操作するのか」が重要なのであって、
そのインスタンスを管理している変数が、どのクラスのメンバーだったのかは
操作する上では直接関係ありません。

極端な話、Parent を書き換えたとしても、操作対象のコントロールは変化しません。


Public Class Form1
  Private Sub Form1_Load(sender As Object, e As EventArgs) Handles MyBase.Load
    Form2.Show()
  End Sub

  Private Sub Button1_Click(sender As Object, e As EventArgs) Handles Button1.Click
    'textName を養子に出してしまう
    Me.textName.Parent = Form2
  End Sub

  Private Sub Button2_Click(sender As Object, e As EventArgs) Handles Button2.Click
    'textName の親権を Form1 から Form2 に移しても、同じように面倒をみることができる
    Me.textName.Text = Now.ToString()
  End Sub
End Class


ちなみに、Parent を Form2 に置き換えた後は、
 Form2.Controls!textName.Text = "TEST"
の構文でもアクセスできます。使うことは無いでしょうけれども。



> フォームを省いても実装出来るという事実はどう解釈したらよいのでしょうか。

今まで、自画面以外から操作する場合は、
どのフォームの変数かを明示しなければならないので
 B画面.textName.Text = "abc"
 My.Forms.B画面.textName.Text = "abc"
 My.MyProject.Forms.B画面.textName.Text = "abc"
などとしていたと思います(上記 3 行はいずれも同じ意味)。

このような記述は、「フォームの既定のインスタンス」と呼ばれますが、
(対象フォーム).textName.Text でアクセスできるのは、
デザイン画面で textName のプロパティ一覧を見た時に、
『Modfiers』が Friend に設定されているからです。

Modfiers の欄は、編集済みであることを示す「太字」での表示になっていると思いますが、
右クリックして「リセット」すると、その設定が Private に変わります。Private の場合、
  A画面.textName.Text = "abc"
のようなアクセスはできないのでご注意ください。(C# では最初から private になっています)


そもそもフォームというのは
 Private Sub Button1_Click(sender As Object, e As EventArgs) Handles Button1.Click
  Dim f1 As B画面
  f1 = New B画面()
  f1.Show()
  Dim f2 As B画面
  f2 = New B画面()
  f2.Show()
 End Sub
などのようにして、同じフォームを複数個同時に開くことさえできます。
(上記の Button1 を再度押せば、さらに 2 つ増えます)

このような呼び方をした場合は、B画面.textName.Text の構文で
操作することができないのでご注意ください。
引用返信 編集キー/
■84307 / inTopicNo.7)  Re[3]: クラスで取得した値を元画面にリアルタイム反映する手法について
□投稿者/ shu (1029回)-(2017/06/13(Tue) 17:21:24)
No84299 (ruru さん) に返信
> shu 様
> 迅速な回答ありがとうございます。
> 出来ました。
>
> 後学の為に教えていただきたいのですが、
> 今回ご教示いただいたようにtextboxを引数で渡しただけなのに
> 何故それがA画面のものと判別してくれたのかがわかりません。
> (プロパティをのぞくとparentとかにそれっぽいのがありますが・・・)
>
>
> 【追加・実装した箇所】
> Load(ByVal filename As String, ByVal textName As TextBox)
>
> textName.Text = "abc"
>
> A画面、B画面、C画面でそれぞれ試しましたが、きちんと対応した画面のみ
> 反応してくれました。
>
> 私の頭では、下記のような固定概念がありました。
> A画面.textName.Text = "abc"
> B画面.textName.Text = "abc"
> C画面.textName.Text = "abc"
>
>
> フォームを省いても実装出来るという事実はどう解釈したらよいのでしょうか。
> そういうものなのでしょうか。

A画面の中にtextNameがなければ話は別になりますが今回は省きます。
A画面の中ではtextNameはA画面にあるtextNameということになります。
Me.textNameという記述をするとわかりやすくなります。

これを引数として渡すと渡した先の処理が外部であろうとtextNameを指す場所
が渡されるので処理する側ではその場所がどこのFormのTextBoxであるか、なんという
名前のTextBoxであるかということは関係なく指し示す場所にあるTextBoxへの
処理を行うことが出来ます。




引用返信 編集キー/
■84314 / inTopicNo.8)  Re[3]: クラスで取得した値を元画面にリアルタイム反映する手法について
□投稿者/ ruru (26回)-(2017/06/14(Wed) 09:39:16)
魔界の仮面弁士 様
詳細な説明をありがとうございます。
とても助かっております。

提示していただいた情報は全て自分の手で確認しました。
デザイナーの中のコードを見たことは一度も無かったです。
プロパティに値をセットして、コントロールにAddしてたんですね。

ちなみに、頂いた案1〜3ですが、最終的には案2になりました。
私自身の理解不足であれもこれもよさそうと迷ってましたが、
クラスのkansiイベントはいつ実行され、いつ終わるかもわからなかったので、
イベントが適しているのかなと思いました。

本件もご教示いただきまして本当にありがとうございます。



shu 様
お返事ありがとう御座います。
画面で実装したtextboxコントロール(という言い方でいいのかな・・・)を引数で渡すと、
元画面の情報も持っているので、クラスでは、引数.text = "hogehoge"みたいに
出来るのですね。
回答ありがとうございます。


恐らくまだ至らないところは多いと思ってますが、本件につきましては解決とさせていただきます。
ありがとうございました。
解決済み
引用返信 編集キー/


トピック内ページ移動 / << 0 >>

このトピックに書きこむ

過去ログには書き込み不可

管理者用

- Child Tree -