はじめに
そのとき、神は「光(Silverlight)あれ。」と言われた。すると光(Silverlight)があった。――さて、この「Silverlight」とは何なのでしょうか。Silverlightは、一言で言えば、Web向け次世代双方向コンテンツを作るためのMicrosoft流ソリューションです。XAMLとJavaScriptを使い、メディアを多用して双方向性を生かしたキラーアプリケーションを作ることができます。現在は、WPFのサブセットです。Silverlightの長所は、この開発フレームワークがクロスブラウザだという点です。プラグインをインストールするだけで、どこででも開発することができます。Macromedia(現Adobe Systems)のFlashに似ていますが、Silverlightの場合、アプリケーションを動かす実体(XAML)は完全なテキストですから、対検索エンジンでは有利です。
Silverlight(バージョン1.0)は、以前は「WPF/e」と呼ばれていました。現在はMicrosoftのWebサイトからダウンロードすることができます。なお、Microsoftは既にバージョン1.1のベータ版を開発者向けにリリースしています。
注
この記事の趣旨に従い、ここではバージョン1.0 RCを使います。
概要
この記事では、Silverlightを使って気の利いた小さなガジェットを作ります。その制作を通して、Silverlightでよく使われるパーツやテクニックを実際に体験していただこうというのがこの記事の目的です。したがって、ここではSilverlightを深く掘り下げたり、サイドバーガジェットを詳細に説明したりすることはしません。それでは、準備として何を揃えておく必要があるのでしょうか。嬉しいことに必要なものはわずかで、次の3つだけです。
実際、ガジェットが関係してくるのは最後の段階だけで、そのわずかな作業を除けばVistaさえ不要です。作業のほとんどはファイルを編集することとブラウザの再読み込みだけですから、XPがあれば十分なのです。
ギターチューナー
私はギターが大好きです。そこで、ギターチューナーを作ることにしました。この記事を読む人の中にギターファンが大勢いるとは思えませんが、ギターチューナーはとても簡単でVistaガジェットにはうってつけです。
ギターチューナーをご存じない方のために、ここで簡単に説明しておきましょう。ご存じの方は、ここを飛ばして次の節に進んでください。さて、ギターは弦楽器です。通常、6本の弦があり、それぞれ演奏しやすいように特定の音程に調整されます。ですから、それぞれの弦を弾くとその弦に固有の音が鳴ります。6本の弦は、普通、標準の音程に調整されますが、この作業を「調弦」(調律、チューニング)と呼んでいます。調弦は基本的に弦の発する音の相互関係であり、標準やDrop Dなどいくつかの種類があります。演奏者は演奏に先だってまずギターを調弦し、自分が演奏する際の舞台を整えます。調弦されていない弦からは雑音しか聞こえてきませんが、調弦された弦から紡ぎ出された音符は楽しげに響きます。ギターチューナーはこの調弦に使う道具です。6本の弦のそれぞれが鳴らすべき音を出し、演奏者は弦を弾きながらその音に合わせて弦を調整します。すべての弦がチューナーの音に合っていることを「ギターは調弦されている」と言います。今では、ほとんどの演奏者はノートパソコンを使ってポピュラーソングをオンラインで調べ、好みの歌を聴き、自分の曲を録音していますから、パソコン上にギターチューナーがあれば便利です。というわけで、このガジェットを作ろうと思いついたのです。
完成図
下の図のようなガジェットを作るのが目標です。
どうです、ギターに見えませんか? ギターのように6本の弦があり、中央にはサウンドホールがあります(これがあるとギターっぽく見えますね)。必要な作業は次のとおりです。
- ギターのボディーと弦を描きます。これには、XAMLのShape要素や描画機能などを使います。
- 弦がクリックされたらと、その弦に対応する音を鳴らします。また、鳴っている弦がわかるように要素を点滅させます。これには、XAMLのイベント処理、アニメーション、メディア(サウンド)などを使います。
- クリックされた弦の音は継続して鳴らす必要があります。ギターのヘッドにあるペグ(糸巻き)を回してチューナーの音と一致させるには時間がかかるからです。これには、メディア要素とそのプロパティを使います。
- 赤いボタンがクリックされたら鳴り止むようにします(調弦が終わったら普通は音を止めたいはずなので)。
以降では、実際にXAMLのいろいろな機能と若干のJavaScriptを使い、それらを組み合わせてギターチューナーを作っていきます。
完成したVistaガジェットは、格納状態ではこんな風になり、
サイドバーから出すとこんな風になるはずです。
ギターチューナーの外観を作る(その1)
基本的な枠組みを準備する
それでは始めましょう。Silverlightプラグインはインストールしてありますか。もしまだなら、ここからダウンロードしてインストールします。もう1つ、SDKはいかがですか。まだなら、ここからダウンロードしてインストールします。
1
「GuitarTuner.gadget」という名前のフォルダを作ります。
2
このフォルダに「Silverlight.js」というファイルをコピーします。このファイルは、[ドライブ名]:¥Program Files¥Microsoft Silverlight 1.0 SDK¥Resourcesにあります。
3
新たにテキストファイルを作り、その拡張子を.HTMLに変更して、「GuitarTuner.HTML」という名前にします。そして、次のテキストを貼り付けます。
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN"
"http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
<html xmlns="http://www.w3.org/1999/xhtml" xml:lang="en">
<head>
<title>Guitar Tuner</title>
<script type="text/javascript" src="Silverlight.js"></script>
</head>
<body>
</body>
</html>
4
これは起動用のHTMLで、ここにSilverlightコントロールを記述します。まず、divタグを追加し、そこにSilverlightコントロールを作るJavaScriptコードを記述します。これでbody部分は次のようになるはずです。
<body>
<div id="SilverLightControlHost">
<script type="text/javascript">
var parentElement =
document.getElementById("SilverLightControlHost");
createGuitarTunerControl();
</script>
</div>
</body>
5
次に、このコードに使われているcreateGuitarTunerControl関数を用意する必要があります。そこで、もう1つテキストファイルを作り、拡張子を.jsに変更して、「GuitarTuner.js」という名前にします。
6
このファイルに次のコードを貼り付けます。
function createGuitarTunerControl()
{
Silverlight.createObject(
"guitarTuner.xaml", // Source property value.
parentElement, // DOM reference to hosting DIV tag.
"guitarTunerControl", // Unique plugin ID value.
{
// Per-instance properties.
width:'400', // Width of rectangular region of
// plugin area in pixels.
height:'290', // Height of rectangular region of
// plugin area in pixels.
// Determines whether to display in-place
// install prompt if invalid version detected.
inplaceInstallPrompt:false,
background:'Orange', // Background color of plugin.
// Determines whether to display plugin in Windowless
// mode.
isWindowless:'true',
framerate:'24', // MaxFrameRate property value.
version:'1.0' // Silverlight version to use.
},
{
onError:null, // OnError property value --
// event handler function name.
onLoad:null // OnLoad property value --
// event handler function name.
},
null); // Context value -- event handler function name.
}
このコードは、Silverlightコントロールオブジェクトを実装しているXAMLファイル(guitarTuner.xaml)へのパスを渡し、実際にインスタンス化します。プラグインID(guitarTunerControl)は、getElementbyIdなどのDOMメソッドからこのチューナーコントロールにアクセスする際に必要になります。さらにこのコードでは、コントロールの大きさを400ピクセル×290ピクセルに設定しています。
7
HTMLファイルのhead部分に、このスクリプトファイルを使う旨の宣言を追加します。
<head>
<title>Guitar Tuner</title>
<script type="text/javascript" src="Silverlight.js"></script>
<script type="text/javascript" src="guitartuner.js"></script>
</head>
8
まだ作っていないのはXAMLファイルだけです。そこで、もう1つテキストファイルを作り、拡張子を.xamlに変更して、「guitarTuner.xaml」という名前にします。
9
次のコードを貼り付けます。
<Canvas
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/
presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
Height="400" Width="290" >
</Canvas>
10
これで、枠組みはできあがりです。それでは、「guitarTuner.HTML」をダブルクリックしてください。このHTMLがWebブラウザ上に読み込まれ、問題がなければ画面には次のようなオレンジ色の矩形が表示されるはずです。
ギターチューナーの外観を作る(その2)
基本的な枠組みが用意できましたので、最初に紹介したギターチューナーを構成するパーツを作り込んでいきましょう。各パーツは次のようになっています。
- ギターの中央にあるサウンドホールを模した円オブジェクト(図の1)。
- 6本の弦(図の2)。
- ブリッジを表す矩形(図の3)。弦はサウンドホールの上を通過し、ここまで伸びている。
- 鳴っている音を止めるための丸いボタン(図の4)。本物のギターにはないが、このガジェットでは必要。
いずれもXAMLの要素を使い、円オブジェクトと停止ボタンはEllipseで、弦とブリッジはRectangleで表現することができます。EllipseもRectangleもShape要素に属し、塗りつぶし(図形の内部を塗りつぶすこと)や大きさなどのプロパティを持っています。
XAMLの要素は、すべてCanvas(従来のWin32で言えばDC)に置きます。キャンバスというのは子要素を置く場です。キャンバスにはコントロール要素(ボタンなど)、Shape要素(RectangleやEllipseなど)、メディア要素などのほか、別のキャンバスを置くこともできます。
こうした要素が集まって、下図のような階層的ツリー構造を作ります。
ですから、ここでの目標はギターチューナーのツリー構造を作ることだとも言えます。つまり、これからXAMLで木を作り、各要素の位置を指定していくわけです。ここで、XAMLでは木の一番上にあるものから下に向かって描画される点に注意してください。したがって、木の下にある図形は上にある図形の上に重ねて描画されます(これはデフォルトで、Zオーダーを明示的に変更することもできます)。
ギターチューナーの場合で言えば、XAMLファイルでは円オブジェクト(図の1)を最初に記述するということになります。重なっていない要素については、どのような順序でもかまいません。
各要素は、次の設計図に従って配置します。
1
XAMLファイルを開き、Ellipse要素(設計図の黒い円)を表す下のコードを追加します。このコードは、Ellipse Shape要素をキャンバスの左上の角を基点(0, 0)として(30, 25)の位置に配置します。幅も高さも100ピクセル、つまり半径50ピクセルで、黒で塗りつぶします。Hollowという名前が付けてあるのは、あとでこの要素を指定する必要があるからです。それでは、XAMLファイルを保存し、ブラウザのHTMLファイルを再度読み込んでください。
<Canvas xmlns="http://schemas.microsoft.com/winfx/2006/xaml/
presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
Height="400" Width="290" >
<Ellipse x:Name="Hollow" Height="100" Width="100"
Canvas.Left="30" Canvas.Top="25" Fill="Black"/>
</Canvas>
2
Zオーダーから言って、次はブリッジ(設計図の赤い矩形)です。今度は、Rectangle Shape要素を使います。親キャンバスを基準として位置と大きさを指定し、Bridgeという名前を付けます。
<Canvas xmlns="http://schemas.microsoft.com/winfx/2006/xaml/
presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
Height="400" Width="290" >
<Ellipse Height="100" Width="100" Canvas.Left="30"
Canvas.Top="25" Fill="Black"/>
<Rectangle x:Name="Bridge" Width="10" Height="90"
Canvas.Left="10" Canvas.Top="30" >
</Rectangle>
</Canvas>
本物のギターではブリッジはかまぼこ形に作られているので、その雰囲気も再現したいですよね。実は、Fill属性を使うと簡単にそれらしく作ることができます。サウンドホールには色を一様に塗ります。穴を表現するにはこの方法が適しているからです。しかし、ブリッジのように膨らんだ感じを出すには、グラデーションの塗りを使用します。左端から中央に向けて黒から徐々に灰色になり、そこから右端に向けて再び黒くするのです。コードは次のようになります。
<Canvas xmlns="http://schemas.microsoft.com/winfx/2006/xaml/
presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
Height="400" Width="290" >
<Ellipse Height="100" Width="100" Canvas.Left="30"
Canvas.Top="25" Fill="Black"/>
<Rectangle x:Name="Bridge" Width="10" Height="90"
Canvas.Left="10" Canvas.Top="30" >
<Rectangle.Fill>
<LinearGradientBrush StartPoint="0,0" EndPoint="1,0">
<GradientStop Color="Black" Offset="0.0"/>
<GradientStop Color="Gray" Offset="0.5"/>
<GradientStop Color="Black" Offset="1.0"/>
</LinearGradientBrush>
</Rectangle.Fill>
</Rectangle>
</Canvas>
コードを詳しく見てみましょう。StartPointとEndPointはグラデーションの方向を示します。ここでは水平方向のグラデーションですから、Y方向は位置0のままとし、X方向は位置0から位置1(要素の反対の端)までとします。GradientStopは色の変化の仕方を指定します。グラデーションする範囲の中で、位置0ではBlack、位置0.5でGray、位置1では再びBlackへと変化させることを指定しています。それでは、XAMLファイルを保存し、ブラウザのHTMLファイルを再度読み込んでください。
3
Zオーダーから言って、次は弦(設計図の青い矩形)です。弦は6本ありますが、ここでは1本だけ取り上げて説明します(弦の位置を除き、設定の仕方はどの弦でも同じです)。ギターの弦は、上から下にそれぞれ、低いE、A、D、G、B、高いEの弦と呼びます。本物のギターでは、一番上の弦(一番低い音を出します)は太く、下に行くほど細くなります。XAMLでも、これを再現することにしましょう。そのためには矩形の高さを変えます。
弦は丸いので、その雰囲気も再現したいですよね。この場合も矩形とグラデーションを使えば再現できます。ただし、弦に似た色で縦方向にグラデーションします(ブリッジとはStartPointとEndPointが異なります)。
<Canvas xmlns="http://schemas.microsoft.com/winfx/2006/xaml/
presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
Height="400" Width="290" >
<Ellipse Height="100" Width="100" Canvas.Left="30"
Canvas.Top="25" Fill="Black"/>
<Rectangle x:Name="Bridge" Width="10" Height="90"
Canvas.Left="10" Canvas.Top="30" >
<Rectangle.Fill>
<LinearGradientBrush StartPoint="0,0" EndPoint="1,0">
<GradientStop Color="Black" Offset="0.0"/>
<GradientStop Color="Gray" Offset="0.5"/>
<GradientStop Color="Black" Offset="1.0"/>
</LinearGradientBrush>
</Rectangle.Fill>
</Rectangle>
<Rectangle x:Name="LowEString" Width="315" Height="5"
Canvas.Left="15" Canvas.Top="37">
<Rectangle.Fill>
<LinearGradientBrush StartPoint="0,0" EndPoint="0,1">
<GradientStop Color="Brown" Offset="0.0"/>
<GradientStop Color="White" Offset="0.5"/>
<GradientStop Color="Brown" Offset="1.0"/>
</LinearGradientBrush>
</Rectangle.Fill>
</Rectangle>
</Canvas>
XAMLファイルを保存し、ブラウザのHTMLファイルを再度読み込んでください。
5
最後は、停止ボタン(設計図の緑の円)です。サウンドホールのときと同じEllipse Shape要素を使います。立体的に見えるように、放射状のグラデーションを使いましょう。コードは次のようになります。
<Canvas xmlns="http://schemas.microsoft.com/winfx/2006/xaml/
presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
Height="400" Width="290" >
<Ellipse Height="100" Width="100" Canvas.Left="30"
Canvas.Top="25" Fill="Black"/>
<Rectangle x:Name="Bridge" Width="10" Height="90"
Canvas.Left="10" Canvas.Top="30" >
<Rectangle.Fill>
<LinearGradientBrush StartPoint="0,0" EndPoint="1,0">
<GradientStop Color="Black" Offset="0.0"/>
<GradientStop Color="Gray" Offset="0.5"/>
<GradientStop Color="Black" Offset="1.0"/>
</LinearGradientBrush>
</Rectangle.Fill>
</Rectangle>
<Rectangle x:Name="LowEString" Width="315" Height="5"
Canvas.Left="15" Canvas.Top="37">
<Rectangle.Fill>
<LinearGradientBrush StartPoint="0,0" EndPoint="0,1">
<GradientStop Color="Brown" Offset="0.0"/>
<GradientStop Color="White" Offset="0.5"/>
<GradientStop Color="Brown" Offset="1.0"/>
</LinearGradientBrush>
</Rectangle.Fill>
</Rectangle>
<Ellipse x:Name="StopButtonb" Height="15" Width="15"
Canvas.Left="8" Canvas.Top="125" Stroke="Black"
StrokeThickness="1" >
<Ellipse.Fill>
<RadialGradientBrush>
<GradientStop Color="Red" Offset="1.0"/>
<GradientStop Color="White" Offset="0.0"/>
</RadialGradientBrush>
</Ellipse.Fill>
</Ellipse>
</Canvas>
XAMLファイルを保存し、ブラウザのHTMLファイルを再度読み込んでください。
6
残り5本の弦も入れて完成させます。コードは次のようになっているはずです。
<Canvas xmlns="http://schemas.microsoft.com/winfx/2006/xaml/
presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
Height="400" Width="290" ><Ellipse Height="100"
Width="100" Canvas.Left="30" Canvas.Top="25" Fill="Black"/>
<Rectangle x:Name="Bridge" Width="10" Height="90"
Canvas.Left="10" Canvas.Top="30" >
<Rectangle.Fill>
<LinearGradientBrush StartPoint="0,0" EndPoint="1,0">
<GradientStop Color="Black" Offset="0.0"/>
<GradientStop Color="Gray" Offset="0.5"/>
<GradientStop Color="Black" Offset="1.0"/>
</LinearGradientBrush>
</Rectangle.Fill>
</Rectangle>
<Rectangle x:Name="LowEString" Width="315" Height="5"
Canvas.Left="15" Canvas.Top="37">
<Rectangle.Fill>
<LinearGradientBrush StartPoint="0,0" EndPoint="0,1">
<GradientStop Color="Brown" Offset="0.0"/>
<GradientStop Color="White" Offset="0.5"/>
<GradientStop Color="Brown" Offset="1.0"/>
</LinearGradientBrush>
</Rectangle.Fill>
</Rectangle>
<Rectangle x:Name="AString" Width="315" Height="5"
Canvas.Left="15" Canvas.Top="52">
<Rectangle.Fill>
<LinearGradientBrush StartPoint="0,0" EndPoint="0,1">
<GradientStop Color="Brown" Offset="0.0"/>
<GradientStop Color="White" Offset="0.5"/>
<GradientStop Color="Brown" Offset="1.0"/>
</LinearGradientBrush>
</Rectangle.Fill>
</Rectangle>
<Rectangle x:Name="DString" Width="315" Height="4"
Canvas.Left="15" Canvas.Top="67">
<Rectangle.Fill>
<LinearGradientBrush StartPoint="0,0" EndPoint="0,1">
<GradientStop Color="Brown" Offset="0.0"/>
<GradientStop Color="White" Offset="0.5"/>
<GradientStop Color="Brown" Offset="1.0"/>
</LinearGradientBrush>
</Rectangle.Fill>
</Rectangle>
<Rectangle x:Name="GString" Width="315" Height="4"
Canvas.Left="15" Canvas.Top="82">
<Rectangle.Fill>
<LinearGradientBrush StartPoint="0,0" EndPoint="0,1">
<GradientStop Color="Brown" Offset="0.0"/>
<GradientStop Color="White" Offset="0.5"/>
<GradientStop Color="Brown" Offset="1.0"/>
</LinearGradientBrush>
</Rectangle.Fill>
</Rectangle>
<Rectangle x:Name="BString" Width="315" Height="3"
Canvas.Left="15" Canvas.Top="97">
<Rectangle.Fill>
<LinearGradientBrush StartPoint="0,0" EndPoint="0,1">
<GradientStop Color="Brown" Offset="0.0"/>
<GradientStop Color="White" Offset="0.5"/>
<GradientStop Color="Brown" Offset="1.0"/>
</LinearGradientBrush>
</Rectangle.Fill>
</Rectangle>
<Rectangle x:Name="HighEString" Width="315" Height="3"
Canvas.Left="15" Canvas.Top="112">
<Rectangle.Fill>
<LinearGradientBrush StartPoint="0,0" EndPoint="0,1">
<GradientStop Color="Brown" Offset="0.0"/>
<GradientStop Color="White" Offset="0.5"/>
<GradientStop Color="Brown" Offset="1.0"/>
</LinearGradientBrush>
</Rectangle.Fill>
</Rectangle>
<Ellipse x:Name="StopButtonb" Height="15" Width="15"
Canvas.Left="8" Canvas.Top="125" Stroke="Black"
StrokeThickness="1" >
<Ellipse.Fill>
<RadialGradientBrush>
<GradientStop Color="Red" Offset="1.0"/>
<GradientStop Color="White" Offset="0.0"/>
</RadialGradientBrush>
</Ellipse.Fill>
</Ellipse>
</Canvas>
XAMLファイルを保存し、ブラウザのHTMLファイルを再度読み込んでください。下の図のようになりましたか?
ギターチューナーに動作を加える
ギターチューナーの外観ができたので、次に動作を加えましょう。ここでの目標は次の3つです。
- 弦をクリックできるようにし、クリックされた場合、音を出していることを示すインジケータを表示します。
- 弦が音を出しているときに赤い停止ボタンを押すと、前項の表示を消します。
- 1項の表示があるときは該当する音を出し続け、表示が消えると音も消えるようにします。
第1の目標
まず、第1の目標です。始めに、弦と停止ボタンの上にマウスを置いたとき、それらがクリックできることがわかるようにしなければなりません。このような場合、手の形のカーソルを使うのが普通です。そこで、XAMLファイルを再度開き、すべての弦と停止ボタンにCursor属性を追加します。コードは次のようになります。
<Rectangle x:Name="LowEString" Width="315" Height="5"
Canvas.Left="15" Canvas.Top="37" Cursor="Hand">
次に、弦がクリックされたときに所定の動作をするようマウスボタンハンドラを追加します。次のように、イベント名とハンドラ関数を設定します。このハンドラ関数はすべての弦で共通して使います。残り5本についても同じように追加します。
<Rectangle x:Name="LowEString" Width="315" Height="5"
Canvas.Left="15" Canvas.Top="37" Cursor="Hand"
MouseLeftButtonDown="stringPlucked">
メモ帳で「guitartuner.js」ファイルを開き、上のコードで指定したstringPlucked関数を追加します。
function stringPlucked(sender, args)
{
}
この関数の中に必要な動作を書き込んでいきます。ここでは、弦が弾かれたら、その弦とブリッジが交差する点に緑のインジケータを点滅させます。まず、次のように、[弦の名前]Indicatorという名の要素を追加します。
<Rectangle x:Name="LowEString" Width="315" Height="5"
Cursor="Hand" Canvas.Left="15" Canvas.Top="37"
MouseLeftButtonDown="stringPlucked">
<Rectangle.Fill>
<LinearGradientBrush StartPoint="0,0" EndPoint="0,1">
<GradientStop Color="Brown" Offset="0.0"/>
<GradientStop Color="White" Offset="0.5"/>
<GradientStop Color="Brown" Offset="1.0"/>
</LinearGradientBrush>
</Rectangle.Fill>
</Rectangle>
<Rectangle x:Name="LowEStringIndicator" Height="15" Width="10"
Canvas.Left="10" Canvas.Top="30" Fill="#00FF00"
Opacity="0.0" >
</Rectangle>
少し説明しておきましょう。Opacity属性はその要素の不透明度を表す属性です。これを0にすると要素全体が透明になり、背景が透けて見えます。デフォルトの不透明度は1.0で、要素ははっきりと完全に見えます。対応する弦が弾かれるときを除いて、このインジケータは透明にしておきます。したがって、ブラウザで再度読み込んでもこの要素は見えません。不透明度が0.0だからです。
次に、インジケータを点滅させます。そのため、stringPlucked関数が呼ばれたとき、該当するインジケータの不透明度を0から1に徐々に変化させます。これでどの弦が鳴っているかがわかり、本物のギターで調弦すべき弦がわかります。タイマーを使って変化させることもできるでしょうが、まずはXAMLの活用を検討しましょう。実際、XAMLのストーリーボードと呼ばれている方法を利用して実現することができます。ストーリーボードはストーリーを記述する方法の1つと考えればよいでしょう。ストーリーは時間軸に沿って順次何かが起こり進行していきます。ギターチューナーの場合で言えば、数秒間にわたってインジケータの不透明度が一定の割合で変化し、終わると再び始まるストーリーになります。したがって、弦が弾かれたら、対応するストーリーボードを取り出して開始すれば点滅を実現できます。これらの手順をXAMLで書くと、次のコードになります。
<Rectangle x:Name="LowEStringIndicator" Height="15" Width="10"
Canvas.Left="10" Canvas.Top="30" Fill="#00FF00"
Opacity="0.0" >
<Rectangle.Resources>
<Storyboard x:Name="LowEStringIndicatoranimation">
<DoubleAnimation
Storyboard.TargetName="LowEStringIndicator"
Storyboard.TargetProperty="Opacity"
From="0.0" To="1.0" Duration="0:0:0.3" AutoReverse="True"
RepeatBehavior="Forever"
/>
</Storyboard>
</Rectangle.Resources>
</Rectangle>
少し説明しておきましょう。まずResourcesセクションの使い方ですが、これは、ストーリーボードを要素に追加するためのXAML流の方法です。名前はLowEStringIndicatorAnimationとしてあります。このような名前にしておくと該当するストーリーボードの指定が簡単になります。重要なのはTargetNameとTargetPropertyです。TargetNameはそのストーリーの操作対象の要素を表し、TargetPropertyはその要素のどのプロパティを操作するかを表します。この例では、LowEStringIndicatorのOpacityプロパティをアニメーションで操作することになります。次に重要なのはFromとToです。アニメーションのタイプがDoubleAnimationなのは、Opacityがdouble型だからです。これはdouble型のプロパティを変更またはアニメートする際に使われます。値は0.0(完全透過)から1.0(完全な表示)に0.3秒で変化させます。Autoreverse=Trueとしてあるので、不透明度が0.0から1.0になると、今度は1.0から0.0に戻ります。RepeatBehaviorはForeverなので、これが指定されていないと再び0.0から始まることになります。
あとは、このストーリーボードのアニメーションが起動されるようにすれば終わりです。そこで、次のコードを「guitartuner.js」ファイルのstringPlucked関数に組み込みます。
function stringPlucked(sender, args)
{
var storyboardObj =
sender.findName(sender.Name + "Indicatoranimation");
storyboardObj.begin();
}
XAMLファイルとJSファイルを保存し、ブラウザのHTMLファイルを再度読み込ませます。ここで、一番上の弦をクリックすると緑のインジケータが点滅するはずです。
ここで、この小さなJavaScriptコードについて若干補足しておきましょう。まず、とても便利なfindName関数が使われています。その引数にあるsenderは、このイベントを生成したオブジェクトを表します。ギターチューナーの場合、Plucked関数はLowEString、AString、GString、DString、BString、HighEStringstring(この6つの要素にこのハンドラを追加しました)によって生成されるので、sender.Nameはイベントを発生させた弦のx:Name属性を表すことになります。一方、命名規則によれば、アニメーションの名前は「該当する弦の名前+Indicatoranimation」です。したがって、sender.Name+"Indicatoranimation"で、実行すべきストーリーボードの名前が得られることになります。そのストーリーボードをfindNameを使って取り出しているわけです。ところで、findNameは非常に興味深い関数で、[オブジェクト].findNameという形で呼び出されることから、渡された名前の要素をツリー構造の下にあるオブジェクトから探すように見えますが、実際には木全体からその名前を探します。したがって、イベントを生成した弦で、その下にストーリーボードを持たなくても、findName関数を使って取得できるのです。ストーリーボードオブジェクトを取得したら、あとはbegin()を使ってアニメーションを開始させるだけです。
これで第1の目標は完成です。
第2の目標
次は第2の目標です。弦が鳴っているときに赤い停止ボタンが押されたら、先ほど実装したインジケータを消さければなりません。基本的に同じ方法で実装することができます。それでは、MouseLeftButtonDownハンドラを追加し、StopClickedなどといった名前を付けます。
<Ellipse x:Name="StopButtonb" Height="15" Width="15"
Canvas.Left="8" Canvas.Top="125" Stroke="Black"
StrokeThickness="1" Cursor="Hand"
MouseLeftButtonDown="StopClicked">
次に、上のコードで指定した関数をJavaScriptに追加します。この関数はどのストーリーボードが動いているのかを知る必要があります。そこで、ストーリーボードを起動したときその情報を保存しておくようにしましょう。次に示すように、storyBoardObjをグローバル変数にします。そして、stopClickedハンドラでこの変数に対してend()を実行します。
var storyboardObj = null;
function stringPlucked(sender, args)
{
storyboardObj = sender.findName(sender.Name + "Indicatoranimation");
storyboardObj.begin();
}
function StopClicked(sender, args)
{
if(storyboardObj != null)
{
//stop any animation first
storyboardObj.stop();
}
}
XAMLとJSファイルを保存し、ブラウザのHTMLファイルを再度読み込んでください。そして、一番上の弦をクリックします。緑のインジケータが点滅します。次に、赤い停止ボタンをクリックすると、アニメーションが止まるはずです。他の5本の弦についても、同じようにコードを追加します。インジケータとストーリーボード要素の名前やそのインジケータ要素に対するCanvas.Topの値(すぐ上の弦に対して15ピクセルずつ増やします)を間違えないようにしましょう。
これで第2の目標は完成です。
第3の目標
次は第3の目標です。インジケータが表示されたら該当する弦の音を鳴らし、インジケータが消えたら音を止めなければなりません。
これには、それぞれの弦に対応する音程のサウンドファイルが必要です。この記事の冒頭で示したサンプルファイル(media.zipファイル)をダウンロードして展開します。その中にWMAファイルがあるので、それを他のファイルと同じ場所に移します。ファイルの名前は、XAMLファイル内の弦要素の名前に拡張子.wmaを付けたものになっているはずです。
XAMLはメディア要素を扱うこともできます。そこで、XAMLファイルを開き、次の要素を追加します。
<Canvas xmlns="http://schemas.microsoft.com/winfx/2006/xaml/
presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
Height="400" Width="290" >
<MediaElement x:Name="sound" Width="0" Height="0"/>
<Ellipse Height="100" Width="100" Canvas.Left="30"
Canvas.Top="25" Fill="Black"/>
名前が付けてあるのは、JavaScriptから指定できるようにするためです。このメディアは見せる必要がありませんから、高さと幅は0にします(必要なのは音だけです)。弦が弾かれるまではどの弦の音を出すかわからないので、ここではメディアファイルを指定しません。第3の目標は、アニメーションと同時にメディアを再生し、アニメーションの停止と同時にメディアの再生を止めることです。そこで、JavaScriptコードを次のように変更します。
function stringPlucked(sender, args)
{
storyboardObj = sender.findName(sender.Name + "Indicatoranimation");
storyboardObj.begin();
sender.findName("sound").Source = sender.Name + ".wma";
sender.findName("sound").Play();
}
function StopClicked(sender, args)
{
if(storyboardObj != null)
{
//stop any animation first
storyboardObj.stop();
sender.findName("sound").Stop();
}
}
このコードは、サウンド要素を探し出し、そのPlayメソッドまたはStopメソッドを実行します。再生すべきサウンドファイルの名前は、命名規則に従って生成します(それをSourceプロパティを使って設定します)。実は、もう1つやり残したことがあります。それは、弦が鳴っている最中に他の弦が弾かれたときの処理です。2つの音が鳴らないように、そのとき鳴っていたアニメーション(インジケータとメディア)を止めなければなりません。そこで、JavaScriptのコードを次のように変更します。
function stringPlucked(sender, args)
{
StopClicked(sender,args);
storyboardObj = sender.findName(sender.Name + "Indicatoranimation");
storyboardObj.begin();
sender.findName("sound").Source = sender.Name + ".wma";
sender.findName("sound").Play();
}
XAMLファイルとJSファイルを保存し、ブラウザのHTMLファイルを再度読み込んでください。そして、一番上の弦をクリックすると、緑のインジケータが点滅し、スピーカーかヘッドフォンに音が聞こえるはずです。赤い停止ボタンをクリックするとアニメーションとサウンドは止まり、他の弦をクリックするとその弦の音が再生されるはずです。
うまく動いていますね。しかし、これでは実用上不便なところがあります。調弦は、普通、音を鳴らしながら行うからです。つまり、音がすぐに止まってしまうのでは使いにくいのです。長いサウンドファイルを使えば音は長くなりますが、これはメディアの無駄というのものです。メディアを反復して再生すればいいのですから。反復再生は、Storyboardアニメーションをちょっと工夫して実現することもできますが、ここでは別の方法を使ってみましょう。MediaElementはMediaEndedというイベントを発生させますから、このイベントを捕捉し、そのハンドラの中でメディアの頭からもう一度再生させるのです。次のように変更します。
XAMLファイル
<MediaElement x:Name="sound" Width="0" Height="0"
MediaEnded="endofmedia"/>
JavaScriptファイル
function endofmedia(sender,args)
{
//reached end, seek to beginning and play again
var mediaElement = sender.findName("sound");
var position = mediaElement.position;
position.seconds = 0;
mediaElement.position = position;
mediaElement.Play();
}
XAMLファイルとJSファイルを保存し、ブラウザのHTMLファイルを再度読み込んでください。そして弦をクリックすると、緑のインジケータが点滅し、スピーカーかヘッドフォンに音が鳴り続け、停止ボタンをクリックすると止まるはずです。
以上で、ブラウザ上で動く簡単なギターチューナーが完成しました。
Vistaガジェットへの変換
最後にVistaガジェットに変換するのですが、この作業は今までで最も簡単です。同じフォルダに「Gadget.xml」という名のファイルを作ってメモ帳で開き、次のコードを貼り付けます。
<?xml version="1.0" encoding="utf-8" ?>
<gadget>
<name>Guitar Tuner</name>
<namespace>GuitarTuner</namespace>
<version>1.0</version>
<author name="[Your Name here]">
</author>
<copyright>2007</copyright>
<description>Guitar Tuner for tuning by ear</description>
<icons>
</icons>
<hosts>
<host name="sidebar">
<base type="HTML" apiVersion="1.0.0"
src="GuitarTuner.html" />
<permissions>full</permissions>
<platform minPlatformVersion="0.3" />
</host>
</hosts>
</gadget>
「名前を付けて保存」で保存し、エンコーディング方式としてUTF-8を選択します。これは重要です。こうしないと、正しいガジェットとして認識されません。ガジェットへの変換作業はこれで終わりです。ブラウザを管理しそこでガジェットを動かすというのが、ガジェットのフレームワークだからです。HTMLページで動けば、ガジェットの中でも十分動くのです。
ここで必要な作業は、ガジェット開発ガイドラインに準拠させ、外観を整え、正常に動作させることだけです。まず、「Vista gadget UI guidelines」に目を通します。
ガイドラインの中で、次の4点については既に準拠しているはずです。
- 機能はいつでも使えるようにすること - この点については、チューナーガジェットは問題ないでしょう。インストールすれば使えますから。
- 主機能以外にも気を配ること - この点については、あとで考えます。しかし、不要なときに利用者を悩ませることはありませんから(調弦を終えたあとにメディアとアニメーションを止めることができます)、方向性としては間違っていません。
- 機能は1つに絞り明確に規定すること - この点についても、問題はありません。目的は調弦の道具であり、その通りの機能を持ち、余計な飾りはありません。
- 目的に相応しい外観にすること - この点についても、問題はありません。ギターのボディーと弦の形をしていますから。
したがって、問題はガジェットを格納したときの大きさを小さくすることだけです。そこで、次のように、GuitarTuner.HTMLでボディー要素の大きさを指定します。
<head>
<title>Guitar Tuner</title>
<style>
body
{
width: 130px;
height: 130px;
margin: 0px;
font-size:11px;
font:10px segoe ui,tahoma;
}
</style>
<script type="text/javascript" src="Silverlight.js"></script>
<script type="text/javascript" src="guitartuner.js"></script>
</head>
これで、ガジェットのガイドラインに準拠できました。
Vistaでガジェットを使っている方ならご存じでしょうが、ガジェットはサイドバーに格納した状態でもサイドバーから出した状態でも使うことができます。Microsoftのガイドラインでは、格納したガジェットの大きさとして130ピクセル×130ピクセル、出したときの大きさは少し制限が緩くなり400ピクセル×400ピクセルが推奨されています。
130ピクセル×130ピクセルというのはかなり窮屈です。この大きさでは6本の弦を入れるのが精一杯です。しかし、400ピクセル×400ピクセルの矩形では、もう少し工夫ができます。ガジェットの開発者に推奨されていることの1つに見栄えのよさがあります。そこで、サイドバーから出ているときはギターのボディーを曲線的な形にしてみましょう。
それでは、サイドバーに格納されているのかサイドバーの外にあるのかをどのようにすれば知ることができるでしょうか。実は、ハンドラを登録するだけでこれらのイベントを捕捉できる機能が、VistaガジェットAPIに用意されています。イベントの通知を登録し、ハンドラの中で必要な処理を行います。つまり、ボディーがロードされたときにこれらのハンドラを登録し、そのハンドラの中で格納状態に応じてボディーの大きさを130から400に変更するのです。そのコードは下に示したようになりますが、説明は不要でしょう。
「GuitarTuner.HTML」を、次のように変更します。
<script type="text/javascript" src="guitartuner.js"></script>
</head>
<script type="text/javascript">
function loadMain()
{
//register for the events and pass in our handler
System.Gadget.onDock = dockStateChanged;
System.Gadget.onUndock = dockStateChanged;
}
function dockStateChanged()
{
//change size depending on state
if(System.Gadget.docked)
{
document.body.style.width = "130px";
document.body.style.height = "130px";
}
else
{
document.body.style.width = "400px";
document.body.style.height = "290px";
}
}
</script>
<body onload="loadMain()">
<div id="SilverLightControlHost">
これで、ガジェットをサイドバーから出すと大きくなります。次に、このガジェット用にギターのボディーを作る必要があります。ここでは、背景画像を利用することにしましょう。サンプルの「media.zip」ファイルには、背景画像「BKGND.png」が含まれています。そこで、HTMLのスタイルを次のように変更します。
<style>
body
{
width: 130px;
height: 130px;
margin: 0px;
font-size:11px;
font:10px segoe ui,tahoma;
background-image:url(bkgnd.png);background-repeat:no-repeat;
}
</style>
そして、「guitarTuner.js」ファイルを開き、次のように背景色を白に変更します。
inplaceInstallPrompt:false, // Determines whether to display
// in-place install prompt if
// invalid version detected.
background:'#00000000', // Background color of plugin.
isWindowless:'true', // Determines whether to display plugin
この時点でHTMLを起動すると、次のようになるはずです。
位置がずれてしまいました。背景画像を少し左上にずらす必要があります。そこで、ボディーのスタイルを次のように調整します。
background-image:url(bkgnd.png);
background-position: -80px -70px;
background-repeat:none;
このように変更すると別の問題が発生します。格納状態では確かに背景画像をこのように移動する必要があり、こうすれば弦などの要素はボディーに対して正しい位置になります。
一方、サイドバーから出した状態では、逆に、背景画像の左上をボディーの左上に合わせなければなりません。つまり、背景画像の位置を(0, 0)にする必要があるのです。しかし、背景画像は移動させたのですから、この場合は弦などの要素の方を移動しなければなりません。すべての要素を移動するのは大変なことです。簡単な方法はないのでしょうか。もちろんあります。キャンバスについて説明したことを思い出してください。キャンバスを別のキャンバスに含めることができるとお話ししましたね。これを利用するのです。すべての要素を1つのキャンバスに入れ、そのキャンバスをルートキャンバスの中に入れれば、子キャンバスを動かすだけでそこに含まれているすべての要素が自動的に動きます。
次に示すコードは、この方法を組み込んだものです。
<Canvas xmlns="http://schemas.microsoft.com/winfx/2006/xaml/
presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
Height="400" Width="290" >
<Canvas x:Name="MainDrawing" Canvas.Left="0" Canvas.Top="0"
Height="130" Width="130" >
<MediaElement x:Name="sound" Width="0" Height="0"
MediaEnded="endofmedia"/>
.....
</Canvas>
</Canvas>
次に、dockstatechangedハンドラの中で、これらの要素を動かします。
GuitarTuner.HTML
function dockStateChanged()
{
if(System.Gadget.docked)
{
document.body.style.width = "130px";
document.body.style.height = "130px";
document.body.style.backgroundPosition = "-80px -70px";
document.getElementById("guitarTunerControl").content.
findName("MainDrawing")["Canvas.Left"]="0";
document.getElementById("guitarTunerControl").content.
findName("MainDrawing")["Canvas.Top"]="-10";
}
else
{
document.body.style.width = "400px";
document.body.style.height = "290px";
document.body.style.backgroundPosition = "0px 0px";
document.getElementById("guitarTunerControl").content.
findName("MainDrawing")["Canvas.Left"]="80";
document.getElementById("guitarTunerControl").content.
findName("MainDrawing")["Canvas.Top"]="70";
}
}
</script>
これで完成です。あとはこのガジェットをインストールするだけです。次の方法が最も簡単でしょう。まず、フォルダにあるファイルをすべて(media.zipファイルを除く)選択し(Ctrl+Aを押します)、右クリックして[送る]→[圧縮(zip形式)フォルダ]を選択し、GuitarTunerフォルダのコンテンツを圧縮します(コンテンツだけを圧縮します。フォルダ自体を圧縮しないでください)。そして、そのzipファイルの拡張子を変更し、たとえば「GuitarTuner.gadget」とします。次に、Vistaマシン上で「GuitarTuner.gadget」ファイルをダブルクリックします。確認メッセージに応答すると、ガジェットが表示され使えるようになるはずです。格納されているガジェットをサイドバーから出してみてください。いかがですか?
参考資料