<コントロール同士のバインディング>
まずは、WPFのバインディングで最初の頃必ず出てきたテキストボックスとスライダーのバインディング。
スライダーを動かすと数値が変化し、数値を入力するとスライダーが動くというやつだ。(双方向のバインディング)
XAMLはこんな感じになる。
<Window x:Class="BindingSample.Window1"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
Title="Window1" Height="300" Width="300">
<StackPanel>
<TextBox Name="myValue">0</TextBox>
<Slider Maximum="100" Minimum="0" Value="{Binding ElementName=myValue, Path=Text}" />
</StackPanel>
</Window>
スライダーのvalueプロパティにテキストボックスのTextプロパティをバインディングするには、
Value="{Binding ElementName=myValue, Path=Text}"
というようにバインドするコントロール名とプロパティ名を指定してあげる。
ちなみに、この{}で囲んだ書き方は「マークアップ拡張」と呼ばれるXAML独自の記法(多分)で、本来は以下のように書くべきもの。
<Slider Maximum="100" Minimum="0">
<Slider.Value>
<Binding ElementName="myValue" Path="Text" />
</Slider.Value>
</Slider>
多分バインドは多用されることになるためシンタックスシュガー的に簡略記法を用意したのだろう。
ただし、私などは最初この省略形であるということが分からずにどうもピンと来なかった。
というか世の中のサンプルを見ていると様々な記法で書かれており(Pathというキーワードも省略できたり)、一体何が違うのかと混乱していた。
バージョンが上がって省略記法が用意されれば解りやすかったのだが、私のような素人にはちょっと取っ付きづらい部分だった。(最初から気が効きすぎていたというか)
また、上記TextBoxにはNameプロパティで名前を指定しているのだが、これもサンプルによってはx:nameと別のネームスペースを使っていたりして混乱した。
今回再勉強して分かったのは、この名前空間はXAML独自の名前空間らしいということ。WPFとして用意されていない要素、属性などはXAMLとして独自に拡張した機能をこの名前空間で提供しているらしい。
なのでnameプロパティにしてもWPFでもともと存在しているものについてはそれを使えばよいし、nameプロパティを持たないコントロールなどを参照したいときはx:nameで名前をつけてあげれば良い。
データバインドは必ずしも1対1でなくとも良いので、最初のサンプルに省略しない記法で書いたスライダーをもう一つ追加するとこんな感じになる。
<Window x:Class="BindingSample.Window1"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
Title="Window1" Height="300" Width="300">
<StackPanel>
<TextBox Name="myValue">0</TextBox>
<Slider Maximum="100" Minimum="0" Value="{Binding ElementName=myValue, Path=Text}" />
<Slider Maximum="100" Minimum="0">
<Slider.Value>
<Binding ElementName="myValue" Path="Text" />
</Slider.Value>
</Slider>
</StackPanel>
</Window>
どちらのスライダーを動かしても双方張り付いて全く同じに動く。
ちなみに、このnameプロパティの値はは大文字と小文字が区別され、間違ったりしてもエラーにならないので注意が必要だ。
Bindオブジェクト(要素)はModeプロパティでバインド方向を、
UpdateSourceTriggerプロパティでそのタイミングも指定できるようになっている。
ここで、こんどはXAMLではなくコードでバインドするとどうなるか試してみた。
Sliderをもう一つ追加し名前を”mySlider”とした。
<Slider Maximum="100" Minimum="0" Value="{Binding ElementName=myValue, Path=Text}" />
<Slider Maximum="100" Minimum="0">
<Slider.Value>
<Binding ElementName="myValue" Path="Text" />
</Slider.Value>
</Slider>
<Slider Name="mySlider" Maximum="100" Minimum="0" />
public Window1()
{
InitializeComponent();
var bind = new Binding();
bind.ElementName = "myValue";
bind.Path = new PropertyPath("Text");
mySlider.SetBinding(Slider.ValueProperty, bind);
}
XAMLの省略していない方の記述を見ながらそのとおりにコーディングしていけばよいので、それほど難しくはない。XAMLが入れ子で表現している部分はSetBindingメソッドを使ってあげれば良いようだ。
SliderのプロパティはSlider自身が持っているxxxPropertyスタティックプロパティが使える。
PropertyPathクラスのコンストラクターで指定している引数(型の定義はobjectになっている)も本来はTextBox.TextPropertyなのだと思うがXAMLと同様に”Text”でも動作してくれる。
PropertyPathクラスは以下のような定義になっているので、
[TypeConverterAttribute(typeof(PropertyPathConverter))]
public sealed class PropertyPath
ここで指定されたPropertyPathConverterが文字列からTextPropertyへの変換をしているのかな・・・。
単純に考えれば、文字列を取るコンストラクターを用意すればよいだけに思えるのだが・・・。
そうか、こいつはコンパイル時に変換してくれるという事なのかも。
<自分自身や親要素とのバインド>
<TextBox Height= "65"
Text="{Binding RelativeSource={RelativeSource Mode=FindAncestor, AncestorType=Window},
Path=Title}"
Width="{Binding RelativeSource={RelativeSource Self}, Path=Height }" />
バインドの対象を指定するのにはnameプロパティを参照する以外にも、このような指定もできる。
2行目では自分の親をたどってWindowコントロールを探し、そのタイトルプロパティとバインドしている。
はっきり言ってちょっと複雑・・・。
それほど使う機能でも無いのでこんなものだとメモしておけば良いか・・・。
4行目はWidthプロパティに対して自分自身のHeightプロパティをバインドしている。(結果的に正方形になる)
ココらへんは記法は面倒だが、当初に比べればだいぶインテリセンスが効いてくれるのですこしは楽になった。
0 件のコメント:
コメントを投稿