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

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

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

Re[1]: コントロールの操作について


(過去ログ 160 を表示中)

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

■92815 / inTopicNo.1)  コントロールの操作について
  
□投稿者/ usushio (39回)-(2019/10/31(Thu) 17:01:03)

分類:[VB.NET/VB2005 以降] 

VisualStudio2017 VB言語で開発をしております。

メインフォームのListBoxに文字列を表示したいのですが、
イベントを介してメインのコントロールの操作を行いたい場合には、
参照が必要なのでしょうか。
また、Form1.ListTextAdd(str)のように[MainForm名].[MainFormのメソッド]を呼び出す際には、
コントロールの操作は制限されてしまうのでしょうか。

---メイン
Public Class Form1

    Public subclass As SubClass = New SubClass
    Private WithEvents class2 As SubClass = New SubClass

    Private Sub Button1_Click(sender As Object, e As EventArgs) Handles Button1.Click
        WriteItemListBox("Button1をクリックしました。")
    End Sub

    Private Sub Button2_Click(sender As Object, e As EventArgs) Handles Button2.Click
        AddStr()
    End Sub

    Public Sub AddStr()

        subclass.AddSubItem()
    End Sub


    Public Sub WriteItemListBox(ByVal str As String)

        Dim listadd As AddListItem = New AddListItem(ListBox1)
        listadd.AddItem(str)

    End Sub

    Public Sub ListTextAdd(ByVal str As String)

        WriteItemListBox(str)

    End Sub


    Private Sub Receiver(ByVal obj As Object, ByVal e As Sub2Class) Handles class2.Receiver

        subclass.AddMainControl(e.myText)

    End Sub
End Class

---サブ1
Public Class SubClass

    Public Event Receiver(ByVal sender As Object, ByVal e As Sub2Class)

    Public Sub AddSubItem()
        Try
            RaiseEvent Receiver("test", New Sub2Class("Button2をクリックしました。"))

        Catch ex As Exception
            MessageBox.Show(ex.ToString)
        End Try

        'Form1.WriteItemListBox("Button2をクリックしました。")
    End Sub

    Public Sub AddMainControl(ByVal str As String)
        Form1.ListTextAdd(str)
    End Sub

End Class


Public Class Sub2Class

    Public myText As String

    Public Sub New(ByVal str As String)
        CopyStr(str)
    End Sub

    Private Function CopyStr(ByVal str As String) As Boolean
        Try
            myText = str
            Return True
        Catch ex As Exception
            Return False
        End Try
    End Function


End Class

---表示
Public Class AddListItem

    Private list As New ListBox

    Sub New(ByVal box As ListBox)
        list = box
    End Sub

    Public Sub AddItem(ByVal str As String)

        list.Items.Insert(0, str)
        If list.Items.Count > 20 Then
            list.Items.RemoveAt(20)
        End If

    End Sub

End Class

引用返信 編集キー/
■92819 / inTopicNo.2)  Re[1]: コントロールの操作について
□投稿者/ 魔界の仮面弁士 (2446回)-(2019/10/31(Thu) 21:01:30)
2019/10/31(Thu) 21:04:23 編集(投稿者)

各クラスの「意図」が分からないので、それぞれの処理に対する追加説明を頂けると答えやすいです。
(もしくは SubClass や Sub2Class などではなく、それぞれの処理内容を表すような命名にするなど)


No92815 (usushio さん) に返信
> メインフォームのListBoxに文字列を表示したいのですが、

・Form1 が、自身の ListBox1 を直接操作して表示させるのは、もちろん OK です。

・呼び出し元(Form1)が作業クラスに、管理対象のオブジェクト(たとえば ListBox1)を
 渡しておき、作業クラスに表示を行わせるというのも OK です。

・作業クラス側が、暗黙の Form1 インタンスを通じて呼び出し元にアクセスするのは NG です。
 互いのクラスが相互依存になってしまい、使い回しにくくなります。



> イベントを介してメインのコントロールの操作を行いたい場合には、
> 参照が必要なのでしょうか。

「イベントを介しているかどうか」や「対象がコントロールであるかどうか」とは関係なく、
クラスのインスタンス メンバー を操作するためには、
操作対象となるオブジェクト インスタンスへの参照が必須となります。


> また、Form1.ListTextAdd(str)のように[MainForm名].[MainFormのメソッド]を呼び出す際には、
> コントロールの操作は制限されてしまうのでしょうか。

現状の Sub AddMainControl の部分の事ですね。

[Form1].[インスタンスメンバー] によるアクセスは、「New Form1()」との併用が出来ないといった
制限はありますが、だからといって使えないというわけではありません。

しかしそれでも、個人的には暗黙のフォームインスタンスの利用はお奨めしません。
https://social.msdn.microsoft.com/Forums/ja-JP/55781635-aaee-4f5b-85bb-29f3b4ce7c03/form1243421628124032098612377225802151212395new123993211823550123?forum=vbgeneralja




> Public subclass As SubClass = New SubClass
> Private WithEvents class2 As SubClass = New SubClass
> Public Sub AddStr()
>   subclass.AddSubItem()
> End Sub

上記では、AddSubItem メソッドを通じて、SubClass の Receiver イベントが呼ばれるようになっていますね。

しかし subclass 変数で保持されている方のインスタンスには、イベントハンドラが何も割り当てられていないため、
Form1 には対しては何もおきません。

イベントハンドラを割り当てるためには、SubClass のインスタンスに対して AddHandler ステートメントを呼ぶか、
もしくは class2 同様に、WithEvents と Handles 句を利用する必要があります。

 Public subclass As New SubClass()
 Sub New()
  InitializeComponent()
  AddHandler subclass.Receiver, AddressOf Receiver
 End Sub




> ---サブ1
> Public Class SubClass
>   Public Event Receiver(ByVal sender As Object, ByVal e As Sub2Class)

SubClass がさらなる継承を望まない場合は、NotInheritable Class として宣言することをお奨めします。

SubClass を継承可能なクラスとして実装する場合は、継承先でイベントの発生を
制御できるようにするため、イベント発火用の Protected メソッドを用意することをお奨めします。

Protected Sub OnReceiver(ByVal e As Sub2Class)
  RaiseEvent Receiver(Me, e)
End Sub


> RaiseEvent Receiver("test", New Sub2Class("Button2をクリックしました。"))

イベント引数の第一引数 sender は、「イベントの発生元」を表すものなので、
ここでは文字列ではなく、Me を渡すべきかと。

また、第二引数のメッセージ内容も不自然です。
このイベントが呼ばれるのは「Button2 がクリックされた時」とは限りません。
SubClass 側で分かるのは、「AddSubItem メソッドが呼ばれた」ということだけであり、
そのメソッドが Button2 から呼ばれるかどうかは、このクラスからは保証できませんよね。

Form1 の Button2_Click が "Button2をクリックしました。" という文字列を生成し、
それが AddSubItem の引数として受け渡されるという流れならアリですが、
現状ではクラス間の結びつきが強すぎるように見えます。

各クラスが『密結合』ではなく、『疎結合』となることを目指しましょう。



> Public Sub AddMainControl(ByVal str As String)
>   Form1.ListTextAdd(str)
> End Sub

このクラスの実装目的が良く分からなかったのですが、いずれにせよ、
暗黙のフォームインスタンスに頼ることは避けましょう。

事前に Form1 インスタンスを渡しておく…という手はありますが、それだと Form1 専用に
なってしまうので、中継用の Interface を設けた方が良いかもしれません。


イベントの取りまとめなどが目的なら、デザイン時に割り当てることもできるような
IExtenderProvider を実装したコンポーネントを用意するという選択肢もあります。


> Public Class Sub2Class
イベント引数は、『EventArgs の継承クラス』であることが望ましいです。

上記のクラスは Inherits System.EventArgs とした上で、
クラス名も "Sub2Class" ではなく、"何某EventArgs" 形式とするのが一般的です。

FormClosing イベントで使われる FormClosingEventArgs 型や
KeyDown イベントで使われる KeyEventArgs 型のように。



> Private Function CopyStr(ByVal str As String) As Boolean
>   Try
>     myText = str
>     Return True
>   Catch ex As Exception
>     Return False
>    End Try
> End Function

このメソッドの存在意義が分かりませんでした。

行っていることは単なる代入操作に思えるのですが、戻り値があるのは何のためでしょうか。
実際には、何か追加の検査処理などがあるのでしょうか?

また、例外を握りつぶして True/False に変換しているようですが、
この Catch 句には、どのような例外が来ることを想定しているのでしょうか?


> ---表示
> Public Class AddListItem
>  Private list As New ListBox
>  Sub New(ByVal box As ListBox)
>   list = box
>  End Sub

フィールド変数宣言部の New は取り除きましょう。

ListBox は IDisposable であり、Form によって生成・管理されるのが望ましいですし、
そもそもこの実装だと、Sub New の段階で最初のインスタンスが上書きされるため、
事前に New しておく必要が無いはずです。
引用返信 編集キー/


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

このトピックに書きこむ

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

管理者用

- Child Tree -