Part 3. 高度な Silverlight 2 アプリ開発


というわけで、Silverlight 2 & Expression Blend 2.5 の記事の最終回 Part 3. では、ここまでの知識を総括して、XML Web サービス経由でデータベースからデータを取り出して ListBox に表示するアプリケーションを開発してみることにします。

なお、以降の作業は Part 2. で完成させたソリューションファイルに対して行います。Expression Blend から Visual Studio の方に戻り、以下の作業を続けてください。

※ Part 2. 終了時点のファイルはこちらになります。

[Step 10] データ取得のための XML Web サービスの準備

まず、サーバ側にデータベースからデータを取得して送り返す XML Web サービスを準備します。出版社サンプルデータベースである pubs.mdf ファイルを準備し、以下の作業を行います。(準備できない方は、後ろに書いてある回避策を使ってください。)

  • Web サイトの App_Data フォルダ下に pubs.mdf ファイルを貼り付ける。
  • App_Code フォルダ下に、Pubs.dbml ファイル(LINQ to SQL の O/R マッピングファイル)を新規作成する。
  • Pubs.dbml ファイルを開き、サーバエクスプローラからすべてのテーブルをドラッグ&ドロップで追加する。(ドラッグ&ドロップできたらファイルはセーブしておきます。)

image

  • WebService.asmx ファイルを開き、LINQ to SQL でデータを取得して返すコードを記述します。

image

ここでは LINQ to SQL の詳細は解説しませんが、実装上のポイントは以下の通りです。

  • using キーワードにより、System.Linq と System.Data.Linq 名前空間の利用宣言を行います。(LINQ to SQL クエリを使うために必要)
  • データを返すための構造体として、AuthorList クラスを定義します。ここでは簡単のため、au_id (著者 ID)と au_name (著者名)の 2 つのフィールドを持つクラスを定義します。
  • AuthorList クラスの配列を返す GetData() メソッドを XML Web サービスとして定義します。LINQ to SQL のクエリを記述した後、.ToArray() メソッドを呼び出すことにより、LINQ クエリの実行結果を静的配列に変換することができます。

実装が終わった WebService.asmx ファイルは以下の通りです。完成したら、WebService.asmx ファイルをブラウザから呼び出して、動作確認を行ってください。

   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: using System.Linq;
   9: using System.Data.Linq;
  10:  
  11: [WebService(Namespace = "http://tempuri.org/")]
  12: [WebServiceBinding(ConformsTo = WsiProfiles.BasicProfile1_1)]
  13: public class WebService  : System.Web.Services.WebService {
  14:  
  15:     [WebMethod]
  16:     public string GetMessage(string name)
  17:     {
  18:         return "Hello World, " + name;
  19:     }
  20:  
  21:     [WebMethod]
  22:     public AuthorList[] GetData()
  23:     {
  24:         using (PubsDataContext pubs = new PubsDataContext())
  25:         {
  26:             var authors = from a in pubs.authors
  27:                           where a.state == "CA"
  28:                           select new AuthorList
  29:                           {
  30:                               au_id = a.au_id,
  31:                               au_name = a.au_fname + " " + a.au_lname
  32:                           };
  33:             return authors.ToArray();
  34:         }
  35:     }        
  36: }
  37:  
  38: public class AuthorList
  39: {
  40:     public string au_id { get; set; }
  41:     public string au_name { get; set; }
  42: }

image

※ pubs.mdf データベースファイルを用意できない場合について

この場合には、ダミーデータを返す XML Web サービスを開発してください。具体的には、WebService.asmx ファイル上に以下のようなコードを実装します。

   1: [WebMethod]
   2: public AuthorList[] GetData()
   3: {
   4:     AuthorList a1 = new AuthorList { au_id = "172-32-1176", au_name = "Johnson White" };
   5:     AuthorList a2 = new AuthorList { au_id = "213-46-8915", au_name = "Marjorie Green" };
   6:     AuthorList a3 = new AuthorList { au_id = "238-95-7766", au_name = "Cheryl Carson" };
   7:     AuthorList a4 = new AuthorList { au_id = "267-41-2394", au_name = "Michael O'Leary" };
   8:     AuthorList a5 = new AuthorList { au_id = "274-80-9391", au_name = "Dean Straight" };
   9:     AuthorList a6 = new AuthorList { au_id = "409-56-7008", au_name = "Abraham Bennet" };
  10:     AuthorList a7 = new AuthorList { au_id = "427-17-2319", au_name = "Ann Dull" };
  11:     return new AuthorList[] { a1, a2, a3, a4, a5, a6, a7 };
  12: }        

[Step 11] XML Web サービスの呼び出しと ListBox へのデータバインド

引き続き、XML Web サービスの呼び出しと ListBox へのデータバインドを行います。まず、Silverlight 2 アプリケーション側のサービス参照を更新し、新しく追加した、GetData() メソッドの情報をプロキシクラスに取り込みます。

image

次に一覧表示ボタンのイベントハンドラとして、XML Web サービス呼び出しとデータバインドを実装します。

  • Page.xaml ファイルを開きます。
  • XAML コードエディタを開き、Button オブジェクトに Click イベントハンドラを追加します。
  • コードビハインドファイルを開き、イベントハンドラコードを追加します。

image

   1: private void button1_Click(object sender, RoutedEventArgs e)
   2: {
   3:     button1.IsEnabled = false;
   4:     ServiceReference1.WebServiceSoapClient proxy = new ServiceReference1.WebServiceSoapClient();
   5:     proxy.GetDataCompleted += new EventHandler<SilverlightApplication1.ServiceReference1.GetDataCompletedEventArgs>(proxy_GetDataCompleted);
   6:     proxy.GetDataAsync();
   7: }
   8:  
   9: void proxy_GetDataCompleted(object sender, SilverlightApplication1.ServiceReference1.GetDataCompletedEventArgs e)
  10: {
  11:     button1.IsEnabled = true;
  12:     listBox1.ItemsSource = e.Result;
  13: }

実装が終了したら、アプリケーションを実行してみます。ボタンを押下すると、XML Web サービスが呼び出され、データバインドが実行されます。実行結果は以下のようになります。

image

ListBox の各行に「SilverlightApplication1.ServiceReference1.AuthorList」という文字列が表示されていることに着目してください。前述のコードでは、ListBox の ItemsSource プロパティに AuthorList オブジェクトのコレクションを割り当てていますが、ListBox コントロールはこのオブジェクトの表示方法を知りません。このため、既定の状態では、下図に示すように、各項目について .ToString() メソッドを呼び出して ListBox に表示する、という挙動を行います。

image

AuthorList オブジェクトの中身(つまり著者 ID や著者名)を取り出して ListBox 内に表示させるためには、ListBox コントロールにレンダリングテンプレート(各行を描画する際に利用するテンプレート)を指定しなければなりません。

[Step 12] ListBox コントロールに対する ItemTemplate の指定

ListBox コントロールに対するレンダリングテンプレートの指定には、Expression Blend を利用します。Expression Blend を使い、以下の作業を行います。

  • Page.xaml ファイルを開きます。
  • 「オブジェクトとタイムライン」ウィンドウの listBox1 を右クリックして、コンテキストメニューを開きます。
  • 「他のテンプレートの編集」→「ItemTemplate の編集」→「空アイテムの作成」を選択します。
  • DataTemplate リソースの作成ウィンドウが開くので、OK ボタンを押してデータテンプレートの編集画面を開きます。

image

image

上記作業を行う際に、「コントロールパーツ(テンプレート)の編集」ではなく「他のテンプレートの編集→ItemTemplate の編集」の方を選択することに注意してください。この二つは全く違う機能です。

  • 「コントロールパーツ(テンプレート)の編集」とは、ListBox コントロールそのものの描画方法(外観)を変えるための機能です。
  • 「ItemTemplate の編集」とは、ListBox 内の各行のレンダリング方法を変えるための機能です。

ItemTemplate の編集モードに入ると、デザイナが listBox1→DataTemplate の編集モードに入ります。

※ (参考) ItemTemplate の編集に入っているのに DataTemplate と表示されるのは、「ListBox コントロールの ItemTemplate プロパティに、データ表示のためのレンダリングテンプレートである DataTemplate オブジェクトを割り当てる」ためです。つまり、"ItemTemplate" は ListBox のプロパティ名、"DataTemplate" はそこに割り当てるオブジェクトのクラス名です。このことは、最後に生成される XAML コードを見るとよくわかるので、興味がある方は見てみてください。

image

このテンプレートの編集画面では、AuthorList オブジェクトを、どのように 1 行としてレンダリングするのかを設定します。著者 ID と著者名を表示したい場合には、下図のようなテンプレートを設定することになります。

image

具体的には、以下の作業を行います。

  • 既定で貼り付いている Grid レイアウトコントロールを、StackPanel レイアウトコントロールに差し替えます。(レイアウトの種類の変更 → StackPanel)
  • StackPanel コントロールの下に、TextBlock コントロールを二つ貼り付けます。(ツールボックスからダブルクリックで追加するとラク。間違って TextBox を貼り付けやすいので注意してください。自分は結構ミスします...)

image

image

  • StackPanel レイアウトコントロールの Orientation プロパティを、Vertical (縦に並べる)から Horizontal (横に並べる)に変更します。
  • 各 TextBlock の Margin プロパティを 4 に変更します。(既定だと 0 のため、文字がくっついて表示されてしまう。これを避けるため。なお、β2 版では IME 制御が不完全なためか、たまに Margin 値を打ち込むときに IME が全角になってしまい、うまく入力できないことがありますのでご注意を。)

image

最後に、各 TextBlock に対して、データバインドの指定を行います。

  • 左側の TextBlock を選択し、プロパティウィンドウの Text プロパティの右側にある「・」ボタン(詳細プロパティオプション)をクリックし、データバインドを選択します。
  • 「データバインドの作成」ウィンドウの「明示的なデータコンテキスト」タブを開き、カスタムパスとして "au_id" を設定します。(※ データフィールドタブのカスタムパスの指定は、コンテキストバインドの機能ではないため、ここでは使いません。)
  • 同様に、右側の TextBlock に対しては、カスタムパスとして "au_name" を設定します。

image

image

image

以上の作業により、レンダリングのためのテンプレートが設定できます。(見た目では、TextBlock の表示がつぶれます。)

image

ファイルをセーブしたら、Visual Studio に戻り、アプリケーションを実行してみてください(ブラウザを再起動することを忘れずに^^)。正しくアプリケーションが作成できていれば、以下のような一覧表示が行えるはずです。

image

最終的に完成した XAML コードとコードビハインドを以下に示します。

[Page.xaml ファイル]

   1: <UserControl
   2:     xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
   3:     xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
   4:     xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
   5:     xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
   6:     mc:Ignorable="d"
   7:     x:Class="SilverlightApplication1.Page"
   8:     d:DesignWidth="400" d:DesignHeight="300">
   9:     <UserControl.Resources>
  10:         <DataTemplate x:Key="DataTemplate1">
  11:             <StackPanel Orientation="Horizontal" Margin="0,0,0,0">
  12:                 <TextBlock Text="{Binding Mode=OneWay, Path=au_id}" TextWrapping="Wrap" Margin="4,4,4,4"/>
  13:                 <TextBlock Text="{Binding Mode=OneWay, Path=au_name}" TextWrapping="Wrap" Margin="4,4,4,4"/>
  14:             </StackPanel>
  15:         </DataTemplate>
  16:     </UserControl.Resources>
  17:  
  18:     <Grid x:Name="LayoutRoot" Background="White" >
  19:         <Grid.RowDefinitions>
  20:             <RowDefinition Height="Auto"/>
  21:             <RowDefinition Height="*"/>
  22:         </Grid.RowDefinitions>
  23:         <Grid.ColumnDefinitions>
  24:             <ColumnDefinition Width="Auto"/>
  25:             <ColumnDefinition Width="*"/>
  26:             <ColumnDefinition Width="Auto"/>
  27:         </Grid.ColumnDefinitions>
  28:         <TextBlock HorizontalAlignment="Left" VerticalAlignment="Stretch" Text="著者一覧表示" TextWrapping="Wrap" Margin="8,8,8,8" Width="Auto" x:Name="textBlock1"/>
  29:         <Button Margin="8,8,8,8" Content="一覧表示" Grid.Column="2" VerticalAlignment="Stretch" HorizontalAlignment="Stretch" x:Name="button1" Click="button1_Click" />
  30:         <ListBox Margin="8,8,8,8" Grid.Row="1" VerticalAlignment="Stretch" HorizontalAlignment="Stretch" Grid.ColumnSpan="3" x:Name="listBox1" ItemTemplate="{StaticResource DataTemplate1}"/>
  31:     </Grid>
  32: </UserControl>

[Page.xaml.cs ファイル]

   1: using System;
   2: using System.Windows;
   3: using System.Windows.Controls;
   4: using System.Windows.Documents;
   5: using System.Windows.Ink;
   6: using System.Windows.Input;
   7: using System.Windows.Media;
   8: using System.Windows.Media.Animation;
   9: using System.Windows.Shapes;
  10:  
  11: namespace SilverlightApplication1
  12: {
  13:     public partial class Page : UserControl
  14:     {
  15:         public Page()
  16:         {
  17:             // Required to initialize variables
  18:             InitializeComponent();
  19:         }
  20:  
  21:         private void button1_Click(object sender, RoutedEventArgs e)
  22:         {
  23:             button1.IsEnabled = false;
  24:             ServiceReference1.WebServiceSoapClient proxy = new ServiceReference1.WebServiceSoapClient();
  25:             proxy.GetDataCompleted += new EventHandler<SilverlightApplication1.ServiceReference1.GetDataCompletedEventArgs>(proxy_GetDataCompleted);
  26:             proxy.GetDataAsync();
  27:         }
  28:  
  29:         void proxy_GetDataCompleted(object sender, SilverlightApplication1.ServiceReference1.GetDataCompletedEventArgs e)
  30:         {
  31:             button1.IsEnabled = true;
  32:             listBox1.ItemsSource = e.Result;
  33:         }
  34:     }
  35: }

以上で基本的には終わりですが、オマケでちょっと遊びをしてみたいと思います。:-)

[Step 13] レンダリング変形機能の活用

従来の Windows フォームや VB6 などのアプリケーションと異なり、Silverlight 2 では、ベクターグラフィクスベースのレンダリングエンジンを利用した描画を行っています。

  • これは、「TextBox や ListBox などがドット絵として描画されるのではなく、ベクターグラフィクスとして描画される」というものです。
  • このような特性を持つが故に、Expression Blend や Visual Studio では、デザイナ画面で「画面そのものを拡大表示する」ことができるようになっています。

image

例えば、Expression Blend 上で、以下のような作業をしてみてください。

  • ListBox コントロールの四隅をつまんで回転させてみる。
  • Button コントロールを台形変形させてみる。(プロパティウィンドウ内の「変換」タブを開き、その中にある「傾斜」をいじってみてください。)

image

このように変形しても、アプリケーションは全く問題なく動作します。(ボタンも問題なく押せるし、リストボックスも問題なく斜めにスクロールします。)

image

実際の業務アプリケーションでこのような変形加工を行うことはないと思いますが^^、Silverlight 2 がベクターベースの描画エンジンを利用している、というポイントは非常に興味深いポイントであるため、覚えておいていただければと思います。

[Step 14] DataGrid コントロールの利用

ちなみに、ここまで ListBox コントロールを使ってきましたが、DataGrid コントロールを使うと簡単にグリッドにデータ表示することができます。これに関しては、細かいやり方は説明しませんので、ここまでの解説を参考にして実際にいじってみてください。:-) キーポイントは以下の通りです。

  • 既定では、Expression Blend のツールボックス上に DataGrid コントロールは現われません。これは、DataGrid コントロールが拡張コントロールであり、参照設定が必要なためです。プロジェクト→参照設定の追加から、C:\Program Files (x86)\Microsoft SDKs\Silverlight\v2.0\Libraries\Client 内にあるSystem.Windows.Controls.Data.dll ファイルに参照設定を張り、さらにアセットライブラリのカスタムコントロール内から選択することで、DataGrid が使えるようになります。
  • データバインドのやり方は基本的に同じで、ItemsSource プロパティにオブジェクトコレクションを割り当てます。

image

※ レンダリングテンプレートの変更は、Columns プロパティに DataGridCellTemplate を割り当てることによって行う....のですが、現時点では Expression Blend からは設定できないかもしれません。(探してみたけど見当たらず....) XAML コードからであれば指定できるので、興味がある方はやってみてください。具体的なやり方は以下のページが参考になると思います。http://blogs.msdn.com/scmorris/

※ 完成したソリューションはこちらになります。

[本エントリと全体のまとめ]

というわけで、本エントリのキーポイントをまとめると以下のようになります。

  • ListBox コントロールの ItemsSource プロパティにオブジェクトコレクションを割り当てると、データバインドができる。
  • データバインド時のレンダリングテンプレートは、ItemTemplate で指定する。
  • Silverlight 2 はベクターベースのレンダリングエンジンを利用している。

3 つのエントリにわけて Silverlight 2 アプリケーションの基本的な開発方法を解説してきましたが、Silverlight 2 と Expression Blend はとにかく面白いツールだと思います。特に、リッチな Web アプリケーションの UI 画面を C# で開発できるというメリットは極めて大きく、また新しい UI 設計概念も非常によくできている、というのが個人的な感想です。

ぜひ、本エントリなどを活用して Silverlight 2 と Expression Blend による開発を楽しんでください。

……というわけでやっと書き終わりました;。やっぱり結構大変っすねー;。

次は何を書こうかな……

Comments (2)

  1. develop .net より:

    私が、細々と Silverlight のトピックを書いている間に、弊社のコンサルティングサービス部門の某コンサルタントが、網羅的な三部作を書き上げましたので、ご紹介します。 ・ Part 1. 最も簡単な

  2. DATAGRIDの連動 より:

    2つ以上のDATAGRIDの連動(スクロール)ができないと、業務アプリを作るのは無理ですね。

Skip to main content