質問をすることでしか得られない、回答やアドバイスがある。

15分調べてもわからないことは、質問しよう!

ただいまの
回答率

87.60%

VB.netで複数のオブジェクトにまとめて一括でプロパティ設定したいがInterfaceとPropertyを宣言すべきか

解決済

回答 3

投稿

  • 評価
  • クリップ 1
  • VIEW 6,651

score 23

複数のオブジェクトに背景色等のプロパティをまとめて設定したいと思います。
イメージ説明
とりあえず、図のようなボタンの配列で

ButtonA1 と ButtonB1
ButtonA2 と ButtonB2
ButtonA3 と ButtonB3

をそれぞれまとめてプロパティ設定したいグループです。

Button等のオブジェクトで、ある特定のひとまとまりに対してまとめて操作することによって、後で仕様変更が発生しても手直しする箇所を少なくしたいのが狙いです。
例えば ButtonC1、ButtonC2、ButtonC3 が後から追加されても1か所手直しするだけで対応できるようにしたいです。

Interfaceを宣言して利用すべきかと思って後述のコードを組みましたが、この内容だと「ButtonA2」だけが青くなります。「ButtonA2」と「ButtonB2」を同時に青くしたいです。
イメージ説明
いろいろ検証してみた結果、Class1.Button の Property 内の Set (2)の内容は関係なく、Get 側(1)の値が反映されている模様。

どのように修正すれば上記の目的通りの動作になりますでしょうか?
そもそも根本的にやり方が違うのでしょうか?

  • Form1のコード
Public Class Form1

    'とりあえずClass1をインスタンス化してみる
    Dim CtrlClass As New Class1

    '同じ番号のボタンをまとめて青塗りにする(「Change」ボタンのイベント)
    Private Sub Button_Change_Click(sender As Object, e As EventArgs) Handles Button_Change.Click
        CtrlClass.Button(1).BackColor = Color.FromArgb(100, 100, 255)
    End Sub
End Class
  • Class1のコード
Public Class Class1 : Implements IButtons
    Public Interface IButtons
        Property Button() As System.Windows.Forms.Button() 'なぜか配列の範囲を指定できないので()のみで配列宣言
    End Interface

    '各ボタンのプロパティ
    Property Button() As System.Windows.Forms.Button() Implements IButtons.Button
        Get
            Return {Form1.ButtonA1, Form1.ButtonA2, Form1.ButtonA3} '★←(1)どうもこちらの値が参照されているらしい
        End Get
        Set(value As System.Windows.Forms.Button()) '★←(2)こんな風に複数のボタンのプロパティをまとめて適用したい
            Dim AGroup_Buttons() As System.Windows.Forms.Button = {Form1.ButtonA1, Form1.ButtonA2, Form1.ButtonA3}
            Dim BGroup_Buttons() As System.Windows.Forms.Button = {Form1.ButtonB1, Form1.ButtonB2, Form1.ButtonB3}

            AGroup_Buttons = value
            BGroup_Buttons = value
        End Set
    End Property

End Class
  • 気になる質問をクリップする

    クリップした質問は、後からいつでもマイページで確認できます。

    またクリップした質問に回答があった際、通知やメールを受け取ることができます。

    クリップを取り消します

  • 良い質問の評価を上げる

    以下のような質問は評価を上げましょう

    • 質問内容が明確
    • 自分も答えを知りたい
    • 質問者以外のユーザにも役立つ

    評価が高い質問は、TOPページの「注目」タブのフィードに表示されやすくなります。

    質問の評価を上げたことを取り消します

  • 評価を下げられる数の上限に達しました

    評価を下げることができません

    • 1日5回まで評価を下げられます
    • 1日に1ユーザに対して2回まで評価を下げられます

    質問の評価を下げる

    teratailでは下記のような質問を「具体的に困っていることがない質問」、「サイトポリシーに違反する質問」と定義し、推奨していません。

    • プログラミングに関係のない質問
    • やってほしいことだけを記載した丸投げの質問
    • 問題・課題が含まれていない質問
    • 意図的に内容が抹消された質問
    • 過去に投稿した質問と同じ内容の質問
    • 広告と受け取られるような投稿

    評価が下がると、TOPページの「アクティブ」「注目」タブのフィードに表示されにくくなります。

    質問の評価を下げたことを取り消します

    この機能は開放されていません

    評価を下げる条件を満たしてません

    評価を下げる理由を選択してください

    詳細な説明はこちら

    上記に当てはまらず、質問内容が明確になっていない質問には「情報の追加・修正依頼」機能からコメントをしてください。

    質問の評価を下げる機能の利用条件

    この機能を利用するためには、以下の事項を行う必要があります。

回答 3

+3

そのボタンのグループを共通にしたいというのであれば、ユーザコントロールを作ってそれをフォームに貼り付けるようにすればいいんじゃないかと。

変更の項目を限定できるなら(例えばコントロールの色だけを変えたいとか)変更項目をまとめた構造体でも作って、プロパティにしてアクセスするとか

投稿

  • 回答の評価を上げる

    以下のような回答は評価を上げましょう

    • 正しい回答
    • わかりやすい回答
    • ためになる回答

    評価が高い回答ほどページの上位に表示されます。

  • 回答の評価を下げる

    下記のような回答は推奨されていません。

    • 間違っている回答
    • 質問の回答になっていない投稿
    • スパムや攻撃的な表現を用いた投稿

    評価を下げる際はその理由を明確に伝え、適切な回答に修正してもらいましょう。

  • 2019/02/06 11:16

    ご提案ありがとうございます。

    ユーザーコントロールに関しては、下記のようにユーザーコントロール内のオブジェクトすべてにFor文で代入することはできました。

    Dim tmpObjects = UserControl1.CCtrl_Buttons '試しに作った名前空間UserControl1内のユーザーコントロールCCtrl_Buttons

    For Each c As Control In tmpObjects.Controls
    c.BackColor = Color.FromArgb(100, 100, 255)
    Next

    ただ、この方法だと仕様変更の都度手間がかかるのと、複数ウィンドウ、複数コントロールにまたがった場合制御できないので今回の用途には適合しなさそうです。


    構造体を使ってFor文でボタン色を変える方法は現在実施しているのですが、やはり仕様変更の際にすべて書き直す必要が出てくるので、任意の複数コントロールを1つにまとめたいです。

    キャンセル

  • 2019/02/06 11:20

    そのすべて書き直す、ってところがちと理解できないけど、
    ユーザコントロールを各フォームに貼り付けてるなら、ユーザコントロール内のコードをいじるだけで、貼り付けてるフォーム全てが一括で変更できるけど、目指してるのはこういうことでないのかな?

    キャンセル

  • 2019/02/06 11:59

    誤解があったようなので補足いたします。

    すべて書き直す必要があるのは構造体とFor文を使った場合の話です。
    例えばボタン名を変更する必要が出た場合は、それ用の構造体を追加したり等、都度対応が必要になります。

    ユーザーコントロールでまとめた場合はその手間はかかりませんが、こちらも先述の通り別ウィンドウや別ユーザーコントロールにまたがって一括処理することはできないです。

    また、ユーザーコントロールを利用した場合も、ユーザーコントロール自体が増えたり減ったりした場合の手間も考えると、インターフェイスを利用したほうが確実なのかなと思い試してみたのですが、うまく行かず今回の質問に至った次第です。

    キャンセル

checkベストアンサー

+1

List(of Button)でまとめればいいのでは?

    Public Buttons As New List(of Button)

    Buttons.Add(ButtonA1)
    Buttons.Add(ButtonB1)

    For Each b As Button In Buttons
        b.BackColor = Color.FromArgb(100, 100, 255)
    Next

追記

クラス側にFormA.ButtonAと書くとグループ内部のボタンが複数フォームにまたがる場合に
意図しないタイミングでフォームのインスタンスが生成される可能性があるかと思います。

また、実際に開いたインスタンスがクラス内で定義したインスタンスと別の可能性もあるので、
クラス側でのFormA.ButtonAはバグを埋め込みやすくしているだけだと思います。

私なら、下記のように書くと思います。

Public Class ButtonGroupClass
    Private _Buttons As New List(Of Button)

    Public Sub AddButton(value As Button)
        _Buttons.Add(value)
    End Sub
    Public Sub SetBackColor(c As Color)

        For Each b As Button In _Buttons
            b.BackColor = c
        Next

    End Sub

End Class


Module

 Public ButtonGroup As ButtonGroupClass() = {New ButtonGroupClass, New ButtonGroupClass, New ButtonGroupClass}

Form1

    Private Sub Form1_Load(sender As Object, e As EventArgs) Handles MyBase.Load

        ButtonGroup(0).AddButton(Button1)
        ButtonGroup(0).AddButton(Button2)
        ButtonGroup(1).AddButton(Button3)
        ButtonGroup(1).AddButton(Button4)
        ButtonGroup(2).AddButton(Button5)
        ButtonGroup(2).AddButton(Button6)

    End Sub

    Private Sub Button9_Click(sender As Object, e As EventArgs) Handles Button9.Click
        ButtonGroup(0).SetBackColor(Color.Red)
    End Sub


Form2

    Private Sub Form2_Load(sender As Object, e As EventArgs) Handles MyBase.Load

        ButtonGroup(0).AddButton(ButtonA)
        ButtonGroup(0).AddButton(ButtonB)
        ButtonGroup(1).AddButton(ButtonC)
        ButtonGroup(1).AddButton(ButtonD)
        ButtonGroup(2).AddButton(ButtonE)
        ButtonGroup(2).AddButton(ButtonF)

    End Sub

投稿

編集

  • 回答の評価を上げる

    以下のような回答は評価を上げましょう

    • 正しい回答
    • わかりやすい回答
    • ためになる回答

    評価が高い回答ほどページの上位に表示されます。

  • 回答の評価を下げる

    下記のような回答は推奨されていません。

    • 間違っている回答
    • 質問の回答になっていない投稿
    • スパムや攻撃的な表現を用いた投稿

    評価を下げる際はその理由を明確に伝え、適切な回答に修正してもらいましょう。

  • 2019/02/07 19:20

    List(of Button)のListで済みますよ
    Public ButtonLists As List(of List(of Button))

    For Each list As List(Of Button) In ButtonLists
    For Each b As Button In list
    b.BackColor = Color.Red
    Next
    Next
    とか
    どのみち、あるグループだけ適用外とかあった場合などの応用性はないと思いますが・・・

    キャンセル

  • 2019/02/07 19:33

    まぁListで管理するかクラスを作って管理するかは方法論なのでいいのですが、
    今回の話で一番気になったのが
    ButtonGroupClassにて
    Public Sub Buttons_set()
    ReDim Buttons(2)
    Buttons(0).Clear()
    Buttons(0).Add(Form1.ButtonA1)
    Buttons(0).Add(Form1.ButtonB1)
    とされている部分です。
    これを修正されたのであればいいのですが、していなのでしたら、
    Public Sub Buttons_set()
    ReDim Buttons(2)
    Buttons(0).Clear()
    Buttons(0).Add(Form1.ButtonA1)
    Buttons(0).Add(Form2.ButtonB1)
    End Sub
    とあって、Form1にてButtons_setを呼んだ時、Form2のインスタンスを生成するつもりがなくても生成されてしまいます。
    また、
    Dim f2 As New Form2
    f2.Show()
    としたりしたとき、クラス内のForm2.ButtonB1とf2.ButtonB1は別物なので、表示されているForm2のButtonB1の色が変わらないなどのバグを生みやすくなります。
    こちらに関するコメントが無かったので、念のため・・・

    キャンセル

  • 2019/02/08 10:29

    List(of Button)をさらにList化する方法ありがとうございます。


    また、インスタンスが分断してしまう問題については、適当なモジュールに下記のようなパブリック変数を宣言しました。

    Public Form1_Instance As Form1

    そして、各フォームロード時にこのように自身をパブリック変数に代入するようにしました。

    Private Sub Form1_Load(sender As Object, e As EventArgs) Handles Me.Load
    Form1_Instance = Me
    End Sub

    お作法的に正しいのかはわかりませんが、インスタンスが分断してバグが生まれるというのは実際にやらかしたことがありまして、この方法で回避しています。

    キャンセル

+1

デザインの制約がありますが、例えば同じグループの「ButtonA2」と「ButtonB2」を同じパネル等コンテナ上に配置することができるのであれば、以下の方法で対応できます。
(以下の例では「ButtonA2」と「ButtonB2」をパネル「Panel2」上に配置したものとします。)

    Private Sub Button_Change_Click(sender As Object, e As EventArgs) Handles Button_Change.Click
        For Each ctr As Control In Panel2.Controls
            If TypeOf ctr Is Button Then
                DirectCast(ctr, Button).BackColor = Color.FromArgb(100, 100, 255)
            End If
        Next
    End Sub


これならば、「ButtonC2」「ButtonD2」…とボタンが増えても、このグループのコンテナ上にボタンを追加するだけで、同グループのボタンと同様に扱ってくれます。
ただし、デザインの制約上、同じグループのボタンを同じコンテナに配置できない場合は、YAmaGNZ様例示のように、グループ化したいボタン等コントロールをリスト化することになります(おそらくリスト化はフォームのコンストラクタかフォームロード時に行う)が、この場合、ボタン追加をする場合にはデザインとコードの両方の編集が必要となります。

追記

ちょっと考え方を変えてみました。
概要は以下の通りです。

1.Buttonクラスを継承したクラスを作成。
同じグループのボタンはすべてこのクラスのものでデザインする。
2.このクラス内に共有イベントを用意して、上記ボタンの対象プロパティに変更があったら、このクラスの共有イベントを発生させる。
3.この共有イベントのハンドラ内で指定プロパティ値の設定を行うようにすれば、このクラスのインスタンスすべてでこのイベントハンドラの処理が行われる。

以下にボタンの継承クラス(ExButton)を使用した例を示します。

継承クラスと共有イベント引数用クラス

Public Class ExButton
    Inherits Button

    Private Shared Event ExBackColorChanged(ByVal e As EventArgs)

    Public Sub New()
        AddHandler ExButton.ExBackColorChanged, AddressOf btn_ExBackColorChanged
    End Sub

    Public Property GroupID As String = String.Empty

    Public Overrides Property BackColor As Color
        Get
            Return MyBase.BackColor
        End Get
        Set(value As Color)
            If value <> MyBase.BackColor Then
                MyBase.BackColor = value
                Dim e As New ExButtonEventArgs
                e.BackColor = value
                e.GroupID = Me.GroupID
                RaiseEvent ExBackColorChanged(e)
            End If
        End Set
    End Property

    Private Sub btn_ExBackColorChanged(ByVal e As ExButtonEventArgs)
        If e.GroupID = Me.GroupID Then
            Me.BackColor = e.BackColor
        End If
    End Sub
End Class

Public Class ExButtonEventArgs
    Inherits EventArgs

    Public Property BackColor As Color
    Public Property GroupID As String
End Class

プロパティ変更箇所サンプルソース

    Private Sub Button_Change_Click(sender As Object, e As EventArgs) Handles Button_Change.Click
        '任意のボタンの背景色を変えると、他の同一クラスのボタンの背景色も変わる。
        ExButton1.BackColor = Color.FromArgb(100, 100, 255)
    End Sub


この継承クラスのGroupIDプロパティを、デザイン時にでも各グループに応じて設定すれば良いです。
(上記例ではこのプロパティの型をStringにしていますが、Integerに変更してもいいでしょう。)

共有・非共有(=インスタンス)をよく理解することが必要ですが、一応、この継承クラスの保守とフォームデザイン構築を独立させることが可能となります。
これなら、ボタンの配置が同一コンテナ・同一フォームでなくともOKです。

短所としては、一括変更したいプロパティの分だけ共有イベント関連のコードを用意する必要があることです。
(ちょっとやりすぎ感もあったりします。)
また、ボタン以外のコントロールにも同じようなことを行うには、そのコントロール用の継承クラスを用意する必要があります。

投稿

編集

  • 回答の評価を上げる

    以下のような回答は評価を上げましょう

    • 正しい回答
    • わかりやすい回答
    • ためになる回答

    評価が高い回答ほどページの上位に表示されます。

  • 回答の評価を下げる

    下記のような回答は推奨されていません。

    • 間違っている回答
    • 質問の回答になっていない投稿
    • スパムや攻撃的な表現を用いた投稿

    評価を下げる際はその理由を明確に伝え、適切な回答に修正してもらいましょう。

  • 2019/02/07 11:53

    ありがとうございます。

    ご提案の方法の場合、メリットデメリットは同じグループのボタンをユーザーコントロールでまとめた場合と同じ感じですね。

    キャンセル

  • 2019/02/07 17:01 編集

    ユーザコントロールを使用する方法の提案と、そのやり取りを拝見しましたが、いくつかの疑問があります。
    まず、本件は仕様変更が発生した場合でも容易に対応できるようにしたい、ということのようですが、仕様変更とは具体的にはどのようなことでしょうか?
    次に、今回の例ではボタンクリック操作で対象グループに属するボタンの背景色を変更するようなもので、ボタン等コントロールのプロパティを一括変更したいのだと推察しましたが、その一括プロパティ変更を行うタイミングは、実行時(ユーザのUI操作)なのでしょうか、それともコーディング時なのでしょうか?
    コーディング時ならば、ボタンコントロールを継承したクラスを用意して、そのクラス内で対象の基底クラスのプロパティを変更すればよく、対象グループに属するボタン等コントロールにこれを使用すればよいわけなのですが、ユーザ操作でグループに属するボタン等コントロールのプロパティを一括で変更するには、やはりどこかでグループ内コントロールを管理することになるでしょうね。

    キャンセル

  • 2019/02/08 10:21

    仕様変更に関しては今後どのような変更がかかるのかはすみませんがわかりません。
    しかし、あり得るのはボタンの数の変更(ButtonA4、ButtonB4と横に増減する可能性とButtonC1~C3と縦に増減する可能性両方)や、色やテキスト等プロパティが実行中に変化する仕様です。
    なので、できるだけ機能を限定しないように作りたいと思いました。

    > 一括プロパティ変更を行うタイミングは、実行時(ユーザのUI操作)なのでしょうか

    そうです。
    プロパティ変更はプログラムの実行中にユーザー操作によって行われます。

    キャンセル

  • 2019/02/10 13:30

    ちょっと別の方法を考えてみました。
    もしよろしければ追記内容をご覧ください。

    キャンセル

15分調べてもわからないことは、teratailで質問しよう!

  • ただいまの回答率 87.60%
  • 質問をまとめることで、思考を整理して素早く解決
  • テンプレート機能で、簡単に質問をまとめられる

関連した質問

同じタグがついた質問を見る

  • トップ
  • VB.NETに関する質問
  • VB.netで複数のオブジェクトにまとめて一括でプロパティ設定したいがInterfaceとPropertyを宣言すべきか