Windows 8 アプリとデータベース/サービスとの連携 – ③ Windows ストア アプリとの連携(C#/XAML)

皆様、こんにちは! 引き続き、9/2(火) タッチ/ペン対応アプリの開発とクラウドを活用した既存サービスとの接続方法のセミナーでの、私が担当したセッション ”Windows 8 アプリでのデータベース/サービスとの接続 “ の詳細情報とコードにつき、ご紹介していきたいと思います。最後にリソースとしてソースコードの置場等を入れてあります。

Windows ストアアプリの追加と Data Adapter の作成

次に、このタイムセール商品情報アプリケーションのソリューションに、Windows ストアアプリを追加してみましょう。最初に、DataAdapter を作成します。これは、この前に作成した、TimeSalesApp Web API にアクセスするための呼び出しのHttpclient クラスとなります。

ストアアプリの追加

TimeSaleソリューションを右クリックし、追加→新しいプロジェクトを選択します。新しいプロジェクトダイアログボックスで、左側のテンプレートを展開し、Visual C#の Windows ストアアプリを選択し、右側の、新しいアプリケーション(XAML)を選択します。次のように指定してください。

・種類:新しいアプリケーション(XAML )(Visual C#

・場所:他のTimeSaleプロジェクトと同じソリューションフォルダ

・プロジェクト名:TimeSaleStoreApp

image

プロジェクトを追加したら、まずはローカルマシンで実行します。黒い画面が立ち上がったらいったんアプリを落とし、Visual Studio に戻ってデバッグを停止します。このときのMainPage.xaml 画面は使わずに削除して、その後、新しい画面を追加します。

TimeSaleStoreApp プロジェクトを右クリックし、追加→新しい項目を選択し、基本ページを選択して、名前を、MainPage.xaml として追加、をクリックします。

image

この時に、このようなダイアログボックスが出ますので、はい、を選んで追加します。

image

※ これをしないと画面遷移等に必要な共通ファイル群(Commonフォルダ配下)が作成されないので注意してください。この共通ファイル群が必要です。

ProductWithPriceをリンクとして追加

まず、TimeSale クラスライブラリの中の ProductwithPrice.cs クラスを参照できるようにします。ソリューションエクスプローラーでプロジェクト TimeSaleStoreAppクリックし、追加→既存の項目、を選択します。そして、TimeSale フォルダにある ProductwithPrice.cs ファイルを選択し、リンクとして追加、を選択します。

image

Json.NET のインストール

ついで、Json.NET のインストールを行います。Json.NETは、ProductWithPriceデータクラスのインスタンスと、JSONの相互変換処理をするためのライブラリです。こちらは、NuGet から取得できます。これを使うのはWindows 8/8.1 ストアアプリにも対応しているためです。実際、Microsoft Azure Mobile Services のクライアントライブラリには、最初から標準でこのライブラリが含まれています。

ソリューションエクスプローラーでプロジェクト TimeSaleStoreApp を右クリックし、NuGet パッケージの管理を選択します。左側のオンラインを展開し、Newtonsoft で検索、NuGet公式パッケージソースを選択し、右側で、Json.NET を選択して、インストールボタンをクリックします。これでこのプロジェクトでJson.NETが使えるようになります。

image

DataAdapter の作成

image

次に、HTTP Clientを使って、DataAdapter すなわち TimeSaleApp API の呼び出し部分のコードを作成します。ソリューションエクスプローラーでプロジェクト TimeSaleStoreAppクリックし、追加→クラスを選択します。名前は、ProductsDataAdapter.cs として、クラスを追加します。この ProductsDataAdapter.cs を下記の通り実装します。

    1: using TimeSale;
    2: using System;
    3: using System.Collections.Generic;
    4: using System.Linq;
    5: using System.Text;
    6: using System.Threading.Tasks;
    7: using System.Net.Http;
    8: using Newtonsoft.Json;
    9: using System.Net;
   10:  
   11: namespace TimeSaleStoreApp
   12: {
   13:     class ProductsDataAdapter
   14:     {
   15:         //Web API の URL (ポート番号は適宜変更してください)
   16:         const string serviceUri = "https://localhost:5922/api/TimeSaleApp";
   17:  
   18:         //全タイムセール対象商品の取得
   19:         public static async Task<List<ProductwithPrice>> GetAllProductsAsync()
   20:         {
   21:             //HttpClient インスタンスを生成
   22:             using (var client = new HttpClient())
   23:             {
   24:                 // GETコマンドを非同期で実行
   25:                 var response = await client.GetAsync(serviceUri);
   26:                 if (response.IsSuccessStatusCode)
   27:                 // コンテンツの取得を非同期で実行する
   28:                 {
   29:                     var jsonText = await response.Content.ReadAsStringAsync();
   30:                     // Json 文字列を List <ProductwithPrice>に変換して返す
   31:                     return
   32:                        JsonConvert.DeserializeObject<List<ProductwithPrice>>(jsonText);
   33:                 }
   34:                 return null;
   35:             }
   36:         }
   37:         // ProductwithPrice の取得
   38:         public static async Task<ProductwithPrice> GetProductwithPriceAsync(int id)
   39:         {
   40:             //HttpClientのインスタンスを作成する。
   41:             using (var client = new HttpClient())
   42:             {
   43:                 //GETコマンドを非同期で実行する。
   44:                 var response = await 
   45:                     client.GetAsync(string.Format("{0}/{1}", serviceUri, id));
   46:                 if (response.IsSuccessStatusCode)
   47:                 {
   48:                     //コンテンツの取得を非同期で実行
   49:                     var jsonText = await response.Content.ReadAsStringAsync();
   50:                     //json文字列を ProductwithPrice に変換して返す
   51:                     return JsonConvert.DeserializeObject<ProductwithPrice>(jsonText);
   52:                 }
   53:                 return null;
   54:             }
   55:         }
   56:  
   57:         // ProductwithPrice 内の検索
   58:         public static async Task<List<ProductwithPrice>> 
   59:                             SearchProductwithPriceAsync(string queryText)
   60:         {
   61:             //HttpClientのインスタンスを作成する。
   62:             using (var client = new HttpClient())
   63:             {
   64:                 //GETコマンドを非同期で実行する。
   65:                 var response = await client.GetAsync(string.Format("{0}?query={1}", 
   66:                             serviceUri, WebUtility.UrlEncode(queryText)));
   67:                 if (response.IsSuccessStatusCode)
   68:                 {
   69:                     //コンテンツの取得を非同期で実行
   70:                     var jsonText = await response.Content.ReadAsStringAsync();
   71:                     //json文字列を List<ProductwithPrice> に変換して返す
   72:                     return 
   73:             JsonConvert.DeserializeObject<List<ProductwithPrice>>(jsonText);
   74:                 }
   75:                 return null;
   76:             }
   77:         }
   78:  
   79:         // ProductwithPrice の追加
   80:         public static async Task<int> AddAsync(ProductwithPrice ProductwithPrice)
   81:         {
   82:             //HttpClientのインスタンスを作成する。
   83:             using (var client = new HttpClient())
   84:             {
   85:                 //ProductwithPriceのインスタンスを json の文字列に変換する。
   86:                 var jsonText = JsonConvert.SerializeObject(ProductwithPrice);
   87:                 //Web API での POST を非同期で実行
   88:                 var response = await 
   89:                     client.PostAsync(serviceUri, new StringContent(jsonText, 
   90:                                 Encoding.Unicode, "application/json"));
   91:                 if (response.IsSuccessStatusCode)
   92:                 {
   93:                     //戻ってきた値から json を取得
   94:                     var result = await response.Content.ReadAsStringAsync();
   95:                     //json 文字列から ProductwithPrice に変換
   96:                     var newProductwithPrice =
   97:                         JsonConvert.DeserializeObject<ProductwithPrice>(result);
   98:                     //id の値を元の ProductwithPrice に反映
   99:                     ProductwithPrice.StoreId = newProductwithPrice.StoreId;
  100:                 }
  101:                 return (int)response.StatusCode;
  102:             }
  103:         }
  104:  
  105:         // ProductwithPrice の削除
  106:         public static async Task<int> DeleteProductwithPrice(int id)
  107:         {
  108:             using (var client = new HttpClient())
  109:             {
  110:                 //DELETEコマンドを非同期で実行
  111:                 var url = string.Format("{0}/{1}", serviceUri, id);
  112:                 var response = await client.DeleteAsync(url);
  113:                 return (int)response.StatusCode;
  114:             }
  115:         }
  116:     }

ビルドして問題なければ、DataAdapter の作成は完了です。これで、Windows ストア アプリの作成 - Data Adapter の作成の箇所は終了です。

XAML の編集と GridView 追加・編集

imageアプリ名の変更

最初に、画面の左上に表示されるアプリ名の変更を行います。XAML エディタで MainPage.xamlを開くと、Visual Studio の左下のデザインタブと、XAML タブとで、表示を切り替えられます。ソリューションエクスプローラーで MainPage.xaml を開き、XAML タブをクリックすると、XAML のテキストが表示されますので、これを編集します。

MainPage.xaml の中の、My Application となっている箇所を、タイムセール商品管理に変更してみましょう。

    1: <Page.Resources>
    2:         <x:String x:Key="AppName">タイムセール商品管理</x:String>
    3: </Page.Resources>

ソリューションエクスプローラーで TimeSaleStoreApp を右クリックし、スタートアッププロジェクトに設定して、実行すると下記のようになります。

image

GridView の追加

商品情報リストを表示する部分を追加しましょう。GridViewコントロールは、データの集まりを、表形式で表示できる標準的なコントロールです。

MainPage.xaml 内のGridの定義の上に、下記のように1行追加します。

    1: ・・・
    2:  <!-- Back button and page title -->
    3: <GridView x:Name="ProductGridView" Margin="120,0,0,0" Grid.Row="1" />
    4: <Grid>
    5:             <Grid.ColumnDefinitions>
    6: ・・・

GridView  による商品情報リストの表示

この GridView を使って、商品情報リストを表示しましょう。

まずは、ソリューションエクスプローラーで、MainPage.xaml  を展開し、XAML のエディタの領域のどこかでクリックし、コードの表示を選択します。MainPage.xaml.cs 内の navigationHelper_LoadState メソッドを、下記のように実装し、メソッドの前に非同期呼び出しを意味する async を追加します。

    1: private async void navigationHelper_LoadState(object sender, LoadStateEventArgs e)
    2:  {
    3:      try  {
    4:         //全商品取得してそれをDataContextにする
    5:             DataContext = await ProductsDataAdapter.GetAllProductsAsync();
    6:     }
    7:     catch (Exception ex)  {
    8:                 var messageDialog = new Windows.UI.Popups.MessageDialog
    9:                 ("商品データが見つかりません。", "エラー");
   10:     }
   11: }

ついで、データを GridView にバインドします。これには、GridView の ItemsSource 属性でデータバインドを行います。GridView の ItemsSource に、MainPage.xaml.cs 内で DataContext に指定した商品情報のリストのデータバインドを行うことで、GridView での表示が可能となります。

 image

image

MainPage.xaml のGridView の定義を変更し、下記の通り、ItemsSource 属性を追加します。

    1: <GridView x:Name="ProductGridView" 
    2:                 Margin="120,0,0,0" Grid.Row="1" ItemsSource=”{Binding}” />

これでビルドして実行すると下記のようになります。

image

各商品の表示方法を指定していないため、このままだと、TimeSale.ProductwithPrice というクラス名がデータ件数文表示されてしまいます。そこで、表示方法を指定します。

GridView による商品情報リスト表示のカスタマイズ

この GridView に、ItemTemplate を追加することにより、各商品の表示方法をカスタマイズできます。MainPage.xaml の GridView の定義を、下記の通り変更します。

    1: <GridView x:Name="ProductGridView" Margin="120,0,0,0" Grid.Row="1"  
    2:                                     ItemsSource="{Binding}">
    3:     <GridView.ItemTemplate>
    4:         <DataTemplate>
    5:             <TextBlock Text="{Binding ProductName}" Height="250" Width="250"/>
    6:         </DataTemplate>
    7:     </GridView.ItemTemplate>
    8: </GridView>

これで、GridViewで表示される各商品データの商品名カラムのみがTextBlock で表示されます。再度実行すると、下記のようになります。

image

各商品の表示が変更されたことが分かります。もう少しきれいに成形してみます。下記のように DataTemplate の中にGrid を入れ、その中に TextBlock を配置する等、XAML を使って様々な要素を追加可能です。

    1: <GridView x:Name="ProductGridView" Margin="120,0,0,0" Grid.Row="1"  
    2:                                     ItemsSource="{Binding}">
    3:         <GridView.ItemTemplate>
    4:                 <DataTemplate>
    5:                     <Grid HorizontalAlignment="Left" Height="250" Width="250">
    6:                         <Border Background="Blue">
    7:                             <TextBlock Text="{Binding ProductName}" 
    8:                                     Height="250" Width="250"/>
    9:                         </Border>
   10:                         <Grid VerticalAlignment="Stretch">
   11:                             <Grid.ColumnDefinitions>
   12:                                 <ColumnDefinition Width="Auto"/>
   13:                                 <ColumnDefinition Width="*"/>
   14:                             </Grid.ColumnDefinitions>
   15:                             <Grid.RowDefinitions>
   16:                                 <RowDefinition Height="50"/>
   17:                                 <RowDefinition Height="50"/>
   18:                                 <RowDefinition Height="50"/>
   19:                                 <RowDefinition Height="50"/>
   20:                                 <RowDefinition Height="50"/>
   21:                             </Grid.RowDefinitions>
   22:                             <TextBlock Text="店舗名"
   23:                                        Style="{StaticResource 
   24:                                 CaptionTextBlockStyle}"
   25:                                        Margin="15,15,15,15"/>
   26:                             <TextBlock Grid.Column="1"
   27:                                        Text="{Binding StoreName}"
   28:                                        Style="{StaticResource TitleTextBlockStyle}"
   29:                                        Height="60" Margin="15,15,15,15"/>
   30:  
   31:                             <TextBlock Grid.Row="1" Text="売価"
   32:                                        Style="{StaticResource 
   33:                                     CaptionTextBlockStyle}"
   34:                                        Margin="15,15,15,15"/>
   35:                             <TextBlock Grid.Row="1" Grid.Column="1"
   36:                                        Text="{Binding RetailPrice}"
   37:                                        Style="{StaticResource TitleTextBlockStyle}"
   38:                                        Height="60" Margin="15,15,15,15"/>
   39:                             
   40:                             <TextBlock Grid.Row="2" Text="価格変更理由"
   41:                                        Style="{StaticResource 
   42:                                     CaptionTextBlockStyle}"
   43:                                        Margin="15,15,15,15"/>
   44:                             <TextBlock Grid.Row="2" Grid.Column="3"
   45:                                        Text="{Binding ReasonforChangePrice}"
   46:                                        Style="{StaticResource TitleTextBlockStyle}"
   47:                                        Height="60" Margin="15,15,15,15"/>
   48:                             
   49:                             <TextBlock Grid.Row="3" Text="売価変更時刻"
   50:                                        Style="{StaticResource 
   51:                                     CaptionTextBlockStyle}"
   52:                                        Margin="15,15,15,15"/>
   53:                             <TextBlock Grid.Row="3" Grid.Column="3"
   54:                                        Text="{Binding PriceFixedDate}"
   55:                                        Style="{StaticResource TitleTextBlockStyle}"
   56:                                        Height="60" Margin="15,15,15,15"/>
   57:  
   58:                             <TextBlock Grid.Row="4" Text="担当者"
   59:                                        Style="{StaticResource 
   60:                                     CaptionTextBlockStyle}"
   61:                                        Margin="15,15,15,15"/>
   62:                             <TextBlock Grid.Row="4" Grid.Column="4"
   63:                                        Text="{Binding Contact}"
   64:                                        Style="{StaticResource TitleTextBlockStyle}"
   65:                                        Height="60" Margin="15,15,15,15"/>
   66:                         </Grid>
   67:                     </Grid>
   68:                 </DataTemplate>
   69:             </GridView.ItemTemplate>
   70: </GridView>

これで再度実行すると、下記のようになります。

image

このように、GridView の ItemTemplate をカスタマイズすることで、表示方法をカスタマイズできます。

DataTemplate のリソース化

この<GridView.ItemTemplate> 配下の<DataTemplate>は、リソースとして、Grid の外においた方が、今後のメンテナンスにも便利です。そこで下記の通り、<DataTemplate>を、<Page.Resources>配下に移動します。このとき、x:Key 属性にキーを設定します。

    1: <Page.Resources>
    2:     <x:String x:Key="AppName">タイムセール商品管理</x:String>
    3:         <DataTemplate x:Key="ProductsTemplate">
    4:             <Grid HorizontalAlignment="Left" Height="250" Width="250">
    5:                 <Border Background="Blue">
    6:                     <TextBlock Text="{Binding ProductName}" Height="250" 
    7:                                         Width="250"/>
    8:                 </Border>
    9:                 <Grid VerticalAlignment="Stretch">
   10:                     <Grid.ColumnDefinitions>
   11:                         <ColumnDefinition Width="Auto"/>
   12:                         <ColumnDefinition Width="*"/>
   13:                     </Grid.ColumnDefinitions>
   14:                     <Grid.RowDefinitions>
   15:                         <RowDefinition Height="50"/>
   16:                         <RowDefinition Height="50"/>
   17:                         <RowDefinition Height="50"/>
   18:                         <RowDefinition Height="50"/>
   19:                         <RowDefinition Height="50"/>
   20:                     </Grid.RowDefinitions>
   21:                     <TextBlock Text="店舗名"
   22:                                        Style="{StaticResource 
   23:                                     CaptionTextBlockStyle}"
   24:                                        Margin="15,15,15,15"/>
   25:                     <TextBlock Grid.Column="1"
   26:                                        Text="{Binding StoreName}"
   27:                                        Style="{StaticResource TitleTextBlockStyle}"
   28:                                        Height="60" Margin="15,15,15,15"/>
   29:  
   30:                     <TextBlock Grid.Row="1" Text="売価"
   31:                                        Style="{StaticResource 
   32:                                     CaptionTextBlockStyle}"
   33:                                        Margin="15,15,15,15"/>
   34:                     <TextBlock Grid.Row="1" Grid.Column="1"
   35:                                        Text="{Binding RetailPrice}"
   36:                                        Style="{StaticResource TitleTextBlockStyle}"
   37:                                        Height="60" Margin="15,15,15,15"/>
   38:  
   39:                     <TextBlock Grid.Row="2" Text="価格変更理由"
   40:                                        Style="{StaticResource 
   41:                                     CaptionTextBlockStyle}"
   42:                                        Margin="15,15,15,15"/>
   43:                     <TextBlock Grid.Row="2" Grid.Column="3"
   44:                                        Text="{Binding ReasonforChangePrice}"
   45:                                        Style="{StaticResource TitleTextBlockStyle}"
   46:                                        Height="60" Margin="15,15,15,15"/>
   47:  
   48:                     <TextBlock Grid.Row="3" Text="売価変更時刻"
   49:                                        Style="{StaticResource 
   50:                                     CaptionTextBlockStyle}"
   51:                                        Margin="15,15,15,15"/>
   52:                     <TextBlock Grid.Row="3" Grid.Column="3"
   53:                                        Text="{Binding PriceFixedDate}"
   54:                                        Style="{StaticResource TitleTextBlockStyle}"
   55:                                        Height="60" Margin="15,15,15,15"/>
   56:  
   57:                     <TextBlock Grid.Row="4" Text="担当者"
   58:                                        Style="{StaticResource 
   59:                                     CaptionTextBlockStyle}"
   60:                                        Margin="15,15,15,15"/>
   61:                     <TextBlock Grid.Row="4" Grid.Column="4"
   62:                                        Text="{Binding Contact}"
   63:                                        Style="{StaticResource TitleTextBlockStyle}"
   64:                                        Height="60" Margin="15,15,15,15"/>
   65:                 </Grid>
   66:             </Grid>
   67:         </DataTemplate>
   68: </Page.Resources>

GridViewの方は、下記の通り、ItemTemplate にこのリソースのキーを指定するだけです。

    1: <GridView x:Name="ProductGridView" Margin="120,0,0,0" Grid.Row="1" 
    2:                   ItemsSource="{Binding}" 
    3: ItemTemplate="{StaticResource ProductsTemplate}"/>

これで実行しても、変更前と同じように表示されることが確認できます。

image

アプリバーの追加とCRUD 処理実装

AppBarの追加

このアプリで、データの表示(読み取り=R) はできるようになりました。それでは、データの新規作成(=C)、 そして削除(=D) もできるようにしましょう。また後述する、共有コントラクトを使った共有処理も、このAppBarのボタンからできるようにしておくと便利です。そこでまず、AppBarを追加しておきましょう。

AppBar の追加には、ドキュメントアウトラインウィンドウからドラッグ&ドロップで追加する方法や、Expression Blend で追加する方法、等GUIを使った方法もありますが、今回は構造を知るためと、各種指定のために、XAMLエディタに直接書き込んでみましょう。下記の通り、先ほどの<Page.Resources>の下に追加します。

    1: <Page.BottomAppBar>
    2:         <CommandBar>
    3:             <AppBarButton Name="addButton" Label="商品追加" Icon="Add" />
    4:             <AppBarButton Name="removeButton" Label="削除" Icon="Remove" />
    5:             <AppBarButton Name="shareButton" Label="共有" Icon="ReShare" />
    6:         </CommandBar>
    7:     </Page.BottomAppBar>

AppBar とAppBar ボタンがXAMLデザイナー画面に表示されるはずです。

image

これで実行するとこのようになります。

image

それぞれのボタンのイベントハンドラの実装

それでは、続いて、AppBarの各ボタンが押された時の処理を追加していきましょう。

まずは、XAMLデザイナー画面で、ドキュメントアウトラインウィンドウをポイントし、addButton を選択します。

image

プロパティウィンドウのタブを稲妻型のアイコンのイベントに切り替えます。そして、Click イベントの右のテキストボックスに、OnAddButtonClick と入力し、ダブルクリックします。

image

これでMainPage.xaml.cs 内にイベントハンドラが生成されます。

    1: private void OnAddButtonClick(object sender, RoutedEventArgs e)
    2: {
    3: }

同様に、removeButtonOnRemoveButtonClickshareButtonOnShareButtonClick を入力して、設定します。

追加用ユーザーコントロールの作成と商品情報の追加処理の実装(OnAddButtonClick

続いて、OnAddButtonClickのイベントハンドラ内の処理を記述します。まずは、追加用の簡単な入力フォームを、ユーザーコントロールとして追加します。

ソリューションエクスプローラーでプロジェクトTimeSaleStoreAppクリックし、追加→新しい項目を選択します。新しい項目の追加ダイアログボックスで、ユーザーコントロールを選択します。名前は、AddProductDialog.xaml として、追加ボタンをクリックします。

image

プロジェクト内に、AddProductDialog.xamlと、AddProductDialog.xaml.cs が追加されます。それぞれ、下記のように実装してください。

AddProductDialog.xaml

    1: <UserControl
    2:     x:Class="TimeSaleStoreApp.AddProductDialog"
    3:     xmlns="https://schemas.microsoft.com/winfx/2006/xaml/presentation"
    4:     xmlns:x="https://schemas.microsoft.com/winfx/2006/xaml"
    5:     xmlns:local="using:TimeSaleStoreApp"
    6:     xmlns:d="https://schemas.microsoft.com/expression/blend/2008"
    7:     xmlns:mc="https://schemas.openxmlformats.org/markup-compatibility/2006"
    8:     mc:Ignorable="d" Height="300" Width="600">
    9:  
   10:     <Grid Background="{StaticResource ApplicationPageBackgroundThemeBrush}">
   11:         <Grid.ColumnDefinitions>
   12:             <ColumnDefinition Width="Auto"/>
   13:             <ColumnDefinition Width="*"/>
   14:         </Grid.ColumnDefinitions>
   15:         <Grid.RowDefinitions>
   16:             <RowDefinition/>
   17:             <RowDefinition/>
   18:             <RowDefinition/>
   19:             <RowDefinition/>
   20:             <RowDefinition/>
   21:             <RowDefinition/>
   22:             <RowDefinition/>
   23:         </Grid.RowDefinitions>
   24:         <TextBlock>
   25:             <Run Text="  "/>
   26:             <Run Text="店舗名"/>
   27:         </TextBlock>
   28:         <TextBox Grid.Column="1"  x:Name="codeTextBox" Margin="5" />
   29:         <TextBlock Grid.Row="1" Text="商品名" Margin="5"/>
   30:         <TextBox Grid.Row="1" Grid.Column="1"  x:Name="nameTextBox" Margin="5"/>
   31:         <TextBlock Grid.Row="2" Text="売価" Margin="5"/>
   32:         <TextBox Grid.Row="2" Grid.Column="1"  x:Name="sellPriceTextBox" 
   33:                                         Margin="5"/>
   34:         <TextBlock Grid.Row="3" Text="価格変更理由" Margin="5"/>
   35:         <TextBox Grid.Row="3" Grid.Column="1" x:Name="priceChangeReason" 
   36:                                         Margin="5"/>
   37:         <TextBlock Grid.Row="4" Text="売価変更時刻" Margin="5"/>
   38:         <TextBox Grid.Row="4" Grid.Column="1" x:Name="priceChangeDate" Margin="5"/>
   39:         <TextBlock Grid.Row="5" Text="担当者" Margin="5"/>
   40:         <TextBox Grid.Row="5" Grid.Column="1" x:Name="Contact" Margin="5"/>
   41:         <StackPanel Grid.Row="2" Grid.ColumnSpan="2" Orientation="Horizontal" />
   42:         <Button x:Name="addButton" Content="追加" Click="addButton_Click" 
   43:                                 Margin="8,3,0,6" Grid.Row="6"/>
   44:         <Button x:Name="cancelButton" Content="キャンセル" Click="cancelButton_Click" 
   45:                             Margin="8,3,0,6" Grid.Column="1" Grid.Row="6"/>
   46:     </Grid>
   47: </UserControl>

・AddProductDialog.xaml.cs

    1: using TimeSale;
    2: using System;
    3: using System.Collections.Generic;
    4: using System.IO;
    5: using System.Linq;
    6: using System.Runtime.InteropServices.WindowsRuntime;
    7: using Windows.Foundation;
    8: using Windows.Foundation.Collections;
    9: using Windows.UI.Xaml;
   10: using Windows.UI.Xaml.Controls;
   11: using Windows.UI.Xaml.Controls.Primitives;
   12: using Windows.UI.Xaml.Data;
   13: using Windows.UI.Xaml.Input;
   14: using Windows.UI.Xaml.Media;
   15: using Windows.UI.Xaml.Navigation;
   16:  
   17: // ユーザー コントロールのアイテム テンプレートについては、https://go.microsoft.com/fwlink/?LinkId=234236 を参照してください
   18:  
   19: namespace TimeSaleStoreApp
   20: {
   21:     public sealed partial class AddProductDialog : UserControl
   22:     {
   23:         public AddProductDialog()
   24:         {
   25:             this.InitializeComponent();
   26:         }
   27:         public event Action<AddProductDialog, ProductwithPrice> ProductAddEnded;
   28:         private void addButton_Click(object sender, RoutedEventArgs e)
   29:         {
   30:             if (ProductAddEnded != null)
   31:                 // 新しい Product を作ってイベント ハンドラーに渡す
   32:                 ProductAddEnded(this, new ProductwithPrice
   33:                 {
   34:                     StoreName = codeTextBox.Text,
   35:                     ProductName = nameTextBox.Text,
   36:                     RetailPrice = sellPriceTextBox.Text,
   37:                     ReasonforChangePrice = priceChangeReason.Text,
   38:                     PriceChangedDate = DateTime.Now.ToString(),
   39:                     Contact = Contact.Text
   40:                 });
   41:         }
   42:         private void cancelButton_Click(object sender, RoutedEventArgs e)
   43:         {
   44:             if (ProductAddEnded != null)
   45:                 // キャンセルの場合はイベント ハンドラーに null を渡す
   46:                 ProductAddEnded(this, null);
   47:         }
   48:  
   49:     }
   50: }

OnAddButtonClick の実装

次に、MainPage.xaml.cs 内のOnAddButtonClick メソッドの実装をします。下記の通り、実装してください。

    1: private void OnAddButtonClick(object sender, RoutedEventArgs e) {
    2:             // ダイアログの作成
    3:             var addProductDialog = new AddProductDialog();
    4:             addProductDialog.ProductAddEnded += OnProductAddEnded;
    5:  
    6:             // ポップアップの作成
    7:             var popup = new Windows.UI.Xaml.Controls.Primitives.Popup
    8:             {
    9:                 Child = addProductDialog,
   10:                 IsLightDismissEnabled = true
   11:             };
   12:  
   13:             // サイズ変更時の調整
   14:             addProductDialog.SizeChanged += (dialogSender, dialogArgs) =>
   15:             {
   16:                 popup.VerticalOffset = this.ActualHeight
   17:                     - addProductDialog.ActualHeight
   18:                     - BottomAppBar.ActualHeight - 48;
   19:                 popup.HorizontalOffset = 48;
   20:             };
   21:             //Popup 開く
   22:             popup.IsOpen = true;
   23:         }
   24:  
   25:         async void OnProductAddEnded(AddProductDialog dialog,
   26:                                     TimeSale.ProductwithPrice productwithprice)
   27:         {
   28:             if (productwithprice != null)
   29:             {
   30:                 try
   31:                 {
   32:                     // 新しい商品を追加
   33:                     await ProductsDataAdapter.AddAsync(productwithprice);
   34:                     // データを取得し直して DataContext に設定
   35:                     DataContext = await ProductsDataAdapter.GetAllProductsAsync();
   36:                 }
   37:                 catch (Exception ex)
   38:                 {
   39:                     var messageDialog =
   40:                         new Windows.UI.Popups.
   41:                             MessageDialog("商品データが見つかりません。", 
   42:                                             "エラー");
   43:                     messageDialog.ShowAsync();
   44:                 }
   45:             }
   46:             // ポップアップを閉じる
   47:             ((Windows.UI.Xaml.Controls.Primitives.Popup)dialog.Parent).IsOpen = false;
   48:         }

これで実行し、Addボタンをクリックすると、下記のようになります。すべての項目(時刻以外)に入力してOKを押すと、SQL データベースにレコードが1件追加され、再度読み込まれます。

image

OnRemoveButtonClick の実装

同じく、MainPage.xaml.cs 内のOnRemoveButtonClickの実装を行います。async 属性をメソッドに付与したうえで、下記の通り実装してください。

    1: private async void OnRemoveButtonClick(object sender, RoutedEventArgs e)
    2:         {
    3:             var productwithprice = 
    4:                         (TimeSale.ProductwithPrice)ProductGridView.SelectedItem;
    5:             if (productwithprice != null)
    6:             {
    7:                 // 確認のメッセージを表示
    8:                 var messageDialog = new 
    9:                 Windows.UI.Popups.MessageDialog(string.Format("{0}:{1}を削除します。",
   10:                     productwithprice.StoreName, productwithprice.StoreName),
   11:                     "削除の確認");
   12:                 var okCommand = new Windows.UI.Popups.UICommand("OK");
   13:                 var cancelCommand = new Windows.UI.Popups.UICommand("キャンセル");
   14:                 messageDialog.Commands.Add(okCommand);
   15:                 messageDialog.Commands.Add(cancelCommand);
   16:                 var result = await messageDialog.ShowAsync();
   17:                 if (result.Equals(okCommand))
   18:                 {
   19:                     try
   20:                     {
   21:                         // 商品を削除
   22:                         await 
   23:                         ProductsDataAdapter.
   24:                             DeleteProductwithPrice(productwithprice.StoreId);
   25:                         // データを取得し直して DataContext に設定
   26:                         DataContext = await 
   27:                             ProductsDataAdapter.GetAllProductsAsync();
   28:                     }
   29:                     catch (Exception ex)
   30:                     {
   31:                         messageDialog =
   32:                             new Windows.UI.Popups.
   33:                                 MessageDialog("商品データが見つかりません。",
   34:                                          "エラー");
   35:                         messageDialog.ShowAsync();
   36:                     }
   37:                 }
   38:             }
   39: }

実行してみます。章:品を1件タップして選択し、AppBarをスワイプして表示します。Removeボタンをクリックすると、確認のダイアログが出ます。

image

その後、OKボタンを押すと、選択された商品がSQL データベースから1件削除され、再度読み込まれます。

共有コントラクト実装

Windows ストアアプリは、共有コントラクトに対応することにより、クリップボードを経由せずに他のアプリとデータを共有することができます。

OnShareButtonClick の実装

先ほど追加したAppBar の中のOnShareButtonClickのイベントハンドラを実装しましょう。MainPage.xaml.cs 内でこれを開き、using 句に

    1: using Windows.ApplicationModel.DataTransfer;

を追加したうえで、下記の通り実装します。

    1: private void OnShareButtonClick(object sender, RoutedEventArgs e)
    2: {
    3:     var dataTransferManager = DataTransferManager.GetForCurrentView();
    4:     // イベント ハンドラーの二重登録を防ぐため一度購読解除して登録し直す
    5:     dataTransferManager.DataRequested -= OnDataRequested;
    6:     dataTransferManager.DataRequested += OnDataRequested;
    7:     DataTransferManager.ShowShareUI();
    8: }

 

OnDataRequestedの実装

MainPage.xaml.cs 内にイベントハンドラとして、OnDataRequested が必要なので、こちらも実装します。

    1: void OnDataRequested(DataTransferManager sender, DataRequestedEventArgs args)
    2: {
    3:     // 選択中の商品データを ProductGridView から取得
    4: var productwithprice = (TimeSale.ProductwithPrice)(
    5:     ProductGridView.Visibility == Windows.UI.Xaml.Visibility.Visible
    6:                 ? ProductGridView.SelectedItem : ProductGridView.SelectedItem);
    7:             // 選択中の商品データがなければ何もしない
    8:             if (productwithprice == null)
    9:                 return;
   10:             args.Request.Data.Properties.Title =
   11:                 string.Format("{0}: {1}",
   12:                 productwithprice.StoreName, productwithprice.ProductName);
   13:             args.Request.Data.SetText("タイムセール商品情報について共有します。");
   14: }

これで実行してみましょう。AppBar を表示して共有ボタンを押します。画面の右側に共有ターゲットのストアアプリが並びます。

image

ここではテストのため、Windows8.1 SDK に入っている Share Target C# Sample を選択しています。下記の図のような画面になります。どのようなスキーマが対象となっているのかよくわかります。設定通り、商品データが選択されて入ればテストはクリアです。

image

もちろん、どんなマシンにもインストールされているWindows ストアアプリ “メール” でもOKです。

以上です。今回で、Windows 8アプリからデータベース/サービス連携のセッション解説は終了です。

リソース

ソースはこちらになります。ここに完成品のソリューション一式と、コードスニペットが入っていますので、ご参照ください。de:code イベントで参加者の方向けに、事前配布物としてZIPファイルで公開されたものです。

https://1drv.ms/1pn2gtY

なお、こちらをベースに、Microsoft Azure Mobile Services に対応させたのが、以前のエントリーでこちらになります。

https://blogs.msdn.com/b/shosuz/archive/2014/06/04/5-29-30-de-code-sv-007-microsoft-azure-mobile-services-2.aspx

併せて前後のエントリーもご参照ください。Microsoft Azure Mobile Services と .NET バックエンド使えば、このようなアプリが、さらに簡単に実装できることがよりよくお分かりいただけるかと思います。

 

次回は、またこの Microsoft Azure Mobile Services ネタになる予定です。それではまた(^^)/

鈴木 章太郎