方法: Windows Phone 用の MVVM でローカル データベース アプリケーションを作成する
2012/02/09
Windows Phone OS 7.1 では、アプリケーションの分離ストレージ コンテナー内に存在するローカル データベースにリレーショナル データを格納できます。このトピックでは、ローカル データベースを使用する複数ページの To Do リスト アプリケーションを作成する方法を説明します。このアプリケーションは、「方法: Windows Phone 用の基本的なローカル データベース アプリケーションを作成する」で取り上げられているアプリケーションと似ていますが、追加で Model-View-ViewModel (MVVM) パターンと Pivot コントロールの使用方法、Silverlight for Windows Phone Toolkit、データベースの関連付けを示します。Windows Phone アプリケーションでのローカル データベースの使用の詳細については、「Windows Phone のローカル データベースの概要」を参照してください。
ヒント: |
|---|
このトピックはローカル データベースのサンプルに対応しています。完全なプロジェクトをダウンロードするには、「Windows Phone のコード サンプル」を参照してください。 |
このトピックでは、次の主な手順について説明します。
次のイメージに、アプリケーション ピボット ページと新しいタスク ページを示します。

プロセス中、次のコード ファイルを変更または作成します。
MainPage.xaml: アプリケーションのメイン ページを変更して、all、home、work、hobbies のカテゴリでグループ化された To Do タスクを表示する Pivot コントロールを追加します。
NewTaskPage.xaml: このページを追加して、新しいタスクをデータベースに追加するための UI を提供します。このページでは Silverlight Toolkit ListPicker コントロールを使用して、タスクに関連付けられたカテゴリを指定しています。
Model\ToDoDataContext.cs: このクラス ファイルを作成して、LINQ to SQL データ コンテキストとローカル データベースを表すオブジェクト モデルを指定します。MVVM に関して、このクラスはデータ モデルです。
ViewModel\ToDoViewModel.cs: このクラス ファイルを作成して、アプリケーションの ViewModel を表します。このクラスには、カテゴリ別に To Do タスクをグループ化するための複数の監視可能なコレクションが含まれます。ViewModel はデータベースへの追加および削除操作を実行するメソッドも提供します。
App.xaml.cs: このファイルを変更して、アプリケーション全体でアクセス可能なローカル データベースと静的 ViewModel を作成します。
MainPage.xaml.cs: このページを変更して、ページ DataContext プロパティをアプリケーション ViewModel に設定し、[add] および [delete] ボタンのクリックを処理します。
NewTaskPage.xaml.cs: このページを変更して、ページ DataContext プロパティをアプリケーション ViewModel に設定し、[ok] および [cancel] ボタンのクリックを処理します。
注: |
|---|
次の手順は、Visual Studio 2010 Express for Windows Phone 向けです。 Visual Studio 2010 Professional や Visual Studio 2010 Ultimate のアドインを使用している場合は、メニュー コマンドやウィンドウのレイアウトが多少異なる場合があります。 |
このセクションでは、アプリケーションを作成し、Silverlight for Windows Phone Toolkit をインストールして、アセンブリ参照を設定し、アプリケーションで使用するアイコンを追加します。
アプリケーションを作成するには
Visual Studio 2010 Express for Windows Phone で、[ファイル] メニューの [新しいプロジェクト] をクリックして新しいプロジェクトを作成します。
[新しいプロジェクト] ウィンドウが表示されます。Visual C# のテンプレートを展開してから、Silverlight for Windows Phone のテンプレートを選択します。
Windows Phone アプリケーション テンプレートを選択します。[名前] ボックスに選択した名前を入力します。
[OK] をクリックします。[新しい Windows Phone アプリケーション] ウィンドウが表示されます。
[ターゲットの Windows Phone のバージョン] メニューで、Windows Phone 7.1 が選択されていることを確認します。
[OK] をクリックします。新しいプロジェクトが作成され、Visual Studio のデザイナー ウィンドウに MainPage.xaml が表示されます。
Silverlight for Windows Phone Toolkit をインストールするには
Silverlight for Windows Phone Toolkit は、Windows Phone SDK 7.1 には含まれていません。Codeplex の Silverlight Toolkit Web サイトから個別にツールキットをダウンロードする必要があります。その Web サイトに移動し、Silverlight for Windows Phone Toolkit のリンクをクリックします。
Silverlight for Windows Phone Toolkit の Web ページで .msi というファイル名拡張子を持つリンクをクリックして、ツールキットをインストールします。
ツールキットのインストール後、ツールキット バイナリのパスを確認します。Windows 7 で Windows エクスプローラーのフォルダーに移動するには、[スタート]、[すべてのプログラム]、[Microsoft Silverlight for Windows Phone Toolkit]、[バイナリ] をクリックします。
アセンブリ参照を設定するには
Visual Studio で、プロジェクトに Silverlight for Windows Phone Toolkit アセンブリを追加します。[プロジェクト] メニューの [参照の追加] をクリックします。
[参照の追加] ウィンドウの [参照] タブをクリックし、Silverlight for Windows Phone Toolkit バイナリが格納されているフォルダーに移動します。
Microsoft.Phone.Controls.Toolkit.dll という名前のアセンブリを選択し、[OK] をクリックします。ソリューション エクスプローラーの [References] フォルダー内に、Microsoft.Phone.Controls.Toolkit アセンブリが表示されます。
このアプリケーションには Windows Phone の LINQ to SQL アセンブリへの参照が必要です。[プロジェクト] メニューの [参照の追加] をクリックし、[.NET] タブの [System.Data.Linq] を選択して、[OK] をクリックします。
Pivot コントロールを使用するため、このアプリケーションでは、Windows Phone Controls アセンブリへの参照が必要です。[プロジェクト] メニューの [参照の追加] をクリックし、[.NET] タブの [Microsoft.Phone.Controls] を選択し、[OK] をクリックします。
アプリケーションで使用するアイコンを追加するには
ソリューション エクスプローラーで、プロジェクトを右クリックし、[追加] を選択して、[新しいフォルダー] をクリックします。
新しいフォルダーに Images という名前を付けます。
ソリューション エクスプローラーで Images フォルダーを右クリックし、[追加] を選択して [既存の項目] を選択します。これにより、[既存の項目の追加」メニューが開き、アプリケーションで使用するアイコンを選択できます。
[既存の項目の追加] ウィンドウで、次のいずれかのパスに移動して、アイコンを選択します。この手順では、Visual Studio の既定のインストールを想定しています。別の場所にインストールしている場合は、対応する場所でアイコンを見つけます。
64 ビット オペレーティング システム: C:\Program Files (x86)\Microsoft SDKs\Windows Phone\v7.1\Icons\dark
32 ビット オペレーティング システム: C:\Program Files\Microsoft SDKs\Windows Phone\v7.1\Icons\dark
フォルダーから、次のアイコンを選択します。
appbar.add.rest.png
appbar.cancel.rest.png
appbar.check.rest.png
appbar.delete.rest.png
これらのアイコンは、暗い背景用に設計されているため、白色にされています。[既存の項目の追加] ウィンドウの白の背景では、アイコンが空白に見えることがあります。
ソリューション エクスプローラーで、各アイコンを右クリックし、アイコンが [コンテンツ] として構築され、常に出力ディレクトリにコピーされる ([常にコピーする]) ように、ファイルのプロパティを設定します。
このセクションでは、このアプリケーションのメイン ページと新しいタスク ページの UI を作成します。MVVM に関して、アプリケーションのページはビューです。メイン ページでは、データを表示するための Pivot コントロールの使用方法を示します。新しいタスク ページでは、データを選択するための Silverlight for Windows Phone Toolkit の ListPicker コントロールの使用方法を示します。
メイン ページ UI を作成するには
アプリケーションのメイン ページ MainPage.xaml で、ページの上部の phone:PhoneApplicationPage 要素に、次の属性を追加します。
xmlns:controls="clr-namespace:Microsoft.Phone.Controls;assembly=Microsoft.Phone.Controls"
Pivot コントロールを使用するには、この名前空間が必要です。
MainPage.xaml で、LayoutRoot という名前のグリッドの上に、次のリソース要素を追加します。
<phone:PhoneApplicationPage.Resources> <DataTemplate x:Key="ToDoListBoxItemTemplate"> <Grid HorizontalAlignment="Stretch" Width="420"> <Grid.ColumnDefinitions> <ColumnDefinition Width="100" /> <ColumnDefinition Width="*" /> <ColumnDefinition Width="Auto" /> <ColumnDefinition Width="100" /> </Grid.ColumnDefinitions> <CheckBox IsChecked="{Binding IsComplete, Mode=TwoWay}" Grid.Column="0" VerticalAlignment="Top"/> <TextBlock Text="{Binding ItemName}" FontSize="{StaticResource PhoneFontSizeLarge}" Grid.Column="1" Grid.ColumnSpan="2" VerticalAlignment="Top" Margin="-36, 12, 0, 0"/> <Button Grid.Column="3" x:Name="deleteTaskButton" BorderThickness="0" Margin="0, -18, 0, 0" Click="deleteTaskButton_Click"> <Image Source="/Images/appbar.delete.rest.png" Height="75" Width="75"/> </Button> </Grid> </DataTemplate> </phone:PhoneApplicationPage.Resources>この項目テンプレートは、各ピボット ページで、ローカル データベースから 1 行のデータ、To Do タスクを表示するために再利用します。各行には、タスクを完了とマークするための CheckBox、To Do タスク テキストを表示するための TextBlock、タスクを削除できるようにするための Button が含まれます。CheckBox はタスクの IsCompleted プロパティへの双方向バインディングで構成されます。UI のタスクにチェックマークを付けると、バインドされている ViewModel の対応する監視可能コレクションによって、新しい値が自動的にキャプチャされます。Textblock は、タスクの ItemName プロパティにバインドされています。
MainPage.xaml 内で、LayoutRoot という名のグリッドを以下のコードで置き換えます。このコードは、前の手順で追加したリソース要素の下に追加する必要があります。
<!--LayoutRoot is the root grid where all page content is placed.--> <Grid x:Name="LayoutRoot" Background="Transparent"> <Grid.RowDefinitions> <RowDefinition Height="Auto"/> <RowDefinition Height="*"/> </Grid.RowDefinitions> <!--TitlePanel contains the name of the application and page title.--> <StackPanel x:Name="TitlePanel" Grid.Row="0" Margin="12,17,0,28"> <TextBlock x:Name="ApplicationTitle" Text="LOCAL DATABASE EXAMPLE: TO-DO LIST" Style="{StaticResource PhoneTextNormalStyle}"/> </StackPanel> <!--ContentPanel - place additional content here.--> <Grid x:Name="ContentPanel" Grid.Row="1" Margin="12,0,12,0"> <controls:Pivot Margin="0, -36, 0, 0"> <controls:PivotItem Header="all"> <ListBox x:Name="allToDoItemsListBox" ItemsSource="{Binding AllToDoItems}" Margin="12, 0, 12, 0" Width="440" ItemTemplate="{StaticResource ToDoListBoxItemTemplate}" /> </controls:PivotItem> <controls:PivotItem Header="home"> <ListBox x:Name="homeToDoItemsListBox" ItemsSource="{Binding HomeToDoItems}" Margin="12, 0, 12, 0" Width="440" ItemTemplate="{StaticResource ToDoListBoxItemTemplate}" /> </controls:PivotItem> <controls:PivotItem Header="work"> <ListBox x:Name="workToDoItemsListBox" ItemsSource="{Binding WorkToDoItems}" Margin="12, 0, 12, 0" Width="440" ItemTemplate="{StaticResource ToDoListBoxItemTemplate}" /> </controls:PivotItem> <controls:PivotItem Header="hobbies"> <ListBox x:Name="hobbiesToDoItemsListBox" ItemsSource="{Binding HobbiesToDoItems}" Margin="12, 0, 12, 0" Width="440" ItemTemplate="{StaticResource ToDoListBoxItemTemplate}" /> </controls:PivotItem> </controls:Pivot> </Grid> </Grid>このグリッドはアプリケーション タイトルと Pivot コントロールを指定します。Pivot コントロールには、To Do タスクに割り当て可能な 4 つの各カテゴリに対応する all、home、work、hobbies の 4 種類のページが含まれます。これらの各ページは、ViewModel の AllToDoItems、HomeToDoItems、WorkToDoItems、HobbiesToDoItems の監視可能コレクションにそれぞれバインドされています。
MainPage.xaml で、LayoutRoot というグリッドの下に次のコードを追加します。
<phone:PhoneApplicationPage.ApplicationBar> <shell:ApplicationBar IsVisible="True" IsMenuEnabled="True"> <shell:ApplicationBarIconButton IconUri="/Images/appbar.add.rest.png" Text="add" x:Name="newTaskAppBarButton" Click="newTaskAppBarButton_Click"/> </shell:ApplicationBar> </phone:PhoneApplicationPage.ApplicationBar>このページは、アプリケーション バーを使用して、データベースに To Do タスクを追加するための add ボタンを表示します。このボタンに対応するメソッドはこのトピックの後で作成します。
新しいタスク ページ UI を作成するには
ソリューション エクスプローラーで、プロジェクトを右クリックし、[追加] を選択して、[新しい項目] をクリックします。
[新しい項目の追加] ウィンドウで、[Windows Phone Portrait Page] を選択し、ファイルに NewTaskPage.xaml という名前を付けます。[追加] をクリックします。
NewTaskPage.xaml で、ページの上部の phone:PhoneApplicationPage 要素に次の属性を追加します。
xmlns:toolkit="clr-namespace:Microsoft.Phone.Controls;assembly=Microsoft.Phone.Controls.Toolkit"
Silverlight for Windows Phone Toolkit の ListPicker コントロールを使用するために、この名前空間が必要です。
NewTaskPage.xaml で、LayoutRoot という名前のグリッドを次のコードで置き換えます。
<!--LayoutRoot is the root grid where all page content is placed.--> <Grid x:Name="LayoutRoot" Background="Transparent"> <Grid.RowDefinitions> <RowDefinition Height="Auto"/> <RowDefinition Height="*"/> </Grid.RowDefinitions> <!--TitlePanel contains the name of the application and page title.--> <StackPanel x:Name="TitlePanel" Grid.Row="0" Margin="12,17,0,28"> <TextBlock x:Name="ApplicationTitle" Text="LOCAL DATABASE EXAMPLE: TO-DO LIST" Style="{StaticResource PhoneTextNormalStyle}"/> <TextBlock x:Name="PageTitle" Text="new task" Margin="9,-7,0,0" Style="{StaticResource PhoneTextTitle1Style}"/> </StackPanel> <!--ContentPanel - place additional content here.--> <StackPanel x:Name="ContentPanel" Grid.Row="1" Margin="12,0,12,0"> <TextBlock Text="Name"/> <TextBox x:Name="newTaskNameTextBox"/> <TextBlock Text="Category"/> <toolkit:ListPicker x:Name="categoriesListPicker" ItemsSource="{Binding CategoriesList}" DisplayMemberPath="Name"> </toolkit:ListPicker> </StackPanel> </Grid>このグリッドには 2 つの StackPanel コントロールが含まれます。最初のコントロールはアプリケーションとページのタイトルを指定します。2 つ目の StackPanel には、新しい To Do タスクのテキストを入力するための TextBox と、タスクのカテゴリを指定するための ListPicker コントロールのデータ入力コントロールが含まれます。ListPicker は、種類 List の ViewModel プロパティ CategoriesList にバインドされています。
NewTaskPage.xaml で、LayoutRoot というグリッドの下に次のコードを追加します。
<phone:PhoneApplicationPage.ApplicationBar> <shell:ApplicationBar IsVisible="True" IsMenuEnabled="True"> <shell:ApplicationBarIconButton x:Name="appBarOkButton" IconUri="/Images/appbar.check.rest.png" Text="ok" Click="appBarOkButton_Click"/> <shell:ApplicationBarIconButton x:Name="appBarCancelButton" IconUri="/Images/appbar.cancel.rest.png" Text="cancel" Click="appBarCancelButton_Click"/> </shell:ApplicationBar> </phone:PhoneApplicationPage.ApplicationBar>このページはアプリケーション バーを使用して、ok ボタンと cancel ボタンを表示します。これらのボタンに対応するメソッドはこのトピックの後で作成します。
このセクションでは、LINQ to SQL データ コンテキストと、データベースのテーブルおよび関連付けを表すオブジェクトを作成します。まず、ファイルを作成し、各テーブルのテンプレートを追加します。次に、各テーブルを構築し、データ コンテキストを作成します。
データ モデル ファイルを準備するには
ソリューション エクスプローラーで、プロジェクトを右クリックし、[追加] を選択して、[新しいフォルダー] をクリックします。
新しいフォルダーに Model という名前を付けます。
ソリューション エクスプローラーの Model フォルダーを右クリックし、[追加] を選択して [新しい項目] を選択します。
[新しい項目の追加] ウィンドウで、[コード ファイル] を選択し、ファイルに ToDoDataContext.cs という名前を付けます。[追加] をクリックします。
ToDoDataContext.cs で、次のディレクティブとデータ モデル クラスを含める名前空間を追加します。
using System; using System.ComponentModel; using System.Data.Linq; using System.Data.Linq.Mapping; namespace LocalDatabaseSample.Model { }
ToDoDataContext.cs で、LocalDatabaseSample.Model 名前空間に次のコードを 2 回追加します。これらのクラスは、テーブルごとに次の手順で名前を変更します。
注:次の手順でクラスの名前を変更するまで、Visual Studio では不明なエラーが表示されます。
[Table] public class AddTableNameHere : INotifyPropertyChanged, INotifyPropertyChanging { // // TODO: Add columns and associations, as applicable, here. // // Version column aids update performance. [Column(IsVersion = true)] private Binary _version; #region INotifyPropertyChanged Members public event PropertyChangedEventHandler PropertyChanged; // Used to notify that a property changed private void NotifyPropertyChanged(string propertyName) { if (PropertyChanged != null) { PropertyChanged(this, new PropertyChangedEventArgs(propertyName)); } } #endregion #region INotifyPropertyChanging Members public event PropertyChangingEventHandler PropertyChanging; // Used to notify that a property is about to change private void NotifyPropertyChanging(string propertyName) { if (PropertyChanging != null) { PropertyChanging(this, new PropertyChangingEventArgs(propertyName)); } } #endregion }これは、ローカル データベース テーブルを表すクラス entity の基本テンプレートです。ほとんどのエンティティで共通して使用することが推奨される次のコード機能を示すために、しばらくの間、列と関連付けが失われます。
[table] 属性は、クラスがデータベース テーブルを表すことを指定します。
変更の追跡には、INotifyPropertyChanged インターフェイスを使用します。
INotifyPropertyChanging インターフェイスにより、変更の追跡に関連するメモリの使用を制限できます。
[Column(IsVersion = true)] 属性を持つ Binary バージョン列は、テーブルの更新パフォーマンスを大幅に向上します。
注:エンティティは、イベント、メソッド、プロパティなどの他のエンティティから CLR オブジェクト メンバーを継承できます。他のエンティティからの LINQ to SQL 属性は継承できません。たとえば、INotifyPropertyChanged と INotifyPropertyChanging を実装する L2SEntity という名前の基本クラスを作成できます。他のエンティティは、L2SEntity からイベントとメソッドを継承できますが、LINQ to SQL は [Table] 属性で明示的にマークされたエンティティのみ認識します。L2SEntity クラス自体に書き込まれる LINQ to SQL 属性は、他のエントリによって継承できません。
いずれかの AddTableNameHere クラスの名前を ToDoItem に変更します。このクラスは To Do タスク情報を格納します。
他の AddTableNameHere クラスの名前を ToDoCategory に変更します。このクラスはカテゴリのリストを格納します。
ToDoItem クラスを構築するには
ToDoDataContext.cs で、ToDoItem クラスに次のコードを追加します。
// Define ID: private field, public property, and database column. private int _toDoItemId; [Column(IsPrimaryKey = true, IsDbGenerated = true, DbType = "INT NOT NULL Identity", CanBeNull = false, AutoSync = AutoSync.OnInsert)] public int ToDoItemId { get {return _toDoItemId;} set { if (_toDoItemId != value) { NotifyPropertyChanging("ToDoItemId"); _toDoItemId = value; NotifyPropertyChanged("ToDoItemId"); } } } // Define item name: private field, public property, and database column. private string _itemName; [Column] public string ItemName { get {return _itemName;} set { if (_itemName != value) { NotifyPropertyChanging("ItemName"); _itemName = value; NotifyPropertyChanged("ItemName"); } } } // Define completion value: private field, public property, and database column. private bool _isComplete; [Column] public bool IsComplete { get {return _isComplete;} set { if (_isComplete != value) { NotifyPropertyChanging("IsComplete"); _isComplete = value; NotifyPropertyChanged("IsComplete"); } } }これらのフィールドとプロパティはデータベースに ToDoItemId、ItemName、IsCompleted の 3 つの列を追加します。[Column] 属性は LINQ to SQL ランタイムに、プロパティがデータベース列を表すことを指定します。すべての有効な列属性設定については、「System.Data.Linq.Mapping.ColumnAttribute」を参照してください。
ToDoDataContext.cs で、ToDoItem クラスに次のコードを追加します。
// Internal column for the associated ToDoCategory ID value [Column] internal int _categoryId; // Entity reference, to identify the ToDoCategory "storage" table private EntityRef<ToDoCategory> _category; // Association, to describe the relationship between this key and that "storage" table [Association(Storage = "_category", ThisKey = "_categoryId", OtherKey = "Id", IsForeignKey = true)] public ToDoCategory Category { get {return _category.Entity;} set { NotifyPropertyChanging("Category"); _category.Entity = value; if (value != null) { _categoryId = value.Id; } NotifyPropertyChanging("Category"); } }このコードは、ToDoItem テーブルと ToDoCategory テーブル間の関連付けを定義します。プライベート _categoryId フィールドは To Do 項目に対応するカテゴリの識別子を格納します。_category エンティティ参照は、このテーブルに関連付けられている他のテーブルを識別します。Category 関連付けは、get 時の適切な ToDoCategory オブジェクトの取得と、set 時の適切な ToDoCategory 識別子の値の _categoryId への割り当てを処理します。
プライベート エンティティ参照とパブリック アクセサーは、ドット表記を使用して、関連付けを移動する方法を提供します。さらに、オブジェクトレベルの変更が行われたときに、データベースの関係を最新に維持します。
ToDoCategory クラスを構築するには
ToDoDataContext.cs で、ToDoCategory クラスに次のコードを追加します。
// Define ID: private field, public property, and database column. private int _id; [Column(DbType = "INT NOT NULL IDENTITY", IsDbGenerated = true, IsPrimaryKey = true)] public int Id { get {return _id;} set { NotifyPropertyChanging("Id"); _id = value; NotifyPropertyChanged("Id"); } } // Define category name: private field, public property, and database column. private string _name; [Column] public string Name { get {return _name;} set { NotifyPropertyChanging("Name"); _name = value; NotifyPropertyChanged("Name"); } }このコードはカテゴリの Id プロパティと Name プロパティを定義します。
ToDoDataContext.cs で、ToDoCategory クラスに次のコードを追加します。
// Define the entity set for the collection side of the relationship. private EntitySet<ToDoItem> _todos; [Association(Storage = "_todos", OtherKey = "_categoryId", ThisKey = "Id")] public EntitySet<ToDoItem> ToDos { get { return this._todos; } set { this._todos.Assign(value); } } // Assign handlers for the add and remove operations, respectively. public ToDoCategory() { _todos = new EntitySet<ToDoItem>( new Action<ToDoItem>(this.attach_ToDo), new Action<ToDoItem>(this.detach_ToDo) ); } // Called during an add operation private void attach_ToDo(ToDoItem toDo) { NotifyPropertyChanging("ToDoItem"); toDo.Category = this; } // Called during a remove operation private void detach_ToDo(ToDoItem toDo) { NotifyPropertyChanging("ToDoItem"); toDo.Category = null; }このコードは ToDoItems テーブルによって、関連付けのコレクション側を定義します。EntitySet コンストラクターは、set の追加イベントと削除イベントのデリゲートを取得します。これにより、そのカテゴリが新しい ToDoItem オブジェクトの作成時に明示的に設定されていない場合でも、コレクションに追加される新しい ToDoItem オブジェクトにカテゴリ情報を正しく設定できます。
LINQ to SQL データ コンテキストを作成するには
ToDoDataContext.cs で、LocalDatabaseSample.Model 名前空間に次のコードを追加します。
public class ToDoDataContext : DataContext { // Pass the connection string to the base class. public ToDoDataContext(string connectionString) : base(connectionString) { } // Specify a table for the to-do items. public Table<ToDoItem> Items; // Specify a table for the categories. public Table<ToDoCategory> Categories; }このデータ コンテキストは Items と Categories の 2 つのテーブルを指定します。Items テーブルは、それぞれ ToDoItem クラスに基づいた To Do タスク項目を格納します。Categories テーブルは、それぞれ ToDoCategory クラスに基づいた To Do タスク カテゴリを格納します。
このセクションでは、アプリケーションの ViewModel を作成します。ViewModel は、データベースへの操作の実行と、複数の監視可能コレクションとリストを介したアプリケーション ページへのデータの表示を担当します。
ViewModel ファイルを準備するには
ソリューション エクスプローラーで、プロジェクトを右クリックし、[追加] を選択して、[新しいフォルダー] をクリックします。
新しいフォルダーに ViewModel という名前を付けます。
ソリューション エクスプローラーで、ViewModel フォルダーを右クリックし、[追加] を選択して [新しい項目] を選択します。
[新しい項目の追加] ウィンドウで、[コード ファイル] を選択し、ファイルに ToDoViewModel.cs という名前を付けます。[追加] をクリックします。
ToDoViewModel.cs で、次のコードを追加します。
using System.Collections.Generic; using System.Collections.ObjectModel; using System.ComponentModel; using System.Linq; // Directive for the data model. using LocalDatabaseSample.Model; namespace LocalDatabaseSample.ViewModel { public class ToDoViewModel : INotifyPropertyChanged { // LINQ to SQL data context for the local database. private ToDoDataContext toDoDB; // Class constructor, create the data context object. public ToDoViewModel(string toDoDBConnectionString) { toDoDB = new ToDoDataContext(toDoDBConnectionString); } // // TODO: Add collections, list, and methods here. // // Write changes in the data context to the database. public void SaveChangesToDB() { toDoDB.SubmitChanges(); } #region INotifyPropertyChanged Members public event PropertyChangedEventHandler PropertyChanged; // Used to notify Silverlight that a property has changed. private void NotifyPropertyChanged(string propertyName) { if (PropertyChanged != null) { PropertyChanged(this, new PropertyChangedEventArgs(propertyName)); } } #endregion } }
これは ViewModel のテンプレートです。LocalDatabaseSample.Model 名前空間へのディレクティブを使用して、前のセクションで作成した LINQ to SQL データ モデルを参照します。ViewModel は変更追跡のための INotifyPropertyChanged インターフェイスを実装します。
ローカル データベースへの操作の実行は、ViewModel のコア機能です。LINQ to SQL データ コンテキスト toDoDB は、ViewModel 経由で参照され、ViewModel のコンストラクターに作成されます。SaveChangesToDB メソッドは、ビューに汎用の保存メカニズムを提供します。
監視可能コレクションおよびリストを作成するには
ToDoViewModel.cs で、ToDoViewModel クラスに次のコードを追加します。
// All to-do items. private ObservableCollection<ToDoItem> _allToDoItems; public ObservableCollection<ToDoItem> AllToDoItems { get { return _allToDoItems; } set { _allToDoItems = value; NotifyPropertyChanged("AllToDoItems"); } } // To-do items associated with the home category. private ObservableCollection<ToDoItem> _homeToDoItems; public ObservableCollection<ToDoItem> HomeToDoItems { get { return _homeToDoItems; } set { _homeToDoItems = value; NotifyPropertyChanged("HomeToDoItems"); } } // To-do items associated with the work category. private ObservableCollection<ToDoItem> _workToDoItems; public ObservableCollection<ToDoItem> WorkToDoItems { get { return _workToDoItems; } set { _workToDoItems = value; NotifyPropertyChanged("WorkToDoItems"); } } // To-do items associated with the hobbies category. private ObservableCollection<ToDoItem> _hobbiesToDoItems; public ObservableCollection<ToDoItem> HobbiesToDoItems { get { return _hobbiesToDoItems; } set { _hobbiesToDoItems = value; NotifyPropertyChanged("HobbiesToDoItems"); } } // A list of all categories, used by the add task page. private List<ToDoCategory> _categoriesList; public List<ToDoCategory> CategoriesList { get { return _categoriesList; } set { _categoriesList = value; NotifyPropertyChanged("CategoriesList"); } }このコードは、Pivot コントロールによってメイン ページで使われる監視可能コレクション AllToDoItems、HomeToDoItems、WorkToDoItems、HobbiesToDoItems を指定しています。さらに、ListPicker コントロールによって、新しいタスク ページで使用される CategoriesList リストも指定しています。
コレクションおよびリストを読み込むには
ToDoViewModel.cs で、ToDoViewModel クラスに次のコードを追加します。
// Query database and load the collections and list used by the pivot pages. public void LoadCollectionsFromDatabase() { // Specify the query for all to-do items in the database. var toDoItemsInDB = from ToDoItem todo in toDoDB.Items select todo; // Query the database and load all to-do items. AllToDoItems = new ObservableCollection<ToDoItem>(toDoItemsInDB); // Specify the query for all categories in the database. var toDoCategoriesInDB = from ToDoCategory category in toDoDB.Categories select category; // Query the database and load all associated items to their respective collections. foreach (ToDoCategory category in toDoCategoriesInDB) { switch (category.Name) { case "Home": HomeToDoItems = new ObservableCollection<ToDoItem>(category.ToDos); break; case "Work": WorkToDoItems = new ObservableCollection<ToDoItem>(category.ToDos); break; case "Hobbies": HobbiesToDoItems = new ObservableCollection<ToDoItem>(category.ToDos); break; default: break; } } // Load a list of all categories. CategoriesList = toDoDB.Categories.ToList(); }このコードでは、ViewModel は、ローカル データベースからデータを含むコレクションとリストを読み込みます。遅延読み込みでは、監視可能コレクションがインスタンス化されるまで、データベースは実際にクエリされません。
注:foreach ステートメントと switch ステートメントは、EntitySet オブジェクトを使用して、その特定のカテゴリに関連付けられているすべての ToDoItem オブジェクトを取得する方法を示すために表示しています。この特定のメソッドでは、3 つの標準 LINQ クエリでも同じ結果を達成できます。
データベースへの追加操作と削除操作を実行するには
ToDoViewModel.cs で、ToDoViewModel クラスに次のコードを追加します。
// Add a to-do item to the database and collections. public void AddToDoItem(ToDoItem newToDoItem) { // Add a to-do item to the data context. toDoDB.Items.InsertOnSubmit(newToDoItem); // Save changes to the database. toDoDB.SubmitChanges(); // Add a to-do item to the "all" observable collection. AllToDoItems.Add(newToDoItem); // Add a to-do item to the appropriate filtered collection. switch (newToDoItem.Category.Name) { case "Home": HomeToDoItems.Add(newToDoItem); break; case "Work": WorkToDoItems.Add(newToDoItem); break; case "Hobbies": HobbiesToDoItems.Add(newToDoItem); break; default: break; } }このメソッドは、新しい To Do 項目、タスクをアプリケーションに追加する場合に呼び出されます。To Do 項目を追加すると、いくつかの更新が必要になりますます、LINQ to SQL データ コンテキスト toDoDB が更新されます。次に、データベースへの変更を保持するために、SubmitChanges メソッドが呼び出されます。最後に、Add メソッドへの呼び出しによって、該当する監視可能コレクションが更新されます。
ToDoViewModel.cs で、ToDoViewModel クラスに次のコードを追加します。
// Remove a to-do task item from the database and collections. public void DeleteToDoItem(ToDoItem toDoForDelete) { // Remove the to-do item from the "all" observable collection. AllToDoItems.Remove(toDoForDelete); // Remove the to-do item from the data context. toDoDB.Items.DeleteOnSubmit(toDoForDelete); // Remove the to-do item from the appropriate category. switch (toDoForDelete.Category.Name) { case "Home": HomeToDoItems.Remove(toDoForDelete); break; case "Work": WorkToDoItems.Remove(toDoForDelete); break; case "Hobbies": HobbiesToDoItems.Remove(toDoForDelete); break; default: break; } // Save changes to the database. toDoDB.SubmitChanges(); }このメソッドは、アプリケーションから To Do 項目、タスクを削除する場合に呼び出されます。項目を削除するシーケンスは、それらを追加するシーケンスとほぼ逆になります。まず、Remove メソッドが AllToDoItems 監視可能コレクションから項目を削除します。次に DeleteOnSubmitメソッドが LINQ to SQL データ コンテキストから項目を削除します。その後、該当するフィルター処理されたコレクションから項目が削除されます。最後に、SubmitChanges メソッドを使用して、ローカル データベースから項目が削除 (および他の変更が保存) されます。
このセクションでは、前のセクションで作成したすべての部分をつなぎ合わせます。まず、アプリケーション クラスを変更して、静的 ViewModel を指定し、ローカル データベースを作成します。次に、ページの分離コード ファイルを、ViewModel を使用し、ボタン クリックを処理するように変更します。
App クラスを完成させるには
App.xaml.cs ファイルで、ページの上部に次のディレクティブを追加します。
// Directives using LocalDatabaseSample.Model; using LocalDatabaseSample.ViewModel;
App.xaml.cs ファイルで、App クラス内のクラス コンストラクタの上に次のコードを追加します。
// The static ViewModel, to be used across the application. private static ToDoViewModel viewModel; public static ToDoViewModel ViewModel { get { return viewModel; } }これにより、各ページによって、アプリケーション全体で使用可能な静的 ViewModel を指定します。
App.xaml.cs ファイルで、App クラスのクラス コンストラクタ内の末尾に次のコードを追加します。
// Specify the local database connection string. string DBConnectionString = "Data Source=isostore:/ToDo.sdf"; // Create the database if it does not exist. using (ToDoDataContext db = new ToDoDataContext(DBConnectionString)) { if (db.DatabaseExists() == false) { // Create the local database. db.CreateDatabase(); // Prepopulate the categories. db.Categories.InsertOnSubmit(new ToDoCategory { Name = "Home" }); db.Categories.InsertOnSubmit(new ToDoCategory { Name = "Work" }); db.Categories.InsertOnSubmit(new ToDoCategory { Name = "Hobbies" }); // Save categories to the database. db.SubmitChanges(); } } // Create the ViewModel object. viewModel = new ToDoViewModel(DBConnectionString); // Query the local database and load observable collections. viewModel.LoadCollectionsFromDatabase();アプリケーション オブジェクトがインスタンス化された時に、ローカル データベースがまだ存在しない場合は作成されます。データベースの作成時に、ToDoCategory テーブルにこれらのカテゴリが事前入力されます。データベースが存在すると、ViewModel オブジェクトが作成され、データが読み込まれます。
MainPage クラスを完成させるには
メイン ページの分離コード ファイル MainPage.xaml.cs で、次のディレクティブを追加します。
// Directive for the ViewModel. using LocalDatabaseSample.Model;
MainPage.xaml で、MainPage コンストラクタの InitializeComponent メソッドの呼び出しの後に、次のコードを追加します。
// Set the page DataContext property to the ViewModel. this.DataContext = App.ViewModel;このコードは、ページの DataContext プロパティを ViewModel に設定します。ページの DataContext は、LINQ to SQL データ コンテキストとは明確に異なります。MVVM に関して、前者は View のプロパティであり、後者は Model を定義します。
MainPage.xaml.cs で、次のコードを MainPage クラスに追加します。
private void newTaskAppBarButton_Click(object sender, EventArgs e) { NavigationService.Navigate(new Uri("/NewTaskPage.xaml", UriKind.Relative)); } private void deleteTaskButton_Click(object sender, RoutedEventArgs e) { // Cast the parameter as a button. var button = sender as Button; if (button != null) { // Get a handle for the to-do item bound to the button. ToDoItem toDoForDelete = button.DataContext as ToDoItem; App.ViewModel.DeleteToDoItem(toDoForDelete); } // Put the focus back to the main page. this.Focus(); } protected override void OnNavigatedFrom(System.Windows.Navigation.NavigationEventArgs e) { // Save changes to the database. App.ViewModel.SaveChangesToDB(); }このコードは、ページのイベント処理コードを示しています。新しいタスク ボタンがクリックされると、NavigationService オブジェクトを使用して、新しいタスク ページに移動します。削除ボタンが呼び出されると、対応する ToDoItem オブジェクトが取得され、ViewModel の DeleteToDoItem メソッドに送られます。ユーザーがページから移動するたびに、SaveChangesToDB メソッドを使用して、データ コンテキストの変更が自動的にローカル データベースに保存されます。
NewTaskPage クラスを完成させるには
新しいタスク ページの分離コード ファイル MainPage.xaml.cs で、次のディレクティブを追加します。
// Directive for the data model. using LocalDatabaseSample.Model;
NewTaskPage.xaml.cs で、NewTaskPage コンストラクタの InitializeComponent メソッドの呼び出しの後に次のコードを追加します。
// Set the page DataContext property to the ViewModel. this.DataContext = App.ViewModel;このコードは、ページの DataContext プロパティを ViewModel に設定します。ページの DataContext は、LINQ to SQL データ コンテキストとは明確に異なります。MVVM に関して、前者は View のプロパティであり、後者は Model を定義します。
NewTaskPage.xaml.cs で、NewTaskPage クラスに次のコードを追加します。
private void appBarOkButton_Click(object sender, EventArgs e) { // Confirm there is some text in the text box. if (newTaskNameTextBox.Text.Length > 0) { // Create a new to-do item. ToDoItem newToDoItem = new ToDoItem { ItemName = newTaskNameTextBox.Text, Category = (ToDoCategory)categoriesListPicker.SelectedItem }; // Add the item to the ViewModel. App.ViewModel.AddToDoItem(newToDoItem); // Return to the main page. if (NavigationService.CanGoBack) { NavigationService.GoBack(); } } } private void appBarCancelButton_Click(object sender, EventArgs e) { // Return to the main page. if (NavigationService.CanGoBack) { NavigationService.GoBack(); } }appBarOkButton_Click メソッドは、タスクにマイナー検証を実行し、それを ViewModel に追加します。
これでアプリケーションが完成しました。F5 キーを押して、デバッグを開始し、アプリケーションをテストします。