カスタムコンポーネントで注意すること(invalidateProperties / commitProperties)
カスタムコンポーネントを作っていて、ちょっとはまりそうになったことのメモです。
Flex の作法ではプロパティ値の変更時にいきなり UI を更新してはいけません。プロパティ値が変更されたときは、変更を記録しておいて invalidateProperties を呼び出すに留めます。invalidateProperties を呼び出しておくと、一連の処理後*1に commitProperties が呼び出されます。このタイミングで変更のあった*2プロパティの値とそれに影響される他の値を確定し、必要であれば invalidateSize や invalidateDisplayList を呼び出して、UI を更新します。
この辺りのことは、id:s-ohira さんの asでカスタムコンポーネント(1)、(2)、(3) が詳しいです。
簡単にまとめると、
- プロパティ値を変更する
- invalidateProperties を呼び出す
- commitProperties が呼び出される
- commitProperties 内でプロパティ値の計算等をする
という流れになります。
通常はこの流れに従い commitProperties を実装するだけなのですが、commitProperties の中で他のプロパティ値を変更する場合は注意が必要です。というのも commitProperties の処理中は invalidateProperties の呼び出しが無効化されるようです*3。このため、commitProperties の処理中に変更したプロパティの内容は、その commitProperties の処理中に反映する必要があります。
例えばコンボボックスを継承したカスタムコンポーネントで、commitProperties 処理中に ComboBox の selectedItem プロパティを変更する場合を考えます。selectedItem プロパティのセッターでは invalidateProperties 呼び出しが記述されていると思うのですが、commitProperties の処理中では無効になってしまい、表示が更新されません。そのため、selectedItem プロパティの変更を反映するには、プロパティ変更後に super.commitProperties を呼び出す必要があります。
ここで、プロパティ変更と super.commitProperties 呼び出しの順序を逆にしてしまうと*4、プロパティ変更に対する commitProperties 呼び出しが無いため、表示が更新されない状態になります。
というわけで commitProperties をオーバーライドした場合、super.commitProperties はメソッド末尾で呼び出すのが良いようです。
以下、ソースコード例です。foo プロパティで設定した値を選択するコンボボックスです。処理内容に意味はありません。*5
コンパイルしていないので間違っているかもしれないです :-P
public class ComboBoxEx extends ComboBox { private var _invalidateFoo:Boolean; private var _foo:Number; public function set foo(value:Number):void { _foo = value; _invalidateFoo = true; invalidateProperties(); } protected override function commitProperties():void { if (_invalidateFoo) { _invalidateFoo = false; selectedItem = _foo; } super.commitProperties(); } }