始めよう!Silverlight

第3回メッセージカードアプリを作ってみよう

メッセージカードアプリの概要

前回はSDKを導入してSilverlight.jsを用いた場合のSilverlightの表示方法と、開発時の最小のファイル構成を確認しました。今回は、それをふまえて簡単なメッセージカードアプリを作成します。

メッセージカードアプリは以下のような機能を持っています。

  • タイトルの入力
  • メッセージの入力
  • メッセージカードの表示

また、画面イメージは以下のようになります。

画像

今回はJavaScriptを使用してプログラミングを行います。残念ながらJavaScript自体の解説は連載の主題ではないため行いませんが、さまざまなリソースがインターネット上に公開されていますので、適宜参照してください。

では、さっそく開発を始めましょう。

基本となるファイルの準備

Silverlightでの開発時の基本となるファイル構成は、以下のようになると前回ご紹介しました。

  • HTMLファイル(index.htm)
  • JavaScriptファイル(index.htm.js)
  • XAMLファイル(app.xaml)

今回も基本の構成はこの形になります。最初にそれぞれの骨組となるコードを記載します。まずはindex.htmです。

<html>
  <head>
    <title>Memo Card</title>
    <script type="text/javascript" src="Silverlight.js"></script>
    <script type="text/javascript" src="index.htm.js"></script>
    <link rel="stylesheet" href="index.css" />
  </head>
  <body onload="createSilverlight();">
    <div id="agContainer"></div>
    <!--[1]-->
    <div id="inputContainer">
      <dl>
        <dt>Title</dt>
        <dd><input type="text" id="titleTextBox" /></dd>
        <dt>Message</dt>
        <dd><textarea id="messageTextBox" cols="20" rows="5"></textarea></dd>
      </dl>
      <button>Create</button>
    </div>
  </body>
</html>

今回はHTMLファイルの中にSilverlightを表示するための領域のほかに、[1]の部分で入力用のテキストボックスとカード作成用のボタンを作成しています。本来であれば、入力自体もSilverlightで行いたいのですが、残念ながら現在のバージョンではTextBoxコントロールは提供されていないため、HTMLのテキストボックスを使用します。アナウンスされているSilverlight2.0の情報では、TextBoxコントロールが含まれるとのことなので、リリースが待ち望まれるところです。

また、レイアウトの調整用にCSSファイルも使用しています。以下の内容をindex.cssとして保存してください。

html,body{
  width : 100%; 
  height : 100%;
}
*{
  margin : 0px; 
  padding : 0px;
}
#inputContainer{
  position : absolute; 
  top : 10px; 
  left : 10px;
}
#inputContainer dt{
  float : left; 
  width : 6em;
  font : bold sans-serif;
}

続いては、Silverlightを書き出すためのJavaScriptファイルです。こちらも以下の内容をindex.htm.jsとして保存してください。

function createSilverlight(){
  Silverlight.createObjectEx({
    source : "app.xaml",
    parentElement : document.getElementById("agContainer"),
    id : "agControl",
    properties : {
      version : "1.0",
      width : "100%",
      height : "100%",
      background : "#000000",
      isWindowless: 'true', //[1]
      enableHtmlAccess: "true"
    },
    events : {
      //[2]
      onLoad : function(plugin, userContext, sender){
        var setSize = function(){
          plugin.content.root.width = document.body.offsetWidth;
          plugin.content.root.height = document.body.offsetHeight;
        }
        setSize();
        plugin.content.onResize = setSize;
      },
      onError : null
    }
  });
}

ポイントが2点あります。まず[1]の部分ですが、Silverlightの作成時にプロパティのisWindowlessの値をtrueにしています。これはSilverlightの上に入力用のHTMLを重ねて表示するためです。isWindowlessがfalseまたは何も設定されていない場合は、Silverightがウィンドウとして作成されてしまい、入力用のHTML部分を上に重ねることができなくなってしまいます。

次に[2]の部分ですが、これはSilverlightが常にウィンドウ全体に表示されるようにしているコードです。Silverlightのロードの成功時に、XAMLのルート要素をウィンドウのサイズと同じになるように指定しています。また、content、つまりSilverlightを表示しているHTMLのobjectタグのリサイズ時にも同様の処理を行うように指定しています。

では、最後にXAMLファイルです。以下の内容をapp.xamlとしてファイルに保存してください。

<Canvas
  xmlns="http://schemas.microsoft.com/client/2007"
  xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
>
  <Canvas.Background>
    <LinearGradientBrush
    EndPoint="0.275,0.285"
    StartPoint="-0.011,-0.008" 
    SpreadMethod="Reflect" 
    MappingMode="RelativeToBoundingBox">
    <GradientStop Color="#FF425F9D" Offset="0"/>
    <GradientStop Color="#FF8A96AF" Offset="1"/>
  </LinearGradientBrush>
  </Canvas.Background>
</Canvas>

Canvasの背景としてBackgroundプロパティにLinearGradientBrushを指定して、線形にグラデーションを描けています。さらにそのグラデーションを反転させながらボックスいっぱいに連続するように指定してます。

ここで、いったんindex.htmをブラウザで開いて、内容を確認してください。以下のように表示されていれば成功です。Silverlight.jsも忘れないように同階層に配置しておいてください。

画像

card.jsの作成

土台となるファイルの作成が完了しました。続いてはメッセージカードを表示する部分を作成していきます。まずは以下の内容をcard.jsとして保存してください。

if(typeof sample == "undefined") var sample = {};

(function(){
//[1]

sample.card = function(plugin){
//[2]
};

})();

[1]と[2]の部分にコードを記述していきます。少し長めのコードになりますので、これ以降はコードを記載する部分として[1]を「[function]の中⁠⁠、[2]を「[sample.card]の中」と記載します。

作成したcard.jsをindex.htmから呼び出すようにしておきます。

<script type="text/javascript" src="Silverlight.js"></script>
<script type="text/javascript" src="card.js"></script>
<script type="text/javascript" src="index.htm.js"></script>

メッセージカードのXAML

メッセージカードを表示するためには、メッセージカードのXAMLを実行時に読み込んでオブジェクトを生成し、app.xamlで記述したルート要素のCanvasに追加します。XAMLを実行時に読み込んでオブジェクトを生成するためには以下のcreateFromXaml関数を使用します。

var rectangle = plugin.content.createFromXaml("<Rectangle />");

ここでのpluginはSilverlightのプラグインオブジェクトを指しています。Silverightを表示しているHTMLタグのことで、Silveright.createObjectEx関数で指定したidを用いて、document.getElementByID関数で取得できます。また、SilverightのオブジェクトのgetHost関数でも取得できます。

では、メッセージカードのXAMLを文字列として変数に格納します。[function]の中に以下を記述してください。

var cardXaml = 
"<Canvas xmlns:x='http://schemas.microsoft.com/winfx/2006/xaml' " +
  "Width='200' Height='150'>" +
  "<Rectangle Width='200' Height='150' Fill='#FF727272' Opacity='0.3' />" +
  "<Rectangle Width='194' Height='144' Canvas.Left='3' Canvas.Top='3'>" +
    "<Rectangle.Fill>" + 
      "<LinearGradientBrush EndPoint='0.7,1.2' StartPoint='0.2,-0.2'>" + 
      "<GradientStop Color='#FFCCCCCC' Offset='0' />" + 
      "<GradientStop Color='#FFFFFFFF' Offset='1' />" + 
      "<GradientStop Color='#FFFFFFFF' Offset='0.3' />" + 
    "</LinearGradientBrush>" +
    "</Rectangle.Fill>" +
  "</Rectangle>" +
  "<Rectangle Opacity='0.5' Width='188' Height='30' Fill='#FF9F9F9F' " +
    "RadiusX='7.5' RadiusY='7.5' Canvas.Left='6' Canvas.Top='6' />" +
    "<Rectangle Width='186' Height='28' RadiusX='7.5' RadiusY='7.5' " + 
    "Canvas.Left='7' Canvas.Top='7'>" +
    "<Rectangle.Fill>" + 
      "<LinearGradientBrush EndPoint='0.5,0' StartPoint='0.5,1'>" + 
        "<GradientStop Color='#FFE67D00' Offset='0' />" +
        "<GradientStop Color='#FFFFB55E' Offset='1' />" +
      "</LinearGradientBrush>" + 
    "</Rectangle.Fill>" + 
  "</Rectangle>" + 
  "<TextBlock Width='175' Height='25' Canvas.Left='12' " + 
    "Canvas.Top='10' TextWrapping='NoWrap' x:Name='cardtitle{id}' />" +
  "<TextBlock Width='186' Height='100' Canvas.Left='6' " +
    "Canvas.Top='40' TextWrapping='Wrap' x:Name='cardmessage{id}'>" +
    "<TextBlock.Clip>" +
      "<RectangleGeometry Rect='0, 0, 186, 100' />" + 
    "</TextBlock.Clip>" + 
  "</TextBlock>" +
"</Canvas>";

最後のほうにある2つのTextBlockに設定されているx:Name属性の値の最後が{id}という文字になっています。これは同じNameの値をもつオブジェクトを複数追加できないため、実行時にサフィックスと置き換えるためのプレースホルダーになります。

メッセージカードの表示

では、メッセージカードを画面に表示するまでのコードを追加していきます。手順としては以下のようになります。

  • サフィックスの取得
  • XAMLの読み込み
  • オブジェクトの追加

まず、サフィックスの取得ですがこれは連番を取得するようにします。0からカウントアップしていく形で、すでにその値をNameにもつオブジェクトがないかを調べ、ない場合は、その値を使用します。オブジェクトがあるかどうかを調べるためには以下のfindName関数を使用します。findName関数は指定された値のNameをもつオブジェクトがある場合は、そのオブジェクトを返します。ない場合はnullを返します。

var obj = plugin.content.findName("cardtitle");

[function]の中に、getSuffixValueという変数で関数を作成します。引数には上記のfindName関数を呼べるように、Silverlightのプラグインオブジェクトを渡します。

var getSuffixValue = function(plugin){
  var counter = 0;
  while(true){
    if(plugin.content.findName("cardtitle" + counter) == null){
      return counter.toString();
    }
    counter++;
  }
}

上記のコード、実はJavaScriptのNumber型の限界まで同じNameがあった場合に、例外が発生してしまいます。しかも結構な時間待たされてから例外が発生することになります。本来であれば別なサフィックスの取得の仕方や、途中であきらめるコードが必要になるのですが、今回の場合は、メッセージカードアプリに限定しおり、かつそれほど枚数を作成しないということからよしとしておきます。

続いて、XAMLを読み込んでオブジェクトを作成します。前述したXAMLの文字列が設定してあるcardXaml変数と、createFromXaml関数を使用します。[function]の中にcreateCardObjectという変数で関数を作成します。引数にはcreateFromXamlを呼べるようにSilverlightプラグインオブジェクトと、cardXamlのプレースホルダーを置換するためのサフィックスを渡します。

var createCardObject = function(plugin, suffix){
  var cardObject = 
    plugin.content.createFromXaml(cardXaml.replace(/{id}/g, suffix));
  plugin.content.root.children.add(cardObject);
  return cardObject;
}

上記、2つの関数を呼び出すコードを[sample.card]の中に記述します。

sample.card = function(plugin){
  var suffix = getSuffixValue(plugin);
  var cardObject = createCardObject(plugin, suffix);
};

最後に、上記のsample.cardを呼び出すコードを追加してHTMLのボタンが押された時に呼び出せば、メッセージカードが画面に表示されます。index.htm.jsに以下の関数を追加してください。

function createNewCard(){
  var plugin = document.getElementById("agControl");
  var card = new sample.card(plugin);
}

createNewCard関数では最初にSilverlightプラグインのオブジェクトを取得しています。次にsample.cardを実行しているのですが、ここではsample.cardを続く処理としてオブジェクトで扱いたいのでコンストラクタ呼び出しにしています。

HTMLのボタンがクリックされた時にcreateNewCardが呼び出されるようにして、メッセージカードの表示の完成です。以下のようにindex.htmのボタンのコードを修正してください。

<button onclick="createNewCard();">Create</button>

では、実行してみましょう。画面を立ち上げてボタンをクリックすると以下のようになります。

画像

無事にメッセージカードが表示されましたが、表示位置を指定していないため常に左上に表示されてしまっています。これでは入力域と重なってしまい何とも微妙な状態にです。表示位置をランダムに設定するように修正します。card.jsのcreateCardObject変数の関数を以下のように修正してください。

var createCardObject = function(plugin, suffix){
  var cardObject = 
    plugin.content.createFromXaml(cardXaml.replace(/{id}/g, suffix));
  //追加
  cardObject["Canvas.Left"] =
    Math.random() * (plugin.content.root.width - 200);
  cardObject["Canvas.Top"] = 
    Math.random() * (plugin.content.root.height - 150);
  plugin.content.root.children.add(cardObject);
  return cardObject;
}

XAMLを読み込んでオブジェクトを生成した後に、左からの距離と上からの距離をランダムに設定するコードを追加しています。画面からはみ出してしまわないように、カード自身の高さと幅を差し引いて調整を行っています。

index.htmを読み直して、再度メッセージカードを表示してください。今回は無事にさまざまな場所にカードが表示されています。

画像

タイトルとメッセージを表示する

では、最後の処理として入力されたタイトルとメッセージをカードに表示します。ボタンが押された時にHTMLのテキストボックスに入力されている値を、メッセージカードに設定します。

まずは、sample.cardにタイトルとメッセージのそれぞれを設定できる関数を用意します。[sample.card]の中の最後に以下のコードを追加してください。

this.setTitle = function(title){
  plugin.content.findName("cardtitle" + suffix).text = title;
};
this.setMessage = function(message){
  plugin.content.findName("cardmessage" + suffix).text = message;
};

findName関数によって、それぞれ対応するTextBlockを取得してtextプロパティに渡された値を設定しています。

続いて、HTMLから値を取得して、今作成した関数に値を渡すコードをindex.htm.jsのcreateNewCard関数に追加します。

function createNewCard(){
  var plugin = document.getElementById("agControl");
  var card = new sample.card(plugin);
  //追加
  card.setTitle(document.getElementById("titleTextBox").value);
  card.setMessage(document.getElementById("messageTextBox").value);
}

では、何かテキストボックスに値を入力して、メッセージカードを表示してみましょう。無事にメッセージカードに値が表示されます。

画像

文字の表示ですが、残念ながら現在のSilverlightのバージョンでは標準では日本語に対応していません。そのためHTMLのテキストボックスに日本語を入力しても、表示時には文字化けを起こしてしまいます。日本語を表示するためには対応したフォントをダウンロードしてTextBlockに設定するなどの手順が必要になります。詳しい手順は筆者のブログに過去に取り上げましたので、参照してください。

Silverlight2.0では日本語表示をサポートするとのことなので、こちらもリリースが待ち遠しいものです。

では、これでメッセージカードアプリはいったん完成ですが、何点か問題点もあります。パッと思いつくところをあげるとすると以下のようなものがあります。

  • 重なりが発生する
  • 作成したカードを修正できない
  • カードを保存できない

これらの機能を追加することで、よりよいアプリケーションになると思いますので、ぜひチャレンジしてみてください。サンプルとして重なりが発生しても大丈夫なようにカードをドラッグドロップで移動できるように拡張したものを作成しました。以下よりダウンロードできますので、ぜひ参考にしてください。

このサンプルではドラッグドロップもマウスを離した後に少し滑って止まるように実装しています。また、カードの表示方法もただ無機質に表示されるだけではなく、飛び出したように見えるアニメーションを追加して表示しています。

次回予告

今回はSilverlight1.0での開発を行いました。JavaScriptでの開発の感触を実感していただけたのであれば幸いです。

次回は、以前からの予告どおりにSilverlight1.1での開発を取り上げようと考えています。が、Silverlight2.0Betaのリリースが2008年の第1四半期とアナウンスされています。米国でMIX08というイベントが3/5から開催されるのですが、筆者の予想ではこの前後に何らかの発表があるのではないかと考えています。そのため、次回の掲載の前に発表された場合は、内容を変更してSilverlight2.0を取り上げようと考えています。お楽しみに。

おすすめ記事

記事・ニュース一覧