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 ファイルを開き、サーバエクスプローラからすべてのテーブルをドラッグ&ドロップで追加する。(ドラッグ&ドロップできたらファイルはセーブしておきます。)
- WebService.asmx ファイルを開き、LINQ to SQL でデータを取得して返すコードを記述します。
ここでは 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 = "https://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: }
※ 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() メソッドの情報をプロキシクラスに取り込みます。
次に一覧表示ボタンのイベントハンドラとして、XML Web サービス呼び出しとデータバインドを実装します。
- Page.xaml ファイルを開きます。
- XAML コードエディタを開き、Button オブジェクトに Click イベントハンドラを追加します。
- コードビハインドファイルを開き、イベントハンドラコードを追加します。
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 サービスが呼び出され、データバインドが実行されます。実行結果は以下のようになります。
ListBox の各行に「SilverlightApplication1.ServiceReference1.AuthorList」という文字列が表示されていることに着目してください。前述のコードでは、ListBox の ItemsSource プロパティに AuthorList オブジェクトのコレクションを割り当てていますが、ListBox コントロールはこのオブジェクトの表示方法を知りません。このため、既定の状態では、下図に示すように、各項目について .ToString() メソッドを呼び出して ListBox に表示する、という挙動を行います。
AuthorList オブジェクトの中身(つまり著者 ID や著者名)を取り出して ListBox 内に表示させるためには、ListBox コントロールにレンダリングテンプレート(各行を描画する際に利用するテンプレート)を指定しなければなりません。
[Step 12] ListBox コントロールに対する ItemTemplate の指定
ListBox コントロールに対するレンダリングテンプレートの指定には、Expression Blend を利用します。Expression Blend を使い、以下の作業を行います。
- Page.xaml ファイルを開きます。
- 「オブジェクトとタイムライン」ウィンドウの listBox1 を右クリックして、コンテキストメニューを開きます。
- 「他のテンプレートの編集」 →「ItemTemplate の編集」→「空アイテムの作成」を選択します。
- DataTemplate リソースの作成ウィンドウが開くので、OK ボタンを押してデータテンプレートの編集画面を開きます。
上記作業を行う際に、 「コントロールパーツ(テンプレート)の編集」ではなく「他のテンプレートの編集→ItemTemplate の編集」の方を選択することに注意してください。この二つは全く違う機能です。
- 「コントロールパーツ(テンプレート)の編集」とは、ListBox コントロールそのものの描画方法(外観)を変えるための機能です。
- 「ItemTemplate の編集」とは、ListBox 内の各行のレンダリング方法を変えるための機能です。
ItemTemplate の編集モードに入ると、デザイナが listBox1→DataTemplate の編集モードに入ります。
※ (参考) ItemTemplate の編集に入っているのに DataTemplate と表示されるのは、「ListBox コントロールの ItemTemplate プロパティに、データ表示のためのレンダリングテンプレートである DataTemplate オブジェクトを割り当てる」ためです。つまり、"ItemTemplate" は ListBox のプロパティ名、"DataTemplate" はそこに割り当てるオブジェクトのクラス名です。このことは、最後に生成される XAML コードを見るとよくわかるので、興味がある方は見てみてください。
このテンプレートの編集画面では、AuthorList オブジェクトを、どのように 1 行としてレンダリングするのかを設定します。著者 ID と著者名を表示したい場合には、下図のようなテンプレートを設定することになります。
具体的には、以下の作業を行います。
- 既定で貼り付いている Grid レイアウトコントロールを、StackPanel レイアウトコントロールに差し替えます。(レイアウトの種類の変更 → StackPanel)
- StackPanel コントロールの下に、TextBlock コントロールを二つ貼り付けます。(ツールボックスからダブルクリックで追加するとラク。間違って TextBox を貼り付けやすいので注意してください。自分は結構ミスします...)
- StackPanel レイアウトコントロールの Orientation プロパティを、Vertical (縦に並べる)から Horizontal (横に並べる)に変更します。
- 各 TextBlock の Margin プロパティを 4 に変更します。(既定だと 0 のため、文字がくっついて表示されてしまう。これを避けるため。なお、β2 版では IME 制御が不完全なためか、たまに Margin 値を打ち込むときに IME が全角になってしまい、うまく入力できないことがありますのでご注意を。)
最後に、各 TextBlock に対して、データバインドの指定を行います。
- 左側の TextBlock を選択し、プロパティウィンドウの Text プロパティの右側にある「・」ボタン(詳細プロパティオプション)をクリックし、データバインドを選択します。
- 「データバインドの作成」ウィンドウの「明示的なデータコンテキスト」タブを開き、カスタムパスとして "au_id" を設定します。(※ データフィールドタブのカスタムパスの指定は、コンテキストバインドの機能ではないため、ここでは使いません。)
- 同様に、右側の TextBlock に対しては、カスタムパスとして "au_name" を設定します。
以上の作業により、レンダリングのためのテンプレートが設定できます。(見た目では、TextBlock の表示がつぶれます。)
ファイルをセーブしたら、Visual Studio に戻り、アプリケーションを実行してみてください(ブラウザを再起動することを忘れずに^^)。正しくアプリケーションが作成できていれば、以下のような一覧表示が行えるはずです。
最終的に完成した XAML コードとコードビハインドを以下に示します。
[Page.xaml ファイル]
1: <UserControl
2: xmlns="https://schemas.microsoft.com/winfx/2006/xaml/presentation"
3: xmlns:x="https://schemas.microsoft.com/winfx/2006/xaml"
4: xmlns:d="https://schemas.microsoft.com/expression/blend/2008"
5: xmlns:mc="https://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 では、デザイナ画面で「画面そのものを拡大表示する」ことができるようになっています。
例えば、Expression Blend 上で、以下のような作業をしてみてください。
- ListBox コントロールの四隅をつまんで回転させてみる。
- Button コントロールを台形変形させてみる。(プロパティウィンドウ内の「変換」タブを開き、その中にある「傾斜」をいじってみてください。)
このように変形しても、アプリケーションは全く問題なく動作します。(ボタンも問題なく押せるし、リストボックスも問題なく斜めにスクロールします。)
実際の業務アプリケーションでこのような変形加工を行うことはないと思いますが^^、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 プロパティにオブジェクトコレクションを割り当てます。
※ レンダリングテンプレートの変更は、Columns プロパティに DataGridCellTemplate を割り当てることによって行う....のですが、現時点では Expression Blend からは設定できないかもしれません。(探してみたけど見当たらず....) XAML コードからであれば指定できるので、興味がある方はやってみてください。具体的なやり方は以下のページが参考になると思います。https://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
Anonymous
September 23, 2008
私が、細々と Silverlight のトピックを書いている間に、弊社のコンサルティングサービス部門の某コンサルタントが、網羅的な三部作を書き上げましたので、ご紹介します。 ・ Part 1. 最も簡単なAnonymous
September 20, 2010
The comment has been removed