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

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

新規登録して質問してみよう
ただいま回答率
85.48%
VB.NET

Microsoft Visual Basic .NETのことで、Microsoft Visual Basic(VB6)の後継。 .NET環境向けのプログラムを開発することができます。 現在のVB.NETでは、.NET Frameworkを利用して開発を行うことが可能です。

Q&A

解決済

3回答

12074閲覧

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

ot2os

総合スコア23

VB.NET

Microsoft Visual Basic .NETのことで、Microsoft Visual Basic(VB6)の後継。 .NET環境向けのプログラムを開発することができます。 現在のVB.NETでは、.NET Frameworkを利用して開発を行うことが可能です。

0グッド

1クリップ

投稿2019/02/06 00:49

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

ButtonA1 と ButtonB1
ButtonA2 と ButtonB2
ButtonA3 と ButtonB3

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

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

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

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

  • Form1のコード

VB

1Public Class Form1 2 3 'とりあえずClass1をインスタンス化してみる 4 Dim CtrlClass As New Class1 5 6 '同じ番号のボタンをまとめて青塗りにする(「Change」ボタンのイベント) 7 Private Sub Button_Change_Click(sender As Object, e As EventArgs) Handles Button_Change.Click 8 CtrlClass.Button(1).BackColor = Color.FromArgb(100, 100, 255) 9 End Sub 10End Class
  • Class1のコード

VB

1Public Class Class1 : Implements IButtons 2 Public Interface IButtons 3 Property Button() As System.Windows.Forms.Button() 'なぜか配列の範囲を指定できないので()のみで配列宣言 4 End Interface 5 6 '各ボタンのプロパティ 7 Property Button() As System.Windows.Forms.Button() Implements IButtons.Button 8 Get 9 Return {Form1.ButtonA1, Form1.ButtonA2, Form1.ButtonA3} '★←(1)どうもこちらの値が参照されているらしい 10 End Get 11 Set(value As System.Windows.Forms.Button()) '★←(2)こんな風に複数のボタンのプロパティをまとめて適用したい 12 Dim AGroup_Buttons() As System.Windows.Forms.Button = {Form1.ButtonA1, Form1.ButtonA2, Form1.ButtonA3} 13 Dim BGroup_Buttons() As System.Windows.Forms.Button = {Form1.ButtonB1, Form1.ButtonB2, Form1.ButtonB3} 14 15 AGroup_Buttons = value 16 BGroup_Buttons = value 17 End Set 18 End Property 19 20End Class

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

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

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

バッドをするには、ログインかつ

こちらの条件を満たす必要があります。

guest

回答3

0

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

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

投稿2019/02/06 01:06

y_waiwai

総合スコア87774

バッドをするには、ログインかつ

こちらの条件を満たす必要があります。

ot2os

2019/02/06 02: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つにまとめたいです。
y_waiwai

2019/02/06 02:20

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

2019/02/06 02:59

誤解があったようなので補足いたします。 すべて書き直す必要があるのは構造体とFor文を使った場合の話です。 例えばボタン名を変更する必要が出た場合は、それ用の構造体を追加したり等、都度対応が必要になります。 ユーザーコントロールでまとめた場合はその手間はかかりませんが、こちらも先述の通り別ウィンドウや別ユーザーコントロールにまたがって一括処理することはできないです。 また、ユーザーコントロールを利用した場合も、ユーザーコントロール自体が増えたり減ったりした場合の手間も考えると、インターフェイスを利用したほうが確実なのかなと思い試してみたのですが、うまく行かず今回の質問に至った次第です。
guest

0

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

VB

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

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

追記

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

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

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

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

VB

1Public Class ExButton 2 Inherits Button 3 4 Private Shared Event ExBackColorChanged(ByVal e As EventArgs) 5 6 Public Sub New() 7 AddHandler ExButton.ExBackColorChanged, AddressOf btn_ExBackColorChanged 8 End Sub 9 10 Public Property GroupID As String = String.Empty 11 12 Public Overrides Property BackColor As Color 13 Get 14 Return MyBase.BackColor 15 End Get 16 Set(value As Color) 17 If value <> MyBase.BackColor Then 18 MyBase.BackColor = value 19 Dim e As New ExButtonEventArgs 20 e.BackColor = value 21 e.GroupID = Me.GroupID 22 RaiseEvent ExBackColorChanged(e) 23 End If 24 End Set 25 End Property 26 27 Private Sub btn_ExBackColorChanged(ByVal e As ExButtonEventArgs) 28 If e.GroupID = Me.GroupID Then 29 Me.BackColor = e.BackColor 30 End If 31 End Sub 32End Class 33 34Public Class ExButtonEventArgs 35 Inherits EventArgs 36 37 Public Property BackColor As Color 38 Public Property GroupID As String 39End Class

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

VB

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

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

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

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

投稿2019/02/06 08:35

編集2019/02/10 04:40
kenshirou

総合スコア772

バッドをするには、ログインかつ

こちらの条件を満たす必要があります。

ot2os

2019/02/07 02:53

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

2019/02/07 10:34 編集

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

2019/02/08 01:21

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

2019/02/10 04:30

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

0

ベストアンサー

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

VB.NET

1 Public Buttons As New List(of Button) 2 3 Buttons.Add(ButtonA1) 4 Buttons.Add(ButtonB1) 5 6 For Each b As Button In Buttons 7 b.BackColor = Color.FromArgb(100, 100, 255) 8 Next

追記

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

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

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

VB.NET

1Public Class ButtonGroupClass 2 Private _Buttons As New List(Of Button) 3 4 Public Sub AddButton(value As Button) 5 _Buttons.Add(value) 6 End Sub 7 Public Sub SetBackColor(c As Color) 8 9 For Each b As Button In _Buttons 10 b.BackColor = c 11 Next 12 13 End Sub 14 15End Class

Module

VB.NET

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

Form1

VB.NET

1 Private Sub Form1_Load(sender As Object, e As EventArgs) Handles MyBase.Load 2 3 ButtonGroup(0).AddButton(Button1) 4 ButtonGroup(0).AddButton(Button2) 5 ButtonGroup(1).AddButton(Button3) 6 ButtonGroup(1).AddButton(Button4) 7 ButtonGroup(2).AddButton(Button5) 8 ButtonGroup(2).AddButton(Button6) 9 10 End Sub 11 12 Private Sub Button9_Click(sender As Object, e As EventArgs) Handles Button9.Click 13 ButtonGroup(0).SetBackColor(Color.Red) 14 End Sub

Form2

VB.NET

1 Private Sub Form2_Load(sender As Object, e As EventArgs) Handles MyBase.Load 2 3 ButtonGroup(0).AddButton(ButtonA) 4 ButtonGroup(0).AddButton(ButtonB) 5 ButtonGroup(1).AddButton(ButtonC) 6 ButtonGroup(1).AddButton(ButtonD) 7 ButtonGroup(2).AddButton(ButtonE) 8 ButtonGroup(2).AddButton(ButtonF) 9 10 End Sub 11

投稿2019/02/06 07:51

編集2019/02/07 06:31
YAmaGNZ

総合スコア10258

バッドをするには、ログインかつ

こちらの条件を満たす必要があります。

ot2os

2019/02/07 02:41

ありがとうございます。 下記のように記載して実装できました。 コードもスマートにまとまっていて良いのですが、配列化できないようです。 例えば仕様変更でButtonA4、ButtonB4等の新たなListが追加で生まれた場合は対応が難しそうです。 ・Form1側のコードが下記 'とりあえずClass1をインスタンス化してみる Dim CtrlClass As New Class1 'フォームのロード時にList(of Button)をまとめる Private Sub Form1_Load(sender As Object, e As EventArgs) Handles Me.Load CtrlClass.Buttons_set() End Sub '同じ番号のボタンをまとめて青塗りにする(「Change」ボタンのイベント) Private Sub Button_Change_Click(sender As Object, e As EventArgs) Handles Button_Change.Click For Each b As Button In CtrlClass.Buttons b.BackColor = Color.FromArgb(100, 100, 255) Next End Sub ・Class1側のコードが下記 Public Buttons As New List(Of Button) Public Sub Buttons_set() Buttons.Clear() Buttons.Add(Form1.ButtonA1) Buttons.Add(Form1.ButtonB1) End Sub Buttonsの宣言の仕方をこのようにすればジャグ配列のように扱えるかと思いましたが、Buttons(0).Clear()の時点で"System.NullReferenceException' のハンドルされていない例外"エラーが出ます。 Public Buttons() As List(Of Button) = New List(Of Button)() {} Public Sub Buttons_set() ReDim Buttons(2) Buttons(0).Clear() Buttons(0).Add(Form1.ButtonA1) Buttons(0).Add(Form1.ButtonB1)
YAmaGNZ

2019/02/07 03:03

「例えば仕様変更でButtonA4、ButtonB4等の新たなListが追加で生まれた場合は対応が難しそうです。」 というのが良く分かりませんが、とりあえず追記しました。
YAmaGNZ

2019/02/07 03:17

Public Buttons() As List(Of Button) = New List(Of Button)() {} これによるエラーですが、 参照型の型の配列を作成した場合、各要素はNewされません。 Public Buttons() As List(Of Button) = {New List(Of Button),New List(Of Button)} といった感じで各要素でNewしてインスタンスを生成しなければいけません。
ot2os

2019/02/07 07:14

補足確認しました。 ありがとうございます。 このように配列にしたかったのは、例えば全ボタングループに順にプロパティを設定したい場合、Buttons1、Buttons2、Buttons3...っと個別に指定すると、その部分でFor文が使えず、最終的にButtons?がいくつまで続くのかによって都度書き換えないといけないためです。 また、補足いただいた内容の「ButtonGroupClass」に下記の関数を追加してみました。 Public Function ToArray() Return _Buttons End Function すると、下記のように直接 ButtonGroup を For Each で呼び出して処理できるようになりました。 もちろん、ご提示いただいた SetBackColor を使う方法でも良いのですが、こうすることによって例えば BackColor 以外のプロパティ項目変更が必要になったりなどの仕様変更にも対応できるかなと考えました。 For Each b As Button In ButtonGroup(1).ToArray b.BackColor = Color.FromArgb(100, 100, 255) Next
YAmaGNZ

2019/02/07 08:26

上記の内容ですと、わざわざButtonGroupClassを作る必要はなく、ただList(of Button)で管理すればいいだけの話に思えます。 画面の変更でボタンが増えたりした場合は、Listへ追加すればいいだけですし あるボタンの属するグループを変更したいのであれば、追加するListを変更するだけです。
ot2os

2019/02/07 09:31

すみません、具体的な例を出すべきでしたね。 あくまで一例ですが、下記のようにすれば「For Each one_BG As ButtonGroupClass In ButtonGroup」文で回すことができ、ButtonGroup の配列数が増減してもこの部分は手直し不要になります。 Dim tmpColor() As Color = {Color.FromArgb(100, 100, 0), Color.FromArgb(100, 100, 100), Color.FromArgb(100, 100, 255)} Dim i As Long = 0 For Each one_BG As ButtonGroupClass In ButtonGroup For Each b As Button In one_BG.ToArray 'b.BackColor = Color.FromArgb(100, 100, 255) b.BackColor = tmpColor(i) Next i += 1 If i > 2 Then i = 0 End If Next これを List(of Button) のみで管理しようとすると、下記のようになってしまい、Buttons? の数が変わるたびに書き換える必要が出てしまうという意味です。 For Each b As Button In Buttons1 b.BackColor = Color.FromArgb(100, 100, 0) Next For Each b As Button In Buttons2 b.BackColor = Color.FromArgb(100, 100, 100) Next For Each b As Button In Buttons3 b.BackColor = Color.FromArgb(100, 100, 255) Next
YAmaGNZ

2019/02/07 10: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 とか どのみち、あるグループだけ適用外とかあった場合などの応用性はないと思いますが・・・
YAmaGNZ

2019/02/07 10: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の色が変わらないなどのバグを生みやすくなります。 こちらに関するコメントが無かったので、念のため・・・
ot2os

2019/02/08 01: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 お作法的に正しいのかはわかりませんが、インスタンスが分断してバグが生まれるというのは実際にやらかしたことがありまして、この方法で回避しています。
guest

あなたの回答

tips

太字

斜体

打ち消し線

見出し

引用テキストの挿入

コードの挿入

リンクの挿入

リストの挿入

番号リストの挿入

表の挿入

水平線の挿入

プレビュー

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

ただいまの回答率
85.48%

質問をまとめることで
思考を整理して素早く解決

テンプレート機能で
簡単に質問をまとめる

質問する

関連した質問