今回取り上げるコンポーネント

  1. データグリッド【DataGrid】

本記事中のサンプルを表示するには最新のFlashPlayer9が必要です(本稿執筆時点では【9,0,28,0】が最新)。こちらから最新のFlashPlayer9をインストールしてください。また,インストール後はブラウザの再起動が必要です。インストールされているFlash Playerのバージョンを確認するにはこちら

 今回はデータグリッド【DataGrid】コンポーネントについて説明します。DataGridは非常に良く使われるコンポーネントの一つではないでしょうか。また,Flex2のDataGridはFlex1.x時代に比べ格段にパフォーマンスが向上したコンポーネントの一つでもあります*1

 昨年のAdobe MAX 2006(参考記事)では,Flex2のDataGridと複数のAjaxのフレームワークにおけるデータグリッドについて,パフォーマンスを比較するプレゼンを見ましたが,そのサンプルの中ではFlex2が一番パフォーマンスが出ていました(そもそも,パフォーマンスに影響が出るほどのデータをクライアントにロードする設計がまずいという意見もあるでしょうが,前述の内容は純然たる動作比較です)。

 DataGridは様々な利用が想定されるので2回に分けて紹介したいと思います。今回はDataGridそのものの機能というよりはDataGridにデータをセットする場合にまつわる内容を紹介したいと思います。

データグリッド【DataGrid】

 DataGridコンポーネント*2はとても便利で,しかも簡単に使用できるコンポーネントです。そして,見かけ通りに大きなコンポーネントでもあります。それは継承構造を見てもわかると思います。第9回で紹介したListコンポーネントの親クラスであるListBaseクラスを継承したDataGridBaseクラス*3をさらに継承しているのがDataGridコンポーネントです。

 ソースの「DataGrid.as」ファイルは165キロバイト,4800行強もあります。ASDocを出力する都合上多くのコメントが書かれているのも事実ですが,半分はコメントと仮定しても2400行。ソースを追う羽目になったら心が折れてしまいそうです。ソースをちらっと眺めると,5階層の"if"があったり,"if"&"else if"を連発してるところがあったりします。他人のソースは好き放題言えるものですが,ユーザーが望む多機能を実現しようと頑張る一方で,ソースの保守性を保つという"プロダクト"の命題の葛藤が垣間見えるような気がしました。

 もっとも,DataGridコンポーネントといえども,すぐに何でもかんでもできるわけではありません。意外に,応用+工夫しないとユーザーのニーズを実現できなかったりします。なので,開発を依頼する側であればExcelのような機能をイメージして見積もりを依頼するとびっくりするような見積書が返ってくるかもしれません。逆に開発する側であれば「できそう」と思っていたことが,ふたを開けてみると意外に大変だったりするので,見積もりは多めに提示したほうが良いかもしれません。

 本質的にはDataGridコンポーネントはDataGridColumnクラス*4という"列"を格納・管理するコンポーネントです。つまり,DataGridコンポーネントには"行"という概念がありません。非常に単純化して説明しますと,ヘッダー付きListのような列コンポーネントの集合体がDataGridと言えます。

 では,実際にサンプルを見てみましょう(図1)。

図1●【DataGrid】サンプル
図1●【DataGrid】サンプル(クリックすると別ウィンドウで表示します。ソースはこちら

 上から順に
1-1.ノーマル状態
1-2.オンコーディングでObject型データをセット
1-3.バインディング機能を利用してデータをセット(Array型)
1-4.バインディング機能を利用してデータをセット(ArrayCollection型)
1-5.バインディング機能を利用してデータをセット(XMLListCollection型1)
1-6.バインディング機能を利用してデータをセット(XMLListCollection型2)
1-7.ListやComboBoxにXMLListCollectionをセット
1-8.サーバーから取得したデータをセット
となります。

 1-1.はデータも無いノーマル状態です。ソースを見るとおわかりになると思いますが「mx:DataGrid」タグでは何もしていません。「columns」属性に「mx:DataGridColumn」を複数指定することで"列ごと"にヘッダーに表示する文字列や列の幅を指定しています。

 1-2.はオンコーディングでObject型のArrayを「dataProvider」属性にセットしています。DataGridでオンコーディングでデータをセットすることは無いと思うのですが,出力されるであろうデータをセットしてFlexBuilder2のデザインビューで列の幅を確認するのには役立ちます。ただ,このままでは確認用のデータが実行時にそのまま表示されてしまうので,実行時に「initilize」イベントで「dataProvider」属性を初期化するという方法もあります。例えば「<mx:DataGrid id="dg" initialize="dg.dataProvider = new ArrayCollection()">」のように。ちなみに,デザインビューで列を追加したり,列の幅を変更したりなどはできません。

 1-3.はDataGrid自体の属性値は「1-2」と同じで,トップレベル関数であるArray関数で作成したObjectの配列を「dataProvider」にセットしています(図2)。

図2●「Array型」のデータ内容(変数:_aryData)
図2●「Array型」のデータ内容(変数:_aryData)

 1-4.では「dataProvider」にバインディングする値として「mx.collections.ArrayCollection」クラス*5を使用しています。「ArrayとArrayCollectionで何が違うのか?」という質問には,以下のオンラインヘルプを引用しますと,

ArrayCollection クラスは,ICollectionView または IList インタフェースのメソッドとプロパティを使用して,アクセスおよび操作できるコレクションとして配列を公開するラッパークラスです。

と,なります。要するに,Arrayをデータ元とし高度なメソッドが用意されているクラスです。実際にArrayからArrayCollectionをインスタンス化しているコードは図3です。

図3●ArrayからArrayCollectionをインスタンス化(変数:_acData)
図3●ArrayからArrayCollectionをインスタンス化(変数:_acData)

 1-5.では前述のArrayCollectionクラスと同じ「mx.collections」パッケージ*6に所属している「mx.collections.XMLListCollection」クラスを使用しています。ArrayCollectionがArrayをデータ元としているのに対して,XMLListCollectionはその名の通り「XMLList」*7をデータ元としています。XMLListというのはXMLオブジェクトの中にある繰り返しデータのことです。「1-5」用XMLデータ構造は図4です。

図4●「1-5」用XMLのデータ構造(変数:_xElements)
図4●「1-5」用XMLのデータ構造(変数:_xElements)

 このデータ構造中でのXMLListに相当するのが「Person」です。したがって,XMLListCollectionには「_xElements.Person」をセットしています(図5)。

図5●「1-5」用XMLListCollectionのインスタンス化(変数:_xElementCollection)
図5●「1-5」用XMLListCollectionのインスタンス化(変数:_xElementCollection)
[画像のクリックで拡大表示]

 1-6.でも「1-5」と同様に「XMLListCollection」を使用していますが,元となるXMLのデータ構造が違います。「1-6」用XMLのデータ構造は図6です。

図6●「1-6」用XMLのデータ構造(変数:_xAttributes)
図6●「1-6」用XMLのデータ構造(変数:_xAttributes)

 「1-5」では「FirstName」などは「Person」の子要素として定義されていましたが,「1-6」では「Person」の属性として定義されています。ただ,「1-5」と同様に「Person」がXMLListです。そして,「1-6」用にXMLLstCollectionをインスタンス化しているのが図7です。

図7●「1-6」用XMLListCollectionのインスタンス化(変数:_xAttributesCollection)
図7●「1-6」用XMLListCollectionのインスタンス化(変数:_xAttributesCollection)
[画像のクリックで拡大表示]

 「1-5」と「1-6」のXMLListCollectionで大事なことは「mx:DataGridColumn」の「dataField」属性に指定している値の違いです。XMLオブジェクトにおいて子要素にアクセスする方法と属性にアクセスする方法が違います。子要素にアクセスする場合は「Person.FirstName」のように通常のドットシンタックスでアクセスできますが,属性にアクセスする場合は「Person.@FirstName」のように「アットマーク」を属性の前に付ける必要があります。「1-5」と「1-6」の「mx:DataGridColumn」の内容を見比べていただくとわかると思います。

 また,「1-5」にも「1-6」にも「dataProvider」属性にXMLListをセットすることができます。なぜなら,DataGridの親の親であるListBaseクラスの内部処理「public function set dataProvider」で,XMLListをXMLListCollectionにコンバートしているからです。同様にArrayをセットするとArrayCollectionにコンバートしています。

 ActionScript3のXMLに関してアドビシステムズのコンサルティング部門に最近異動された上条晃宏氏がブログで過去に解説されています。非常に有用なコンテンツですので,ここで紹介したいと思います。

 1-7.では以前に紹介しましたListコンポーネント,ComboBoxコンポーネントの「dataProvider」属性にも同様にXMLListCollectionを使用することができることを示しています。このように「dataProvider」にセットできる値は"ある規則"に則っていれば良いわけなのでICollectionViewまたはIListをインプリメントした自作クラスやArrayCollectionやXMLListCollectionを継承したクラスでも良いというわけです。

 1-8.はHTTPServiceクラス*8を使用してサーバーにHTTPリクエストを投げ,HTTPレスポンスからデータを抽出しDataGridにデータをセットする,というサンプルです(図8)。HTTPServiceクラスについては別の機会に他のRPC系クラスとともに紹介しますので,今回はDataGridに値をセットするところにフォーカスしたいと思います。

 「data1.xml」は図4と全く同じで,「data2.xml」は図6と全く同じです。また,「dg1.dataProvider = dataSet;」の行にブレークポイントを設定してデバッグ実行をすると,ここで説明している内容についてさらに理解を深めることができると思います。ぜひ,試してみてください。

図8●結果データセットのフォーマットによって処理を分岐
図8●結果データセットのフォーマットによって処理を分岐

 まず「resultFormat」属性が「object」の場合はレスポンスデータ(テキスト)を単純なObject(構造体)に自動変換してくれます。「ResultEvent.result」のレスポンス時のデータ型は「ObjectProxy」というラッパークラスになります*9。また,データ行に相当する「Person」には単純にドットシンタックスでアクセスできます。したがって,「dataProvider」属性に値をセットするには「ResultEvent.result.root.Person」をセットします。

 しかし,「data2.xml」の場合は,エラーも発生しなければデータも表示されません。この"何も起こらない"というのが問題を切り分けるのに厄介なところです。本来,システム開発においては結果データセットは仕様で決められているので,そんなに労せずしてDataGridにデータを表示することができると思います。しかし,勉強や興味がてらにちょっと触ってみようといったときは結果データセットは案外意識しないもので,"何も起こらない"という状況になると"どうすべきなのか?"もわからないわけです。

 今回のサンプルにおいては,「data2.xml」用のDataGridでは「dataField」属性に「アットマーク」付のフィールド名を指定しています。つまり,結果データセットがXMLでない限り「@FirstName」といったプロパティは存在しないわけで,故に"見えないレコード"は存在しているけどもデータが表示されないという状態になってしまうわけです。

 次に「resultFormat」属性が「xml」の場合はXML系のクラスの作法に従う必要があります。また,前述のObjectの場合,結果データセットは「root」よりも一つ上の階層であったのに対し,xmlの場合は「root」が結果データセットになります。したがって,「Person」は「root」の1階層下なので「children」メソッドを使用してアクセスしています。また,「children」メソッドで取得できる値は「Person」のXMLListなので,その値をDataGridの「dataProvider」属性にセットすることでデータが表示されます。

 最後に「resultFormat」属性が「e4x」*10の場合は結果データセットはxmlであるにもかかわらずObjectのようにして「Person」にアクセスできます。この新機能はxmlデータを取り扱ううえで今までより直感的に扱えるようになるので,ぜひとも覚えたい機能です。

 「Person」にアクセスするためには「children」メソッドではなく,ドットシンタックスで,かつ,xmlのように「root」が結果データセットの一番上の階層になるので「ResultEvent.result.Person」のようにアクセスし,「dataProvider」属性にセットします。この「e4x」の場合は「data1.xml」「data2.xml」ともにDataGridに表示されています。つまり,Objectのようにドットシンタックスでアクセスできる一方で,「FirstName」などの属性には「アットマーク」でアクセスすることも可能というわけです。