ユーザー操作によるオプション設定を即座にデータに反映するには?[Win 8]:WinRT/Metro TIPS
エンド・ユーザーによる画面の変更をデータに反映させる方法を説明。さらに、その変更をほかの画面や設定ファイルに即座に反映させる方法も紹介。
powered by Insider.NET
前回までで紹介してきたように、データ・バインディングを使えば、データを画面に表示したり、データの変更を画面に反映したりできる。では逆に、エンド・ユーザーが画面を操作した結果をデータに反映できないだろうか?
そこで本稿では、アプリのオプションをユーザーが設定する画面を例にして、画面の変更をデータに反映させる方法を説明する。さらに、即座にその変更をほかの画面や設定ファイルに反映させる方法も紹介する。本稿のサンプルは「Windows Store app samples:MetroTips #38(Windows 8版)」からダウンロードできる。
なお、設定ファイルを読み書きする方法がWindowsストア・アプリとWindows Phone 8(以降、WP 8)アプリで大きく異なるため、WP 8での説明は割愛させていただくが、バインディングを利用して変更を反映させるという考え方は同じである。
●事前準備
Windows 8(以降、Win 8)向けのWindowsストア・アプリを開発するには、Win 8とVisual Studio 2012(以降、VS 2012)が必要である。これらを準備するには、第1回のTIPSを参考にしてほしい。本稿では64bit版Win 8 ProとVS 2012 Express for Windows 8を使用している。
●双方向バインディング
データ・バインディングには3種類のモードがあり、BindingMode列挙体(Windows.UI.Xaml.Data名前空間)の値で指定する(次の表)。データを画面に表示するだけの用途には、デフォルトの一方向バインディングでよい。今回の目的である画面の変更をデータに反映させるには、双方向バインディングを使う。なお、前回までバインディング・モードを指定してこなかったが、前回までは未指定のときの既定値、すなわち一方向バインディングが使われていたのだ。
モード | BindingMode列挙体の値 | 説明 |
---|---|---|
一方向 | OneWay | バインディング・ソースの変更がバインディング・ターゲットに反映される(既定値) |
1回 | OneTime | バインドされたときに1回だけソースからターゲットに反映される |
双方向 | TwoWay | ソースの変更がターゲットに反映されるだけでなく、ターゲットの変更がソースに反映される |
画面に表示するだけなら一方向バインディング、画面(バインディング・ターゲット)の変更をデータ(バインディング・ソース)に反映させるには双方向バインディングを使う。
●アプリケーション・データ・コンテナ
Windowsストア・アプリでは、アプリごとの設定はアプリケーション・データ記憶域*1の設定ファイルに保存する*2。
それには、Windows.Storage名前空間のApplicationDataContainerクラスを利用する。ApplicationDataContainerオブジェクトのValueプロパティを読み書きするだけで、自動的に設定ファイルの読み取り/更新を行ってくれるのだ。なお、設定ファイルには、ローカル(そのデバイス上でのみ有効)とローミング(ユーザーが所持する複数のデバイスで共有)の2種類がある。
*1 アプリケーション・データ記憶域: その実体は「\Users\{user}\AppData\Local\Packages\{app-id}\」フォルダに置かれている。
*2 設定の保存場所: 独自実装によってほかの場所に保存しても構わないが、アプリケーション・データ・コンテナを利用した実装の方がどれだけ楽かは、本稿のサンプル・コードを見ていただければ分かるだろう。
●オプション設定を即座に反映させるには?
- オプション設定データと設定画面を、双方向バインドする
- オプション設定データと通常の画面は、一方向バインドする
- オプション設定データのプロパティのバッキング・ストア*3は、アプリケーション・データ・コンテナにする
以上のように作ると、ユーザーがオプション設定画面で行った変更が、ほかの画面と設定ファイルに即座に反映されるようになる。これから本稿で作るサンプル・アプリの動作イメージは、次の図のようになる。
オプション設定が即座に反映される動作のイメージ
・ バインディング・ソースであるOptionSettingsオブジェクトのプロパティは、ApplicationDataContainerクラスを使って設定ファイルと同期が常に取られている(右上)
・ 設定画面とOptionSettingsオブジェクトは双方向バインディングで相互に値を反映している。例えば、設定画面で[背景色]を変更すると、OptionSettingsオブジェクトのBackgroundSettingプロパティに反映される(左下)
・ OptionSettingsオブジェクトの変更は、一方向バインディングされている他の画面に反映される。例えば、OptionSettingsオブジェクトのBackgroundSettingプロパティが変わると、実際の画面の背景色に反映される(右下)
*3 プロパティのバッキング・ストア: プロパティの実際の値を格納している場所。通常はprivateにしたメンバ変数。
●バインディング・ソースを作る
それでは、バインディング・ソースになるOptionSettingsクラスから作っていこう。まずクラスを作り、OptionSettingsクラスのインスタンスとApplicationDataContainerクラスのインスタンスを定義する。
using System.Collections.ObjectModel; // ObservableCollection
using System.Runtime.CompilerServices; // CallerMemberName
using Windows.Storage; // ApplicationDataContainer, ApplicationData
using Newtonsoft.Json; // JsonConvert(JSON.NET)
using System.Collections.Specialized; // NotifyCollectionChangedEventArgs
namespace MetroTips038CS
{
public class OptionSettings : Common.BindableBase
{
// プロセスで唯一のインスタンス
private static OptionSettings _thisInstance = new OptionSettings();
public static OptionSettings Current { get { return _thisInstance; } }
// ApplicationDataContainerのインスタンス
private ApplicationDataContainer _settings
= ApplicationData.Current.LocalSettings;
}
}
Imports System.Collections.ObjectModel ' ObservableCollection
Imports System.Runtime.CompilerServices ' CallerMemberName
Imports Windows.Storage ' ApplicationDataContainer, ApplicationData
Imports Newtonsoft.Json ' JsonConvert(JSON.NET)
Imports System.Collections.Specialized 'NotifyCollectionChangedAction
Public Class OptionSettings
Inherits Common.BindableBase
' プロセスで唯一のインスタンス
Private Shared _thisInstance As OptionSettings = New OptionSettings()
Public Shared ReadOnly Property Current As OptionSettings
Get
Return _thisInstance
End Get
End Property
' ApplicationDataContainerのインスタンス
Private _settings As ApplicationDataContainer _
= ApplicationData.Current.LocalSettings
End Class
※この時点ではまだコンパイルできない。
OptionSettingsクラスのインスタンスとApplicationDataContainerクラスのインスタンスを定義した。
なお、名前空間の後ろのコメントに、その名前空間から利用する主なクラスなどを掲載してある(これから利用するものも全て載せてある)。また、継承しているBindableBaseクラスについては、「WinRT/Metro TIPS:バインドするデータのPropertyChangedを楽に実装するには?[Win 8/WP 8]」を参照。
1つのアプリ内にアプリ設定を表すオブジェクトが複数存在しては困るので、OptionSettingsクラスのインスタンスを1つだけ静的メンバ変数に保持してそれを使うようにした。
また、上のコードでは、ローカルのアプリケーション・データ記憶域を使っている。これをローミングで使いたい場合は、「ApplicationData.Current.LocalSettings」を「ApplicationData.Current.RoamingSettings」に変えるとともに、ApplicationData.Current.DataChangedイベントで該当するプロパティのPropertyChangedイベントを発火させるようにする*4。
次に、_settingsメンバ変数(=ApplicationDataContainerのインスタンス)を使って設定ファイルのデータを読み書きするメソッドを作っておこう。どんな型のデータでも読み書きできるような汎用的なメソッドにする*5ために、NuGetからJson.NETをインストールして利用する。
*4 ローミングの場合: さらに、後述するバインディング・ソースのプロパティがコレクションの場合には、ApplicationData.Current.DataChangedイベントでコレクションを読み込み直す必要もある。
*5 設定ファイルに保存できる型: Windows Runtimeの基本データ型(文字列、数値、DateTime、PointやRectなど)は、そのまま設定ファイルに保存できる。また、MSDNのドキュメントには記載されていないが、それらの配列も保存できるようである。以上の型だけを設定ファイルに保存するならば、Json.NETでシリアライズする必要はない。
Json.NETをインストールするには、VS 2012のソリューション・エクスプローラでプロジェクトを選んでおいて、メニューバーから[プロジェクト]−[NuGet パッケージの管理]を選ぶ。すると、[NuGet パッケージの管理]ダイアログが表示されるので、左側のリストで[オンライン]を選び、右上の検索ボックスに「Json.NET」と入力する。中央に表示された検索結果に「Json.NET」があるので、それをクリックして選択すると表示される[インストール]ボタンをクリックすると、Json.NETがダウンロードされてインストールされる。なお、この作業は、プロジェクトごとに行う。
Json.NETのJsonConvertクラスを使うと、任意の型のオブジェクトをJSON形式の文字列にシリアライズ/デシリアライズできる。この機能を使って、設定ファイルのデータを読み書きする汎用的なメソッドを、次のようにしてOptionSettingsクラスに実装する。
…… 省略 ……
public class OptionSettings : Common.BindableBase
{
…… 省略 ……
// 設定ファイルを読み書きするための汎用メソッド(Json.NETを利用)
private T GetValue<T>(T defaultValue, [CallerMemberName] string key = "")
{
if (!_settings.Values.ContainsKey(key))
return defaultValue;
var json = (string)_settings.Values[key];
return JsonConvert.DeserializeObject<T>(json);
}
private void SetValue<T>(T newValue,
[CallerMemberName] string key = "")
{
var json = JsonConvert.SerializeObject(newValue);
_settings.Values[key] = json;
base.OnPropertyChanged(key);
}
}
…… 省略 ……
Public Class OptionSettings
Inherits Common.BindableBase
…… 省略 ……
'設定ファイルを読み書きするための汎用メソッド(Json.NETを利用)
Private Function GetValue(Of T)(defaultValue As T, _
<CallerMemberName> Optional key As String = "") As T
If (Not _settings.Values.ContainsKey(key)) Then
Return defaultValue
End If
Dim json = DirectCast(_settings.Values(key), String)
Return JsonConvert.DeserializeObject(Of T)(json)
End Function
Private Sub SetValue(Of T)(newValue As T, _
<CallerMemberName> Optional key As String = "")
Dim json = JsonConvert.SerializeObject(newValue)
_settings.Values(key) = json
MyBase.OnPropertyChanged(key)
End Sub
End Class
設定ファイルを読み書きするための汎用的なメソッドを追加した。値が変更されたときには、BindableBaseクラスのOnPropertyChangedメソッドを呼び出すことで、PropertyChangedイベントが発火するようになっている。 CallerMemberName属性については「WinRT/Metro TIPS:バインドするデータのPropertyChangedを楽に実装するには?[Win 8/WP 8]」を参照。
上で作った汎用的な読み書きメソッドを使って、バインディング・ソースとして使うプロパティを実装していく。プロパティのバッキング・ストアとして、メンバ変数ではなく、設定ファイルを使うようにするのだ。
まず、単純な型のプロパティから作ってみよう。文字列や数値などの単純な型の場合は、次のコードのように簡単に実装できる。
…… 省略 ……
public class OptionSettings : Common.BindableBase
{
…… 省略 ……
// 単純な値(string)
private const string BackgroundDefault = "DarkGoldenrod";
public string BackgroundSetting
{
get { return GetValue<string>(BackgroundDefault); }
set { SetValue<string>(value); }
}
// 単純な値(double)
private const double FontSizeDefault = 18.0;
public double FontSizeSetting
{
get { return GetValue<double>(FontSizeDefault); }
set { SetValue<double>(value); }
}
}
…… 省略 ……
Public Class OptionSettings
Inherits Common.BindableBase
…… 省略 ……
' 単純な値(string)
Private Const BackgroundDefault As String = "DarkGoldenrod"
Public Property BackgroundSetting As String
Get
Return GetValue(Of String)(BackgroundDefault)
End Get
Set(value As String)
SetValue(Of String)(value)
End Set
End Property
' 単純な値(double)
Private Const FontSizeDefault As Double = 18.0
Public Property FontSizeSetting As Double
Get
Return GetValue(Of Double)(FontSizeDefault)
End Get
Set(value As Double)
SetValue(Of Double)(value)
End Set
End Property
End Class
前述の汎用的な読み書きメソッドを使って、単純な型のプロパティを実装した。なお、この2つのプロパティは、前に掲載した図の設定画面にある[背景色]ドロップダウン・リストと[文字サイズ]のスライダにバインドすることになる。
上のコードでは、デフォルト値を定数としてハード・コーディングしている。これは、最初に起動されたときには設定ファイルが存在しないからだ。一度そのプロパティを変更して設定ファイルに保存された後は、このデフォルト値は無視され、設定ファイルから読み出した値が使われる。
さて、バインディング・ソースのプロパティがコレクションの場合はどうしたらいいだろう? 双方向バインディングしていても、コレクションに含まれる項目が変更されたときにプロパティのsetterは呼び出されないのだ。これを解決するには、次のようにする。
- バインドするコレクションにはObservableCollection<T>(System.Collections.ObjectModel名前空間)を使う
- ObservableCollection<T>の項目に変化があったときはCollectionChangedイベントが発生するので、そのイベント・ハンドラで設定ファイルに保存する
その実装は、次のコードのようになる。ここでは、2つあるテキスト・ブロックの表示に使用する文字色をコレクションとして管理している。
…… 省略 ……
public class OptionSettings : Common.BindableBase
{
…… 省略 ……
private OptionSettings()
{
this.FontColorsSetting.CollectionChanged
+= FontColorsSetting_CollectionChanged;
}
private const string FontColorsSettingPropertyName = "FontColorsSetting";
private ObservableCollection<string> FontColorsDefault
= new ObservableCollection<string> { "White", "LemonChiffon", };
private ObservableCollection<string> _fontColors;
public ObservableCollection<string> FontColorsSetting
{
get
{
if (_fontColors == null)
_fontColors = GetValue<ObservableCollection<string>>(
FontColorsDefault,
FontColorsSettingPropertyName
);
return _fontColors;
}
}
void FontColorsSetting_CollectionChanged(object sender,
NotifyCollectionChangedEventArgs e)
{
if (e.Action == NotifyCollectionChangedAction.Replace)
SetValue<ObservableCollection<string>>(_fontColors,
FontColorsSettingPropertyName);
}
}
…… 省略 ……
Public Class OptionSettings
Inherits Common.BindableBase
…… 省略 ……
Private Sub New()
AddHandler Me.FontColorsSetting.CollectionChanged, _
AddressOf FontColorsSetting_CollectionChanged
End Sub
Private Const FontColorsSettingPropertyName As String = "FontColorsSetting"
Private FontColorsDefault As ObservableCollection(Of String) _
= New ObservableCollection(Of String)({"White", "LemonChiffon"})
Private _fontColors As ObservableCollection(Of String)
Public ReadOnly Property FontColorsSetting As ObservableCollection(Of String)
Get
If (_fontColors Is Nothing) Then
_fontColors = GetValue(Of ObservableCollection(Of String))( _
FontColorsDefault, _
FontColorsSettingPropertyName)
End If
Return _fontColors
End Get
End Property
Private Sub FontColorsSetting_CollectionChanged(sender As Object, _
e As NotifyCollectionChangedEventArgs)
If (e.Action = NotifyCollectionChangedAction.Replace) Then
SetValue(Of ObservableCollection(Of String))( _
_fontColors, FontColorsSettingPropertyName)
End If
End Sub
End Class
コレクションのプロパティを実装した。CollectionChangedイベントを使って、設定の変更を設定ファイルに保存している。なお、このプロパティは、前に掲載した図の設定画面にある[文字色]の2つのドロップダウン・リストにバインドすることになる。
上のコードでは、まずコンストラクタで、CollectionChangedイベントにイベント・ハンドラを結び付けている。
プロパティのgetterでは、最初に呼び出されたときに既定値(FontColorsDefaultメンバ変数)か設定ファイルを使ってObservableCollectionクラスのインスタンスを作りメンバ変数_fontColorsに格納している。この_fontColorsメンバ変数をバッキング・ストアとしてgetterで公開する。
バインディング・ターゲットの側で_fontColorsメンバ変数の内容を変更する([文字色]ドロップダウン・リストで文字色を変更する)とCollectionChangedイベントが発生するので、イベント・ハンドラの中で_fontColorsメンバ変数の内容を設定ファイルに書き出している。ここで、コレクションを丸ごとJSONフォーマットにシリアライズして設定ファイルに格納しているため、コレクションの項目ごとに設定ファイルに書き出すことはできず、コレクションの全体を書き出さねばならない。また、そのために、_fontColorsメンバ変数にデータをキャッシュしておく必要も生じている。
なお、一般的には、設定項目をコレクションのインデックスで区別するのは、対応関係が分かりにくくなるので、よくないとされている。コードの作り勝手からも、このようにとても面倒になる。
●設定画面にバインドする
それでは、設定画面を作って、上で作成したOptionSettingsクラスのインスタンスと双方向バインディングしてみよう。
本来は、設定画面はユーザー・コントロールとして実装し、設定チャームのイベントで右端からポップアップとして表示させるべきだが、本稿では説明のために簡略化してメイン画面の一部を間借りする形にする。
前に掲載した図にある設定画面は、次のコードのように定義される。
<Frame x:Name="frameSettings" Grid.Column="1"
Background="LightGray" Foreground="Black" FontSize="24" Padding="10">
<StackPanel>
<TextBlock Text="設定" FontSize="30" />
<TextBlock Text="背景色" Margin="0,10,0,0" />
<ComboBox SelectedValuePath="Content"
SelectedValue="{Binding BackgroundSetting, Mode=TwoWay}"
Background="{Binding SelectedItem.Background, RelativeSource={RelativeSource Mode=Self}}"
Foreground="{Binding SelectedItem.Foreground, RelativeSource={RelativeSource Mode=Self}}"
>
<ComboBoxItem Content="DarkRed" Background="DarkRed" Foreground="White" />
<ComboBoxItem Content="DarkGoldenrod" Background="DarkGoldenrod" Foreground="Black" />
<ComboBoxItem Content="DarkGreen" Background="DarkGreen" Foreground="White" />
</ComboBox>
<StackPanel Margin="0,20,0,0" Orientation="Horizontal">
<TextBlock Text="文字サイズ:" />
<TextBlock Text="{Binding Value, ElementName=slider1}" Margin="5,0,0,0" />
</StackPanel>
<Slider x:Name="slider1" Grid.Column="1" Minimum="9" Maximum="90"
Background="GreenYellow" Padding="0,-10,0,-20"
Value="{Binding FontSizeSetting, Mode=TwoWay}" />
<TextBlock Text="文字色" Margin="0,10,0,0" />
<StackPanel Orientation="Horizontal">
<TextBlock Text="[0]" Margin="0,0,0,0" />
<ComboBox Width="90"
SelectedValuePath="Content"
SelectedValue="{Binding FontColorsSetting[0], Mode=TwoWay}"
Background="{Binding SelectedItem.Background, RelativeSource={RelativeSource Mode=Self}}"
Foreground="{Binding SelectedItem.Foreground, RelativeSource={RelativeSource Mode=Self}}"
>
<ComboBoxItem Content="White" Background="White" Foreground="Black" />
<ComboBoxItem Content="Black" Background="Black" Foreground="White" />
</ComboBox>
<TextBlock Text="[1]" Margin="10,0,0,0" />
<ComboBox Width="140"
SelectedValuePath="Content"
SelectedValue="{Binding FontColorsSetting[1],Mode=TwoWay}"
Background="{Binding SelectedItem.Background, RelativeSource={RelativeSource Mode=Self}}"
Foreground="{Binding SelectedItem.Foreground, RelativeSource={RelativeSource Mode=Self}}"
>
<ComboBoxItem Content="LemonChiffon" Background="LemonChiffon" Foreground="Black" />
<ComboBoxItem Content="DarkSlateBlue" Background="DarkSlateBlue" Foreground="White" />
</ComboBox>
</StackPanel>
</StackPanel>
</Frame>
Frameコントロールの内側部分のみ抜粋した(詳細は公開するソース・コードを参照してほしい)。「WinRT/Metro TIPS:コントロール同士をデータ・バインドするには?[Win 8/WP 8]」で解説したコントロールのプロパティへのバインディングも、有効に使っている。
上のコードでは、やはり説明を簡略にするため、ComboBoxItemオブジェクトをハード・コーディングしている。本来ならば、こういった選択肢もOptionSettingsクラスに定義しておいて、バインドするとよい。OptionSettingsクラスを修正するだけで、画面も設定ファイルも変更されるからだ。
それぞれのSliderコントロールやComboBoxコントロールは、データ・コンテキストと双方向バインドしている。「Mode=TwoWay」となっているところだ。
次に、このコードのトップレベルのFrameコントロール(名前は「frameSettings」)のデータ・コンテキストに、OptionSettingsクラスのインスタンスをセットする。次のコードのようにして、コードビハインドで行う。
protected override void LoadState(Object navigationParameter, Dictionary<String, Object> pageState)
{
// 設定画面のDataContextを設定
this.frameSettings.DataContext = OptionSettings.Current;
}
Protected Overrides Sub LoadState(navigationParameter As Object, pageState As Dictionary(Of String, Object))
' 設定画面のDataContextを設定
Me.frameSettings.DataContext = OptionSettings.Current
End Sub
これはLayoutAwarePageを継承してページクラスを作成した場合。そうでない場合は、OnNavigatedToメソッドに記述する。
<Frame x:Name="frameContent" Padding="120,10,10,10"
Background="{Binding BackgroundSetting}"
FontSize="{Binding FontSizeSetting}"
>
<StackPanel>
<TextBlock Text="1行目の文字列です。文字色[0]が適用されます。"
TextWrapping="Wrap"
Foreground="{Binding FontColorsSetting[0]}" />
<TextBlock Text="2行目の文字列です。文字色[1]が適用されます。"
TextTrimming="WordEllipsis"
Foreground="{Binding FontColorsSetting[1]}" />
</StackPanel>
</Frame>
Frameコントロールの内側部分のみ抜粋した(詳細は公開するソース・コードを参照してほしい)。Frameコントロールの背景色には、OptionSettingsクラスのBackgroundSettingプロパティをバインドした。FrameコントロールのフォントサイズはFontSizeSettingプロパティに。また、1つ目のTextBlockコントロールの前景色はFontColorsSettingプロパティのインデックス0番の値に、2つ目は同じくインデックス1番の値にバインドされている。
このコードのトップレベルのFrameコントロール(名前は「frameContent」)のデータ・コンテキストにも、OptionSettingsクラスのインスタンスをセットする。次のコードのようにして、やはりコードビハインドで行う。
protected override void LoadState(Object navigationParameter, Dictionary<String, Object> pageState)
{
// 設定を適用する画面のDataContextを設定
this.frameContent.DataContext = OptionSettings.Current;
…… 省略 ……
}
Protected Overrides Sub LoadState(navigationParameter As Object, pageState As Dictionary(Of String, Object))
' 設定を適用する画面のDataContextを設定
Me.frameContent.DataContext = OptionSettings.Current
…… 省略 ……
End Sub
これはLayoutAwarePageを継承してページクラスを作成した場合。そうでない場合は、OnNavigatedToメソッドに記述する。
以上で完成だ。実行してみると次の画像のようになる。
右側の「設定画面」を操作すると、即座に左側の画面の背景色/文字サイズ/文字色が変化する。また、その時点で設定ファイルにも書き出されており、次に起動したときには設定ファイルから読み出されたデータでOptionSettingsクラスの各プロパティが初期化されるため、画面の状態が復元される。
●まとめ
双方向バインディングを使うことで、ユーザーによる設定変更操作を即座に画面に反映させられる。また、バインディング・ソースのバッキング・ストアにApplicationDataContainerクラスを利用することで、設定の変更を直ちに設定ファイルに保存できる。
なお、アプリの設定に関する情報やJson.NETのドキュメントは、次のURLを参照してほしい。
- MSDN:Windows ランタイムを使ったアプリ データへのアクセス
- MSDN:クイック スタート: ローカル アプリケーション データ
- MSDN:クイック スタート: アプリケーション データのローミング
- MSDN:アプリ設定の追加
- MSDN:Windows Phone の設定ページを作成する方法
- Json.NETのドキュメント(英語)
また、データ・バインドに関してこれまでに説明してきたことは、本稿では解説していない。必要に応じて以下のバックナンバーも参照してほしい。
- WinRT/Metro TIPS:文字列をコントロールにバインドするには?[Win 8/WP 8](2013/04/04)
- WinRT/Metro TIPS:文字列以外の値をコントロールにバインドするには?[Win 8/WP 8](2013/04/11)
- WinRT/Metro TIPS:バインドするデータのPropertyChangedを楽に実装するには?[Win 8/WP 8](2013/04/18)
- WinRT/Metro TIPS:デザイン画面でデータをバインドするには?[Win 8/WP 8](2013/04/25)
- WinRT/Metro TIPS:コントロール同士をデータ・バインドするには?[Win 8/WP 8](2013/05/09)
- WinRT/Metro TIPS:データ・コレクションをバインドするには?[Win 8/WP 8](2013/05/16)
- WinRT/Metro TIPS:複雑なデータをバインドするには?[Win 8/WP 8](2013/05/23)
Copyright© Digital Advantage Corp. All Rights Reserved.