DataContextやBindingのテスト
キーワードは
DataBinding:縛る、束ねる紐付けるかな
DataContext:文脈、脈絡これも繋がりみたいな感じね
INotifyPropertyChanged:通知Notifyが通知でこれはそのまま
Implements:用具、実装ググってると実装はたまによく聞く
オレンジと緑の四角はBorderコントロール
このBorderの縦横の大きさや色をスライダーやボタンを使って変化させている
クリックした方のBorderだけ変化する
こっちを変えたらあっちも変わる、あっちを変えたらこっちも変わるとかの
コントロール同士の連携みたいなのをBindingを使うと自動で処理してくれる
デザイン画面とXAML
VBコード
Imports System.ComponentModel
Class MainWindow
'Private DataList As New List(Of ExData) 'これをObservableに変更すれば良さそう↓
Private DataList As New System.Collections.ObjectModel.ObservableCollection(Of ExData)
Private ImaBoder As Border '選択中のBorderを入れておく用
Private BoderList As New ObjectModel.ObservableCollection(Of Border) '全部のBorderを入れておく用
'DataContextの設定
Private Sub DataReset()
DataList.Clear()
'DataList.Add(New ExData With {.Height = 50, .Width = 50, .BackgroundColor = New SolidColorBrush(Colors.Orange)})
'DataList.Add(New ExData With {.Height = 10, .Width = 70, .BackgroundColor = New SolidColorBrush(Colors.Green)})
Dim data As New ExData
With data
.Height = 50
.Width = 50
.BackgroundColor = New SolidColorBrush(Colors.Orange)
End With
DataList.Add(data)
data = New ExData
With data
.Height = 10 : .Width = 70
.BackgroundColor = New SolidColorBrush(Colors.Green)
End With
DataList.Add(data)
For i As Integer = 0 To BoderList.Count - 1
BoderList(i).DataContext = DataList(i)
Next
'grid1はGridでスライダーなどが配置されている
'grid1.DataContext = DataList(CuBoder.Tag)
grid1.DataContext = ImaBoder.DataContext '↑と同じ結果だけどこっちのほうがいいかな
End Sub
Private Sub buttonReset_Click(sender As Object, e As RoutedEventArgs) Handles btxReset.Click
Call DataReset()
End Sub
Private Sub MainWindow_Initialized(sender As Object, e As EventArgs) Handles Me.Initialized
'Border作成してStackPanelに追加
For i As Integer = 0 To 1 ' 2つ作る
Dim b As New Border
With b
'Binding設定
.SetBinding(WidthProperty, New Binding("Width"))
.SetBinding(HeightProperty, New Binding("Height"))
.SetBinding(BackgroundProperty, New Binding("BackgroundColor"))
.Tag = i
End With
sp1.Children.Add(b) 'sp1はStackPanel、そこに作ったBorderを配置
AddHandler b.MouseDown, AddressOf Border_Click '左クリックした時の動作
ImaBoder = b '選択中のBorder
BoderList.Add(b) 'Borderのリストに追加
Next
Call DataReset()
'sld1.SetBinding(Slider.ValueProperty, New Binding("Width"))
'↑↓のBindingは同じ、vbコードで指定するかXAMLで指定するかの違い
'<Slider x:Name="sld1" Maximum="100" Value="{Binding Width}"/>
End Sub
'Borderを左クリックした時の動作
Private Sub Border_Click(sender As Object, e As RoutedEventArgs)
Dim b As Border = DirectCast(sender, Border)
ImaBoder = b
grid1.DataContext = ImaBoder.DataContext 'スライダーのDataContextを変更
tbxCB.Text = ImaBoder.Tag.ToString
End Sub
Private Sub button1_Click(sender As Object, e As RoutedEventArgs) Handles button1.Click
Dim d As ExData = ImaBoder.DataContext
d.BackgroundColor = New SolidColorBrush(Colors.Cyan)
d.Width = 70
End Sub
Private Sub button2_Click(sender As Object, e As RoutedEventArgs) Handles button2.Click
Dim d As ExData = DataList(ImaBoder.Tag)
d.BackgroundColor = New SolidColorBrush(Colors.Pink)
d.Width = 40
End Sub
'これは失敗、実行するとBindingが無効になってしまう
Private Sub button3_Click(sender As Object, e As RoutedEventArgs) Handles button3.Click
'ImaBoder.Background = New SolidColorBrush(Colors.Blue)
'ImaBoder.Width = 20
End Sub
End Class
'プロパティ -Programming / .NET Framework - 総武ソフトウェア推進所
Public Class ExData
Implements ComponentModel.INotifyPropertyChanged
Public Event PropertyChanged As PropertyChangedEventHandler Implements INotifyPropertyChanged.PropertyChanged
Private Sub OnPropertyChanged(name As String)
RaiseEvent PropertyChanged(Me, New PropertyChangedEventArgs(name))
End Sub
Private Property _Width As Double
Public Property Width As Double
Get
Return _Width
End Get
Set(value As Double)
_Width = value
Call OnPropertyChanged("Width")
End Set
End Property
Private Property _Height As Double
Public Property Height As Double
Get
Return _Height
End Get
Set(value As Double)
_Height = value
Call OnPropertyChanged("Height")
End Set
End Property
Private Property _BackgroundColor As SolidColorBrush
Public Property BackgroundColor As SolidColorBrush
Get
Return _BackgroundColor
End Get
Set(value As SolidColorBrush)
_BackgroundColor = value
OnPropertyChanged("BackgroundColor")
End Set
End Property
End Class
簡単なBindingの例だと上のアプリではスライダーとその値を表示しているTextBlockがBindingで連携している
このBindingはXAMLのほうで指定している
TextblockのTextプロパティにsld1と名前をつけたSliderの値ValueをBinding
ElementNameに連携するコントロールを指定、ここではsld1
Pathに連携するプロパティを指定、ここではValue
StringFormatは表示形式の指定で0.0だと小数点一桁まで表示になる
これだけでスライダーを動かすと自動でその値をTextBlockに表示されるようになる
TextBlockのTextとSliderのValueが同期する感じ
SliderのValueに{Binding Width}ってBinding指定をしているのはTextBlockとは関係なくてDataContextのWidthプロパティとのBindingになる
今回の場合だと
grid1(Gridコントロール)のDataContextにいろいろなプロパティを入れたものを指定しておいて、その中のWidthプロパティとSliderのValueプロパティをBindingしている
いろいろなプロパティを入れておくクラスを用意
ExDataって名前をつけたクラスを作ったのがこの部分で、今回はまだだけど最終的にはシリアライズしてファイルに保存する予定
Width、HeightがDouble型、BackgroundColorはSolidColorBrush型?
基本的には3つのプロパティをもたせただけの簡単なクラスなんだけど
例えばこのクラスのWidthとスライダーのValueをBindingさせた状態で、どちらか片方の値が変化した時にもう一方の値も変化させるっていう双方向のBindingにしたい場合は、このクラスのWidthを変化させた時に相手側に通知する必要がある
そのためには通知する機能を持たせる必要がある、それが
INotifyPropertyChangedっていうインターフェイスってものらしい
これは
プロパティ - Programming/.NET Framework - 総武ソフトウェア推進所ここを参照した、丁寧に説明されているのでここ見たほうが早い
http://smdn.jp/programming/netfx/property/#INotifyPropertyChanged
って打ってエンターキー押すと
ってのが自動で入力される
どうやら必要なEventらしい、これが実行されると変更が通知されるってことなのかな
続いて、このEventを実行させるメソッド?が必要みたいで
Private Sub OnPropertyChanged(name As String)
RaiseEvent
End Sub
RaiseEventってのがEventを実行させる宣言みたいなものかな、ここまで入力するとEventの一覧表が出てくる、一覧表と言ってもさっき自動で追加されたPropertyChanged一個しかないけどね、面白い
PropertyChangedイベントを選んで入力すると引数が要るみたいで
senderはこのクラス自身のMeで、もう一つはPropertyChangedEventArgs
これで通知機能を持たせることができたになるのかな
あとは通知するタイミング
変更(Set)した直後に通知したいから
例えばWidthってプロパティの場合は
以上でプロパティの変更通知機能付きのExDataクラスの完成
Borderを全部を入れるリスト
ExDataを全部入れるリスト
選択中のBorderを入れる
アプリ起動時の初期設定
縦横の大きさや色の指定はしない
SetBindingを使ってBindingする各プロパティを指定
Tagには連番を入れているけど、必要じゃなかった
左クリックで選択中のBorderを切り替えるのでそのイベントに関連付け
作成したBorderをリストに入れる
最後の call DataResetは
14行目から19行目までが1つ分のExDataクラスを作成して各プロパティに値を指定している
この6行分は
DataList.Add(New ExData With {.Height = 50, .Width = 50, .BackgroundColor = New SolidColorBrush(Colors.Orange)})
っていう1行でもできるみたいで、これは慣れたらこっちのほうがいいのかな?
29行目からの3行で各BorderのDataContextに各ExDataを指定している
これでBorderとExDataが連携できるBindingが完了
35行目はgrid1(Grid)のDataContextに今選択中のBorderのDataContext(中身はExData)を指定している、これでgrid1の中に配置されているコントロールはこのDataContextを参照できるみたいなので、わざわざ1つ1つのコントロールのDataContextに指定しなくても良くなっている
実際にこのDataContextとBindingしているのは
19行目と24行目のSliderでそれぞれ自身のValueをDataContextのなかのWidthとHeightにBindingしている
Borderをクリックした時
grid1のDataContextを選択中のBorderのDataContextにする
これでクリックされたBorderの横幅、高さの値がSliderに反映されるしSliderを動かすとBorderのサイズが変更されるようになる
各ボタンをおした時の動作
Button2はExDataのリスト中のExDataの中の色と横幅を変更している
Button3は選択中のBorderの色と横幅を変更している、けど今回では失敗しているのでコメントアウトしている、これを実行するとBorderは変化するけどSliderとExDataとのBindingが外れてしまったような状態になって、Sliderを動かしてもサイズ変更しなくなる
WindowsFormアプリ作成でのコントロール同士の連携はめんどくさかった
Aを変えたらBも変える、Bを変えたらAも変えるって普通に書くと無限ループのはずなんだけど空気を読んでくれているのかループしないようになっていた気がする
WPFでは同じように書くと無限ループするのでフラグを用意して処理していた
この前まで作っていたカラーピッカーがそれ
BindingってのはWPFの特徴みたいでBindingを使うとWPFっぽくなる感じ
でも慣れていないせいかBindingもめんどくさい感じがするし難しいw
参照したところ
プロパティ - Programming/.NET Framework - 総武ソフトウェア推進所
http://smdn.jp/programming/netfx/property/#INotifyPropertyChanged
データ バインドの概要
https://msdn.microsoft.com/ja-jp/library/ms752347(v=vs.110).aspx
【WPF】データバインディングの基本 | ザワプロ!
http://zawapro.com/?p=790
【WPF】xamlと同等の内容をC#のコードで書いた例 | ザワプロ!
http://zawapro.com/?p=938
INotifyPropertyChanged インターフェイス (System.ComponentModel)
https://msdn.microsoft.com/ja-jp/library/system.componentmodel.inotifypropertychanged(v=vs.110).aspx
コード全部
Wpf_test96_データバインドDataContext - Visual Studio Team Services
https://gogowaten.visualstudio.com/DefaultCollection/WPF/_git/WPF_test6?path=%2FWpf_test96_%E3%83%87%E3%83%BC%E3%82%BF%E3%83%90%E3%82%A4%E3%83%B3%E3%83%89DataContext&version=GBmaster&_a=contents