Part 1. 最も簡単な Silverlight 2 アプリ開発

というわけで今日は TechEd 2008 Yokohama の会場に来てます。今回は MCS がコンサルティングサービス体験コーナーを出展していて、そのヘルプで来てるんですが、なにげにお昼までは出番がない……

20080828100818

というわけで、昨日書きかけた原稿を完成させるべく控え室で Live Writer と格闘中。そんなわけで、早速 SL2 アプリの開発を見て行きます。

まず Part 1. では、以下の項目について解説したいと思います。

  • Visual Studio 2008 を使った Silverlight 2 アプリ開発の基本
  • Hello World Silverlight 2 アプリの開発
  • XML Web サービス連携の基本

なお、本資料を書くにあたっては、Silverlight 2 beta 2 chained installer の SDK キットと、Expression Blend 2.5 June Preview (日本語版) を利用しています。こちらのセットアップ方法については解説しませんので、必要な方はネットを検索してみてください。

[Step 1] Silverlight 2 プロジェクトの新規作成

まず、VS2008 を起動して、新規に Silverlight 2 のプロジェクトを作成します。

  • SL2 の SDK をインストールすると、VS2008 に SL2 のプロジェクトテンプレートがインストールされます。こちらから新規に SL2 アプリを作成します。
  • 作成時に、SL2 をホストするためのサンプル Web サイトも同時に作成するかを問われるので、こちらも同時に作成してください。

20080828a

20080828b

SL2 のプロジェクトはクラスライブラリプロジェクト(SilverlightApplication1)として作成されます。

  • このプロジェクトがコンパイルされると、.xap ファイル(SL2 アプリがパッケージングされた zip ファイル)が作成されます。
  • .xap ファイルは、テスト用サンプル Web サイト(SilverlightApplication1Web)にコピーされ(ClientBin フォルダ下)、これがテストページから呼び出される形で動作します。

20080828c

[Step 2] StackPanel を利用したコントロールの表示

まず、クラスライブラリプロジェクト(SilverlightApplication1)内の Page.xaml を開きます。

  • 画面上側にプレビュー、下側に XAML エディタが現れます。
  • 下側のウィンドウから、直接 XAML コードを書きます。

20080828d

簡単のため、レイアウトシステムを Grid から StackPanel に切り替えます。

  • Page.xaml 内には、<Grid> タグがありますが、これはタグ内に書かれた UI 部品をグリッド(表)に割り当てて表示するという特殊なレイアウトシステムです。
  • Grid レイアウトは使い方が複雑なので(Part 2 で説明します)、ここでは簡単な StackPanel を使うことにし、コードを以下のように書き換えます。

[修正前]

    1: <UserControl x:Class="SilverlightApplication1.Page"
    2:     xmlns="https://schemas.microsoft.com/winfx/2006/xaml/presentation" 
    3:     xmlns:x="https://schemas.microsoft.com/winfx/2006/xaml" 
    4:     Width="400" Height="300">
    5:     <Grid x:Name="LayoutRoot" Background="White">
    6:  
    7:     </Grid>
    8: </UserControl>

[修正後]

    1: <UserControl x:Class="SilverlightApplication1.Page"
    2:     xmlns="https://schemas.microsoft.com/winfx/2006/xaml/presentation" 
    3:     xmlns:x="https://schemas.microsoft.com/winfx/2006/xaml" 
    4:     Width="400" Height="300">
    5:      <StackPanel> 
    6:         
    7:      </StackPanel> 
    8: </UserControl>

次に、StackPanel 内に、タグを使って UI 部品を配置していきます。

  • ここではまず、テキストボックス、ラベル、ボタンの 3 つを配置してみます。(ツールボックスをダブルクリックしてコードを自動生成することもできますが、IntelliSense を使ってコードを書いてもよいと思います。)
  • 部品群は、StackPanel により自動的に「縦に並べて」表示されます。(コードを書くと、preview 画面に反映が行われます。)
  • 各オブジェクトに名前をつける場合には、x:Name という属性を利用してください。
    1: <UserControl x:Class="SilverlightApplication1.Page"
    2:     xmlns="https://schemas.microsoft.com/winfx/2006/xaml/presentation" 
    3:     xmlns:x="https://schemas.microsoft.com/winfx/2006/xaml" 
    4:     Width="400" Height="300">
    5:     <StackPanel> 
    6:          <TextBox x:Name="textBox1" Text="Nobuyuki" />  
    7:          <TextBlock x:Name="textBlock1" Text="あああ" />  
    8:          <Button x:Name="button1" Content="ボタン" />  
    9:     </StackPanel> 
   10: </UserControl>

20080828e

上記のコードには、非常に興味深いポイントがいくつかあります。これは SL2/WPF 特有の UI 概念を反映しているので、ぜひ注目してください。

  1. SL2 には Label コントロールがありません。かわりに TextBlock コントロールを利用します。
  2. TextBox と TextBlock コントロールでは、表示内容の指定に .Text プロパティを使います。 しかし、Button コントロールでは、表示内容の指定に .Content プロパティを使います。

これは、SL2/WPF では「コンテンツ合成」と呼ばれる概念がサポートされていることに起因するものです。例えば 1. についていうと、

  • ラベルは必ずしも「文字」とは限りません。場合によってはイラストだったりすることもあります。
  • しかし、従来の Label コントロールは、中身を「文字」に暗黙的に決めてしまっていました。
  • この問題を解決するため、「文字」を表示するためには、Label ではなく TextBlock を使います。

また、2. については、

  • ボタンの中身は必ずしも「文字」とは限りません。もしかしたら「絵」かもしれませんし、あるいは複合コンテンツかもしれません。
  • こうしたケースをサポートできるように、ボタンの中身に「複合コンテンツ」を指定できるように設計されています。
  • 例えば、ボタンの中に画像を埋め込みたい場合には、以下のようにします。(詳細なコードは理解しなくても OK です。)
    1: <UserControl x:Class="SilverlightApplication1.Page" 
    2:     xmlns="https://schemas.microsoft.com/winfx/2006/xaml/presentation" 
    3:     xmlns:x="https://schemas.microsoft.com/winfx/2006/xaml" 
    4:     Width="400" Height="300"> 
    5:     <StackPanel> 
    6:         <TextBox x:Name="textBox1" Text="Nobuyuki" /> 
    7:         <TextBlock x:Name="textBlock1" Text="あああ" /> 
    8:         <Button x:Name="button1"> 
    9:              <Button.Content>  
   10:                  <Image Height="80.75" Width="56.791" Source="pic1.jpg" Stretch="Fill"/> 
   11:              </Button.Content>  
   12:         </Button> 
   13:     </StackPanel> 
   14: </UserControl> 

以上を整理すると、以下の通りになります。

  • SL2 には、Label コントロールはありません。かわりに TextBlock コントロールを使います。
  • 各コントロールの表示内容に文字列を指定する場合、.Text プロパティを使う場合と .Content プロパティを使う場合があります。
  • .Content プロパティを使うコントロールの場合、表示コンテンツとして画像や複合コンテンツを使うことができるようになっています。

[Step 3] Hello World メッセージの表示

引き続き、ボタンをクリックした際に Hello World メッセージが表示されるようにします。 まずはボタンのイベントハンドラを作成します。

  • Preview 画面からダブルクリックでイベントハンドラを作る機能は今のところありません。
  • XAML コードにて、Click イベントハンドラを追加し、コードビハインドに処理を記述します。
    1: <UserControl x:Class="SilverlightApplication1.Page" 
    2:     xmlns="https://schemas.microsoft.com/winfx/2006/xaml/presentation" 
    3:     xmlns:x="https://schemas.microsoft.com/winfx/2006/xaml" 
    4:     Width="400" Height="300"> 
    5:     <StackPanel> 
    6:         <TextBox x:Name="textBox1" Text="Nobuyuki" /> 
    7:         <TextBlock x:Name="textBlock1" Text="あああ" /> 
    8:         <Button x:Name="button1" Content="ボタン" Click="button1_Click"  /> 
    9:     </StackPanel> 
   10: </UserControl>
    1: private void button1_Click(object sender, RoutedEventArgs e) 
    2: { 
    3:     textBlock1.Text = "Hello World SL2, " + textBox1.Text; 
    4: } 

20080828f

これを Ctrl+F5 キーで実行すると、Hello World メッセージが表示できます。

20080828g

  • コードを修正してアプリケーションを再実行する場合には、いったんブラウザを終了させてください。(ブラウザ内にキャッシュが残るため)
  • SL2 では、いわゆるモーダルダイアログがサポートされていません。もしモーダルダイアログを表示したい場合には、以下のようなコードでブラウザの警告ポップアップを使う必要があります。
    1: private void button1_Click(object sender, RoutedEventArgs e) 
    2: { 
    3:     System.Windows.Browser.HtmlPage.Window.Alert("Hello World SL2, " + textBox1.Text); 
    4: }

[Step 4] XML Web サービスの呼び出し

SL2 では、XML Web サービスを呼び出すこともできます(クロスドメインアクセスも可能)。以下の手順で、XML Web サービスの作成と呼び出しを行ってみます。 ここでは簡単のため、WCF ではなく ASP.NET XML Web サービスを使うことにします。

まずは Web サイト側に WebService.asmx ファイルを追加します。

  • コードビハインドを使わない開発モデルを使います。
  • 以下のような GetMessage Web サービスを開発します。
  • 開発したら、この WebService.asmx ファイルを呼び出してみて、動作確認を行ってください。

20080828h

    1: <%@ WebService Language="C#" Class="WebService" %> 
    2:  
    3: using System; 
    4: using System.Web; 
    5: using System.Web.Services; 
    6: using System.Web.Services.Protocols; 
    7:  
    8: [WebService(Namespace = "https://tempuri.org/")] 
    9: [WebServiceBinding(ConformsTo = WsiProfiles.BasicProfile1_1)] 
   10: public class WebService  : System.Web.Services.WebService { 
   11:  
   12:     [WebMethod] 
   13:     public string GetMessage(string name) {  
   14:         return "Hello World, " + name;  
   15:      }  
   16: } 

20080828j

次に、Silverlight 2 アプリケーション側に、サービス参照を追加します。

  • サービス参照では、URL 欄に WebService.asmx ファイルの URL を入力します。ポート番号はランダムで決定されるので、各自の環境に合わせて設定してください。(探索ボタンを押すと簡単に追加できますのでこちらがオススメ。)
  • これにより、Web サービスを呼び出すためのプロキシクラスが作成されます。

20080828k

20080828l

作成されるプロキシクラスに関しては、以下の 2 つの大きな特徴がありますので注意してください。 まず一つ目は、WCF(Windows Communication Foundation)のプロキシクラスが作成される、という点です。

  • .NET Framework 2.0 で利用していた、ASP.NET XML Web サービスのプロキシクラスではなく、WCF のプロキシクラスが作成されます。
  • ただし、SL2 では WCF のサブセットのみのサポートです。このため、WS-* を使ったサイトの呼び出しなどはできません。(BasicHttpBinding のみがサポートされています。)

そしてもう一つは、非同期呼び出しパターンのみがサポートされている、という点です。

  • プロキシクラスを使った XML Web サービス呼び出しは、多くの場合、同期型(=メソッドを呼び出すと、すぐに結果が取り出せる)になります。
  • しかし SL2 で作成される XML Web サービスのプロキシクラスは、非同期型(=Web サービス呼び出しを行うメソッドと、処理結果を受け取るメソッドが別になる)になります。

具体的には、Page.xaml.cs ファイルの button1_Click メソッドに以下のようなコードを書きます。

    1: private void button1_Click(object sender, RoutedEventArgs e)
    2: {
    3:     string name = textBox1.Text;
    4:     // プロキシクラスのインスタンス化
    5:     ServiceReference1.WebServiceSoapClient proxy = new ServiceReference1.WebServiceSoapClient();
    6:     // GetMessage Web サービス呼び出し終了時に呼び出されるメソッドを指定
    7:     proxy.GetMessageCompleted += new EventHandler<SilverlightApplication1.ServiceReference1.GetMessageCompletedEventArgs>(proxy_GetMessageCompleted);
    8:     // 非同期呼び出しを開始
    9:     proxy.GetMessageAsync(name);
   10: }
   11:  
   12: // 非同期呼び出し終了時に呼び出されるメソッド
   13: void proxy_GetMessageCompleted(object sender, SilverlightApplication1.ServiceReference1.GetMessageCompletedEventArgs e)
   14: {
   15:     // 呼び出し結果取り出し(呼び出しに失敗している場合には、e.Result を取り出そうとすると例外発生)
   16:     string result = e.Result; 
   17:     textBlock1.Text = result;
   18: }

これを実行すると、(見た目は変わりませんが) XML Web サービスが呼び出されるような Silverlight 2 アプリケーションになります。(再実行時はブラウザを一度終了させることをお忘れなく^^)

20080828m

なお、現在の実装のままだと、ボタンが二重押しされる可能性があります。このため、以下のようなコードを追加して、XML Web サービス呼び出し中はボタンを Disable 化するとよいでしょう。

    1: private void button1_Click(object sender, RoutedEventArgs e)
    2: {
    3:     button1.IsEnabled = false;   
    4:     string name = textBox1.Text;
    5:     ServiceReference1.WebServiceSoapClient proxy = new ServiceReference1.WebServiceSoapClient();
    6:     proxy.GetMessageCompleted += new EventHandler<SilverlightApplication1.ServiceReference1.GetMessageCompletedEventArgs>(proxy_GetMessageCompleted);
    7:     proxy.GetMessageAsync(name);
    8: }
    9:  
   10: void proxy_GetMessageCompleted(object sender, SilverlightApplication1.ServiceReference1.GetMessageCompletedEventArgs e)
   11: {
   12:     button1.IsEnabled = true; 
   13:     string result = e.Result; 
   14:     textBlock1.Text = result;
   15: }
   16:  

以上で XML Web サービスと連携する基本的な Silverlight 2 アプリケーションの開発方法についての説明はおわりです。キーポイントをまとめると次のようになります。

  • Silverlight 2 では、XAML と呼ばれるファイルを使って画面を作る。
  • Silverlight 2 では、Grid や StackPanel などの中に、UI 部品を並べていって画面を作る。
  • Silverlight 2 には Label コントロールはない。かわりに TextBlock コントロールを使う。
  • コントロールに文字列を設定する場合には、.Text プロパティを使う場合と、.Content プロパティを使う場合がある。.Content プロパティが使われている場合には、中身のコンテンツとして複合コンテンツを指定できるようになっている。
  • Silverlight 2 からはサービス参照機能を使うことで、簡単に XML Web サービスが呼び出せるようになっている。

引き続き、Part 2. では Expression Blend との連携方法などについて説明していきたいと思います。

というわけで初めて Windows Live Writer 使ってみましたがめちゃめちゃ使いやすいですね。こんな便利ツールがあったとは……^^