データバインディング(1)

前回のMXMLをFlexBuilderのデザインエディタでいじって、とりあえずこんなものを作ってみました。

<?xml version="1.0" encoding="utf-8"?>
<mx:Application xmlns:mx="http://www.adobe.com/2006/mxml" layout="vertical">
	<mx:Form backgroundColor="halogreen">
		<mx:FormItem label="性別" backgroundColor="haloblue">
			<mx:HBox width="100%" backgroundColor="red">
				<mx:RadioButtonGroup id="radiogroup1"/>
				<mx:RadioButton label="男" groupName="radiogroup1"/>
				<mx:RadioButton label="女" groupName="radiogroup1"/>
			</mx:HBox>
		</mx:FormItem>
	</mx:Form>
</mx:Application>

男女の区別を入力するフォームです。
HTMLで書くのに似ていますね。

違うのは、
・タグで使われるのは一つ一つがFlexコンポーネントである
コンポーネント名前空間を示す接頭辞(XX:)がついている
ぐらいでしょうか。
mx:という接頭辞はAdobeの用意した名前空間で、Flashの標準クラスとFlexの標準クラスはすべてこの名前空間に存在します。
つまり、標準の部品を使う限りは、mx:がついていればよいということです。
そして、自分の作成したコンポーネントを使う場合には、そのコンポーネント名前空間を表す接頭辞をトップクラスのタグ内で宣言し、その接頭辞を前につけます。これもBuilderを使う分には意識しないで済みます。


これだけではつまらないので、選択した値をテキストで表示してみましょう。

<?xml version="1.0" encoding="utf-8"?>
<mx:Application xmlns:mx="http://www.adobe.com/2006/mxml" layout="vertical">
	<mx:Form backgroundColor="halogreen">
		<mx:FormItem label="性別" backgroundColor="haloblue">
			<mx:HBox width="100%" backgroundColor="red">
				<mx:RadioButtonGroup id="radiogroup1"/>
				<mx:RadioButton label="男" value="男" groupName="radiogroup1"/>
				<mx:RadioButton label="女" value="女" groupName="radiogroup1"/>
			</mx:HBox>
		</mx:FormItem>
	</mx:Form>
	<mx:Label text="{radiogroup1.selectedValue}"/>
</mx:Application>

ラジオボタンを選択すると、選択されたラジオボタンvalueが、FORMの下のラベルに表示されます。
こういうのってHTMLだと、JavaScriptなんかでコードを書いてやる必要がありますよね?
MXMLでは、これぐらいの簡単な処理はScriptを書かなくてもできてしまいます。


とはいえ、内部では結構複雑なことが起きています。

1.選択されたRadioButtonから、changeイベントが発生する
2.radiogroup1というidのRadioButtonGroupに登録されているので、radiogroup1はそのイベントをキャッチし、ラジオボタンが選択されたことを知る。
3.radiogroup1は自分の持つプロパティselectedValueを更新する。このとき、radiogroup1もまたselectedValueが更新されたというイベントを発生する。
4.labelがradiogroup1.selectedValueの内容が変更されたというイベントをキャッチして、textを更新する。

このように、あるオブジェクトの値の変更を受け取って、自身のもつプロパティを自動的に書き換えることを「データバインディング」といいます。MXMLでは、データバインディングを十分に理解することがとても重要です。


うえの処理の流れをふまえて、データバインディングの重要な点を押さえておきましょう。

1.データバインディングは、発信元から受信先への一方通行

 上の流れをみるとわかるように、データバインディングはイベントを(受け取る対象がいるかいないかにかかわらず)投げるコンポーネント(発信元)と、あらかじめイベントを発生するオブジェクトを監視するコンポーネント(受信先)から成り立っています。
したがって、データが伝わっていく方向は一方通行です。

 たとえばLabelを文字入力が可能なコンポーネントであるTextBoxに書き換えたプログラムを作成し、「男」と表示されたところを「女」と入力し直しても、ラジオボタンがそれを反映して「女」が選択されるといったことはありません。
双方向にする場合はお互いをバインディングしますが、無限ループに陥らないような対策がコンポーネント側でとられている必要があります。

2.イベントを投げないオブジェクトとはバインディングできない

 バインディングされるデータオブジェクトは、その内容が変わったときにイベントを投げるようコーディングされていなければなりません。
 例のradiogroup1.selectedValueも、selectedValueというプロパティ自身が変更されたときにradiogroup1がイベントを投げるからこそ、その変化を検出することができます。 
 このことは、複雑なデータオブジェクトを使うときに忘れがちです。
 例えば、selectedValueに入っていたデータが文字列ではなく、いくつものプロパティをもつカスタムクラスのオブジェクトだったとします。そのとき、text="{radiogroup1.selectedValue.name}"などと書いても、うまく動作しません。
 なぜなら、送信元が発信しているのはあくまでselectedValueが更新されたというイベントであって、その中のnameというプロパティが更新されてもイベントを発生しないからです。
 また受信側についても、監視するのは「最初に選択されていたラジオボタンvalueに入っていたオブジェクトのなかのnameプロパティ」になってしまい、selectedValueがほかのラジオボタンのものに入れ替わっても、最初のラジオボタンのデータを監視し続けてしまいます。
 
 このような状態を解決するには、ActionScriptを用います。これを次回で説明します。

最後になりましたが、{}の説明です。

これはMXMLの要素の値の中で、ActionScriptを記述したいときに利用します。つまり、簡単な計算や処理、定数、プロパティ、メソッド名などはたいてい{}でくくる必要があります。
これを使わなかった場合、プロパティの受け取る値の型と見なされます。
例えば、Labelのtextに、アクションスクリプトの変数strの中身を代入したいならば text="{str}" と書かなければならず、逆にtextにstrという文字列を表示する場合は text="str" と書く必要があります。


次回は、データバインディングの続きとして、バインドするデータをAS3で処理する方法をやります。
MXMLの中で、Scriptタグを使ってAS3のコードを記述します。