情報
要求されたトピックは次のとおりです。しかし、このトピックはこのライブラリには含まれていません。

Windows Phone 8 用の MVVM でローカル データベース アプリを作成する方法

2014/06/18

対象: Windows Phone 8 および Windows Phone Silverlight 8.1 | Windows Phone OS 7.1

Windows Phone OS 7.1 以降、アプリケーションのローカル フォルダーに存在するローカル データベースにリレーショナル データを格納できるようになりました。このトピックでは、ローカル データベースを使用する複数ページの To Do リスト アプリを作成する方法を説明します。このアプリは、「Windows Phone 8 の基本的なローカル データベース アプリを作成する方法」で取り上げられているアプリと似ていますが、追加で Model-View-ViewModel (MVVM) パターンと Pivot コントロールの使用方法、Windows Phone Toolkit、およびデータベースの関連付けも示しています。Windows Phone アプリでのローカル データベースの使用の詳細については、「Windows Phone 8 のローカル データベース」を参照してください。

ヒントヒント:

このトピックはローカル データベースのサンプルに対応しています。

MVVM パターンの使用方法の詳細については、「MVVM パターンを使用した Windows Phone アプリケーションの開発」および「高度な Windows Phone アプリケーションを構築するためのケース スタディ」を参照してください。

このトピックは、次のセクションで構成されています。

次のイメージに、アプリ ピボット ページと新しいタスク ページを示します。

AP_Con_Local_Database_MVVM

作業の過程で、次のコード ファイルを変更または作成します。

  • MainPage.xaml: アプリのメイン ページを変更すると、allhomeworkhobbies のカテゴリでグループ化された To Do タスクを表示する Pivot コントロールを追加できます。

  • NewTaskPage.xaml: このページを追加すると、新しいタスクをデータベースに追加する UI が提供されます。このページでは Windows Phone 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] ボタンのクリックを処理できます。

このセクションでは、アプリを作成し、Windows Phone Toolkit をインストールして、アセンブリ参照を設定し、アプリで使用するアイコンを追加します。

アプリを作成するには

  • Windows Phone SDK を使用し、Windows Phone アプリ テンプレートで新しいプロジェクトを作成します。

Windows Phone Toolkit をインストールするには

  • Windows Phone Toolkit は、Windows Phone SDK には含まれていません。Codeplex を参照し、Windows Phone Toolkit を別途ダウンロードする必要があります。Codeplex の Web サイトに移動し、指示に従ってインストールしてください。

    メモメモ:

    ツールキットをインストールする方法はいくつかあります。このトピックでは、Windows Phone Toolkit (Microsoft.Phone.Controls.Toolkit) への参照がアプリに含まれていると仮定します。

アセンブリ参照を設定するには

  1. このアプリには Windows Phone の LINQ to SQL アセンブリへの参照が必要です。[プロジェクト] メニューの [参照の追加] をクリックし、[アセンブリ/フレームワーク] リストの [System.Data.Linq] を選択して、[OK] をクリックします。

    メモメモ:

    Windows Phone 8 が対象の場合、このアセンブリは Windows Phone アプリ テンプレートを使用する新しいアプリで自動的に参照されます。

  2. Pivot コントロールを使用するため、このアプリでは、Windows Phone Controls アセンブリへの参照が必要です。[プロジェクト] メニューの [参照の追加] をクリックし、[.NET] タブの [Microsoft.Phone.Controls] を選択し、[OK] をクリックします。

アプリで使用するアイコンを追加するには

  1. ソリューション エクスプローラーで、プロジェクトを右クリックし、[追加] を選択して、[新しいフォルダー] をクリックします。

  2. 新しいフォルダーに Images という名前を付けます。

  3. ソリューション エクスプローラーで [Images] フォルダーを右クリックし、[追加] を選択して [既存の項目] を選択します。これにより、[既存の項目の追加] メニューが開き、アプリで使用するアイコンを選択できます。

  4. [既存の項目の追加] ウィンドウで、次のパスに移動して、アイコンを選択します。この手順では、Visual Studio の既定のインストールを想定しています。別の場所にインストールしている場合は、対応する場所でアイコンを見つけます。

    C:\Program Files (x86)\Microsoft SDKs\Windows Phone\v8.0\Icons\dark

    フォルダーから、次のアイコンを選択します。

    • appbar.add.rest.png

    • appbar.cancel.rest.png

    • appbar.check.rest.png

    • appbar.delete.rest.png

    これらのアイコンは、暗い背景用に設計されているため、白色にされています。[既存の項目の追加] ウィンドウの白の背景では、アイコンが空白に見えることがあります。

  5. ソリューション エクスプローラーで、各アイコンを右クリックし、アイコンが [コンテンツ] として構築され、常に出力ディレクトリにコピーされる ([常にコピーする]) ように、ファイルのプロパティを設定します。

このセクションでは、このアプリのメイン ページと新しいタスク ページの UI を作成します。MVVM では、アプリのページはビューです。メイン ページでは、データを表示するための Pivot コントロールの使用方法を示します。新しいタスク ページでは、Windows Phone Toolkit のデータを選択する ListPicker コントロールの使用方法を示します。

メイン ページ UI を作成するには

  1. アプリのメイン ページ MainPage.xaml で、ページの上部の phone:PhoneApplicationPage 要素に、次の属性を追加します。

    xmlns:controls="clr-namespace:Microsoft.Phone.Controls;assembly=Microsoft.Phone.Controls"
    

    Pivot コントロールを使用するには、この名前空間が必要です。

  2. 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 プロパティにバインドされています。

  3. MainPage.xamlLayoutRoot というグリッドを次のコードで置き換えます。このコードは、前の手順で追加したリソース要素の下に追加する必要があります。

        <!--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 つの各カテゴリに対応する allhomeworkhobbies の 4 種類のページが含まれます。これらの各ページは、ViewModel の AllToDoItemsHomeToDoItemsWorkToDoItemsHobbiesToDoItems の監視可能コレクションにそれぞれバインドされています。

  4. 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 を作成するには

  1. ソリューション エクスプローラーで、プロジェクトを右クリックし、[追加] を選択して、[新しい項目] をクリックします。

  2. [新しい項目の追加] ウィンドウで、[Windows Phone Portrait Page] を選択し、ファイルに NewTaskPage.xaml という名前を付けます。次に、[追加] をクリックします。

  3. NewTaskPage.xaml で、ページの上部の phone:PhoneApplicationPage 要素に次の属性を追加します。

    xmlns:toolkit="clr-namespace:Microsoft.Phone.Controls;assembly=Microsoft.Phone.Controls.Toolkit"
    

    この名前空間は、Windows Phone Toolkit の ListPicker コントロールを使用するために必要です。

  4. 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 app 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 にバインドされています。

  5. 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 データ コンテキストと、データベースのテーブルおよび関連付けを表すオブジェクトを作成します。まず、ファイルを作成し、各テーブルのテンプレートを追加します。次に、各テーブルを構築し、データ コンテキストを作成します。

データ モデル ファイルを準備するには

  1. ソリューション エクスプローラーで、プロジェクトを右クリックし、[追加] を選択して、[新しいフォルダー] をクリックします。

  2. 新しいフォルダーに Model という名前を付けます。

  3. ソリューション エクスプローラーの [Model] フォルダーを右クリックし、[追加] を選択して、[新しい項目] を選択します。

  4. [新しい項目の追加] ウィンドウで、[コード ファイル] を選択し、ファイルに ToDoDataContext.cs という名前を付けます。次に、[追加] をクリックします。

  5. ToDoDataContext.cs で、次のディレクティブとデータ モデル クラスを含める名前空間を追加します。

    using System;
    using System.ComponentModel;
    using System.Data.Linq;
    using System.Data.Linq.Mapping;
    
    namespace LocalDatabaseSample.Model
    {
    
    }
    
    
  6. 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 属性は継承できません。たとえば、INotifyPropertyChangedINotifyPropertyChanging を実装する L2SEntity という名前の基本クラスを作成できます。他のエンティティは、L2SEntity からイベントとメソッドを継承できますが、LINQ to SQL は [Table] 属性で明示的にマークされたエンティティのみ認識します。L2SEntity クラス自体に書き込まれる LINQ to SQL 属性は、他のエントリによって継承できません。

  7. いずれかの AddTableNameHere クラスの名前を ToDoItem に変更します。このクラスは To Do タスク情報を格納します。

  8. 他の AddTableNameHere クラスの名前を ToDoCategory に変更します。このクラスはカテゴリのリストを格納します。

ToDoItem クラスを構築するには

  1. 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");
                    }
                }
            }
    
    

    これらのフィールドとプロパティはデータベースに ToDoItemIdItemNameIsCompleted の 3 つの列を追加します。[Column] 属性は LINQ to SQL ランタイムに、プロパティがデータベース列を表すことを指定します。すべての有効な列属性設定については、System.Data.Linq.Mapping.ColumnAttribute を参照してください。

  2. 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 クラスを構築するには

  1. 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 プロパティを定義します。

  2. 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;
        }
    
    

    このデータ コンテキストは ItemsCategories の 2 つのテーブルを指定します。Items テーブルは、それぞれ ToDoItem クラスに基づいた To Do タスク項目を格納します。Categories テーブルは、それぞれ ToDoCategory クラスに基づいた To Do タスク カテゴリを格納します。

このセクションでは、アプリの ViewModel を作成します。ViewModel は、データベースへの操作の実行と、複数の監視可能コレクションとリストを介したアプリ ページへのデータの表示を行います。

ViewModel ファイルを準備するには

  1. ソリューション エクスプローラーで、プロジェクトを右クリックし、[追加] を選択して、[新しいフォルダー] をクリックします。

  2. 新しいフォルダーに ViewModel という名前を付けます。

  3. ソリューション エクスプローラーで、[ViewModel] フォルダーを右クリックし、[追加] を選択して [新しい項目] を選択します。

  4. [新しい項目の追加] ウィンドウで、[コード ファイル] を選択し、ファイルに ToDoViewModel.cs という名前を付けます。次に、[追加] をクリックします。

  5. 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 the app 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 コントロールによってメイン ページで使われる監視可能コレクション AllToDoItemsHomeToDoItemsWorkToDoItemsHobbiesToDoItems を指定しています。さらに、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 クエリでも同じ結果を達成できます。

データベースへの追加操作と削除操作を実行するには

  1. 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 メソッドへの呼び出しによって、該当する監視可能コレクションが更新されます。

  2. 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 クラスを完成させるには

  1. App.xaml.cs ファイルで、ページの上部に次のディレクティブを追加します。

    // Directives
    using LocalDatabaseSample.Model;
    using LocalDatabaseSample.ViewModel;
    
    
  2. 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 を指定します。

  3. 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 クラスを完成させるには

  1. メイン ページの分離コード ファイル MainPage.xaml.cs で、次のディレクティブを追加します。

    // Directive for the ViewModel.
    using LocalDatabaseSample.Model;
    
    
  2. 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 を定義します。

  3. 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 クラスを完成させるには

  1. 新しいタスク ページの分離コード ファイル MainPage.xaml.cs で、次のディレクティブを追加します。

    // Directive for the data model.
    using LocalDatabaseSample.Model;
    
    
  2. 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 を定義します。

  3. 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 に追加します。

  4. これでアプリが完成しました。F5 キーを押してデバッグを開始し、アプリをテストします。

表示: