注意
この記事は2008年6月27日に公開しましたが,2008年9月9日に更新しました(最後の「RubyやPythonを使える「DynamicSilverlight」は除く)。この記事のコードはSilverlight Beta 2をもとにしています。そのため,2008年秋にリリース予定の正式版では,そのままのコードでは動作しない可能性があります。

記事中のサンプル・コードをこちらからダウンロードできます。

 最終回となる今回は,Silverlightアプリケーションの表現力を高める「Deep Zoom」とアニメーションの機能を取り上げます。さらに,RubyやPythonなどの動的言語でSiliverlightアプリケーションを作る「DynamicSilverlight」についてもコラムを設けて紹介します。

Deep Zoomテクノロジ

 第2回で紹介したように,Deep Zoomは任意の解像度/サイズの画像を,インタラクティブに表示/操作するテクノロジです。SilverlightではMultiScaleImage要素を使って,このDeep Zoomのデータを表示できます。

 これを使って,マウス操作で画像の平行移動と拡大/縮小ができるWebアプリケーションを作ってみましょう。Silverlightにはマウスのホイール用のイベントハンドラがありませんが,ヘルパー関数を使ってホイールを処理する手法も紹介します。

Deep Zoom Composerでデータを作る

 最初にDeep Zoom用のデータを作成します。マイクロソフトのWebサイトから「Deep Zoom Composer」をダウンロードしてインストールしてください*1。Deep Zoom Composerを起動し,適当な画像をインポートして,レイアウトします(図1)。画像は1枚でも複数枚でも構いません。レイアウトできたら,[Output Type]オプションを[Export Images]にして,名前を付けてエクスポートします。ここでは名前を「myDeepZoom」としました。この名前がエクスポート先のフォルダ名になります。

図1●Deep Zoom ComposerでDeep Zoomのデータを作る。背景に大きな画像を置き,その中に小さな画像を二つ置いてみた
図1●Deep Zoom ComposerでDeep Zoomのデータを作る。背景に大きな画像を置き,その中に小さな画像を二つ置いてみた
(画像協力:高橋忍氏)

 エクスポート先のフォルダには,0から始まる数字のフォルダ名を持つ複数のフォルダと,三つのファイルdzc_output.xmlとMetadata.xmlとSparseImageSceneGraph.xmlが作られています。後ほど,このdzc_output.xmlをSilverlightのMultiScaleImageのソースとして指定します。数字が付いたフォルダの中身を確認すると,様々なレベルに縮小,分割された画像が格納されているのがわかります。MultiScaleImageは,元になっているDeep Zoomデータのどの部分をどれくらいの大きさで表示しようとしているかによって,どのフォルダのどの画像をロードすべきかを自動的に決め,それを非同期的にロードして,最適な画像を表示します。

 データが用意できたので,アプリケーションを作っていきましょう*2。Visual Studio 2008で,Visual C#のSilverlight Applicationテンプレートをベースに新規プロジェクト「DeepZoom」を作成してください。すぐにF5キーを押してビルド/実行し,ブラウザを閉じます。そして,先ほどDeep Zoom Composerで作ったmyDeepZoomフォルダ全体を,DeepZoom_Web¥ClientBinフォルダにコピーします*3

 次に,DeepZoomプロジェクトに含まれるXAML(Extensible Application Markup Language)ファイル「Page.xaml」を編集します。リスト1(1)のようにMultiScaleImageタグをGridタグに挿入するだけです。コピーしたdzc_output.xmlをソースとして指定して,マウスのイベントハンドラを三つ追加します。F5キーを押してビルド/実行するとDeep Zoom Composerで生成した画像が表示されるはずです。しかし,イベントハンドラのコールバック関数は,中身がまだ空なので,マウスをドラッグしてもホイールを動かしても何も起こりません。

リスト1●Deep Zoomのデータを表示するMultiScaleImage要素を追加する(Page.xaml)
リスト1●Deep Zoomのデータを表示するMultiScaleImage要素を追加する(Page.xaml)
[画像のクリックで拡大表示]

マウス操作に応じて画像を拡大/縮小,移動する

 マウス操作に対応するコールバック関数を実装していきましょう。下準備として,ホイールのイベントを処理するヘルパー関数を導入します。Pete Blois氏のブログに公開されているMouseWheelHelperプロジェクトをダウンロードして利用してみましょう。URLはhttp://blois.us/Silverlight/Scrolling2/akadiasl.zipです。このZIPファイルの中にあるMouseWheelHelper.csファイルをDeepZoomプロジェクト・フォルダにコピーします。コピーしたらVisual Studioのソリューションエクスプローラで,「DeepZoomプロジェクト」を右クリックし,[既存項目の追加]を選びます。表示されたダイアログで,今コピーしたMouseWheelHelper.csを選び,「開く」をクリックします。

 Page.xaml.csを開き,ファイル先頭のusingディレクティブが並んでいる個所に

using akadia;

を追加し,F6キーでビルドしてください。これでMouseWheelHelperが使えるようになりました。コンストラクタのInitializeComponentメソッド呼び出しの後にMouseWheelHelperのイベントハンドラを追加し,myMSI_MouseWheelMovedコールバック関数を生成します。コードはリスト2(2)のようになります。

リスト2●マウス操作に対応するコールバック関数を実装する(Page.xaml.cs)
リスト2●マウス操作に対応するコールバック関数を実装する(Page.xaml.cs)
[画像のクリックで拡大表示]

 マウス操作に対応する処理の基本的な戦略は,ドラッグに応じてMultiScaleImage.ViewportOriginプロパティを更新すること,そしてマウス・ホイールの操作に応じてMultiScaleImage.ZoomAboutLogicalPointメソッドを呼び出すことの二つです。まず,マウスが押されているかどうかを示すフラグ(mouseButtonPressed)と,現在のマウス位置(currentMousePos),ドラッグ開始時のマウス位置(dragStartMousePos)と,そのときのビューポート原点*4(dragStartOrigin)を格納するメンバー変数をリスト2(1)のように宣言します。

 (3)のコールバック関数myMSI_MouseWheelMovedでは,マウス・ホイールの操作に対応します。まず,(4)でマウス・ホイールの操作が前向きか後ろ向きかに応じて,ズーム因子を決めます。(5)ではelementToLogicalPointメソッドを使って,マウス位置を画像空間の論理位置に変換します。(6)ではZoomAboutLogicalPointメソッドを使ってその論理位置を中心にズームさせます。

 (7)のコールバック関数myMSI_MouseLeftButtonDownでは,左ボタンが押された時のマウス位置とビューポート原点をメンバー変数に格納し,マウス押し下げフラグをtrueにします。(8)のコールバック関数myMSI_MouseMoveでは,ドラッグ時に画像の平行移動を行うために,新しいマウス位置から新しいビューポート原点を計算してViewportOriginプロパティに代入します。(9)のボタン・アップに対応したコールバック関数では,マウス押し下げフラグをfalseにするだけです。

 F5キーを押してビルド実行すると,図2のようにUserControlのサイズでマルチスケール画像が表示されます。マウス・ホイールの操作で拡大/縮小ができ,マウスのドラッグで上下左右への平行移動もできます。拡大/縮小時には,マウス位置を中心として拡大/縮小されることにも注意してください*5

 実は,Deep Zoom Composerでエクスポートするとき,[Output Type]オプションを[Export Images and Silverlight Project]にすると,MouseWheelHelper.csも含まれた,これらのコードが実装されたプロジェクトが生成されます。

図2●MultiScaleImageを使ったDeep Zoom画像の表示。図1でレイアウトした画像の中央部分が表示されていることがわかる。マウス操作で拡大/縮小と移動ができる
図2●MultiScaleImageを使ったDeep Zoom画像の表示。図1でレイアウトした画像の中央部分が表示されていることがわかる。マウス操作で拡大/縮小と移動ができる
(画像協力:高橋忍氏)