はじめに
Windows Formsで配列の要素に対してバインドしたい時が結構あります。
同じコントロールを複数配置した場合も、配列で管理した方が楽な時があります。
前提
- Window.Forms
- VB 2017
- .NET Framework 4.6.1
ソースコード
ささっと以下のコード。
Form1.vb
Imports System.ComponentModel
Public Class Form1
Private VM As New ViewModel
Sub Form1_Load(sender As Object, e As EventArgs) Handles MyBase.Load
TextBox1.DataBindings.Add(New Binding("Text", VM.Inputs(0), "Value"))
TextBox2.DataBindings.Add(New Binding("Text", VM.Inputs(1), "Value"))
TextBox3.DataBindings.Add(New Binding("Text", VM.Inputs(2), "Value"))
End Sub
Sub Button1_Click(sender As Object, e As EventArgs) Handles Button1.Click
VM.Update()
End Sub
Private Class ViewModel
Public Inputs As t()
Sub New()
Inputs = {
New t With {.Value = "a"},
New t With {.Value = "b"},
New t With {.Value = "c"}
}
End Sub
Sub Update()
Inputs(0).Value = "1"
Inputs(1).Value = "2"
Inputs(2).Value = "3"
End Sub
Class t
Implements INotifyPropertyChanged
Event PropertyChanged As PropertyChangedEventHandler Implements INotifyPropertyChanged.PropertyChanged
Private _Value As String
Property Value As String
Get
Return _Value
End Get
Set
_Value = Value
RaiseEvent PropertyChanged(Me, New PropertyChangedEventArgs(NameOf(Value)))
End Set
End Property
End Class
End Class
End Class
試す
ばっちり
なにがおきたか
配列の用意
ViewModelクラス内では、Inputs
の要素に対してクラスを指定します。そして、Inputs
の要素に対してバインドする為、Inputs
自身はただのメンバでよいです。
バインド先であるViewModel内
Private Class ViewModel
Public Inputs As t()
' ~~中略~~
End Class
配列の「要素」となるプロパティの用意
代わりに、Inputs
の要素に対して指定したクラスとそのプロパティに対しては、プロパティを用意し、INotifyPropertyChanged
によるPropertyChanged
イベントを発火(Raise)させるようにします。
はい、要素自身をINotifyPropertyChanged
を継承したクラスにすることで、個々の要素に対してバインド可能なプロパティを設定することに成功しています。
バインド先であるViewModel内
Private Class ViewModel
Public Inputs As t()
' ~~中略~~
Class t
Implements INotifyPropertyChanged
Event PropertyChanged As PropertyChangedEventHandler Implements INotifyPropertyChanged.PropertyChanged
Private _Value As String
Property Value As String
Get
Return _Value
End Get
Set
_Value = Value
RaiseEvent PropertyChanged(Me, New PropertyChangedEventArgs(NameOf(Value)))
End Set
End Property
End Class
End Class
配列とその要素の初期化
バインド先がnullだと普通にヌルポされちゃいます。ちゃんと配列とその要素を初期化しましょう。ついでに
配列の初期化
' ~~前略~~
Private Class ViewModel
Public Inputs As t()
Sub New()
Inputs = {
New t With {.Value = "a"},
New t With {.Value = "b"},
New t With {.Value = "c"}
}
End Sub
Sub Update()
Inputs(0).Value = "1"
Inputs(1).Value = "2"
Inputs(2).Value = "3"
End Sub
' ~~中略~~
End Class
バインドの設定
フォームロード時に、各テキストボックスに対して、ViewModelクラスのメンバにある配列Inputs
の要素と結び付けます。もちろん個々の要素に対してバインドするので、配列へのインデックスはきちんと決めてあげましょう。
フォームロード時に処理されるバインドの設定
' ~~前略~~
Public Class Form1
Private VM As New ViewModel
Sub Form1_Load(sender As Object, e As EventArgs) Handles MyBase.Load
TextBox1.DataBindings.Add(New Binding("Text", VM.Inputs(0), "Value"))
TextBox2.DataBindings.Add(New Binding("Text", VM.Inputs(1), "Value"))
TextBox3.DataBindings.Add(New Binding("Text", VM.Inputs(2), "Value"))
End Sub
' ~~中略~~
End Class
この時、指定されたインデックスに当てはまる要素がない場合、エラーが起きます。
あと、変な設定すると正しくバインドされませんでした(3敗)
だめな例__バインドの設定
' ~~前略~~
Public Class Form1
Sub Form1_Load(sender As Object, e As EventArgs) Handles MyBase.Load
' エラーになる:存在しない(ようにした)インデックスを指定
TextBox1.DataBindings.Add(New Binding("Text", VM.Inputs(5000), "Value"))
' エラーになる:プロパティ「名」としてのInputs(2)は存在しない。特に「(2)」が存在しない
TextBox3.DataBindings.Add(New Binding("Text", VM, "Inputs(2).Value"))
' エラーにはならないし、初回だけ値は設定されるけど、実はバインドされてない
TextBox2.DataBindings.Add(New Binding("Text", VM.Inputs(1).Value, ""))
End Sub
' ~~中略~~
End Class