チュートリアル: 顧客データベース アプリケーションの作成

このチュートリアルでは、顧客の一覧を管理するための簡単なアプリを作成します。 そうすることで、UWP のエンタープライズ アプリの基本的な概念の選択について説明します。 学習内容は次のとおりです。

  • ローカル SQL データベースに対して作成、読み取り、更新、および削除といった操作を実装します。
  • UI で顧客データを表示および編集するためのデータ グリッドを追加します。
  • UI 要素を基本的なフォーム レイアウトに配置します。

このチュートリアルの開始点は、顧客注文データベース サンプル アプリの簡略化されたバージョンに基づいた、最小限の UI と機能を備えた単一ページのアプリです。 これは C# と XAML で記述され、これらの両方の言語に関する基本的な知識を持っている必要があります。

作業アプリのメイン ページ

前提条件

このリポジトリを複製してダウンロードした後、Visual Studio で CustomerDatabaseTutorial.sln を開くことによって、プロジェクトを編集できます。

Note

このチュートリアルは、WinUI と Windows アプリ SDK を使用するように最近更新された顧客注文データベースのサンプルに基づいています。 このチュートリアルとコードが更新されるまで、2 つのサンプルには違いがあります。

パート 1: 関心のあるコード

アプリを開いた直後に実行すると、空白の画面の上部にいくつかのボタンが表示されます。 表示されていませんが、アプリには、いくつかのテスト顧客でプロビジョニング済みのローカル SQLite データベースが既に含まれています。 ここからは、それらの顧客を表示するための UI コントロールを実装することから始め、次にデータベースに対する操作の追加に進みます。 開始する前に、作業を開始する場所を次に示します。

表示

CustomerListPage.xaml は、このチュートリアルの単一ページの UI を定義するアプリのビューです。 UI で visual 要素を追加または変更する必要がある場合は、いつでもこのファイルで行います。 このチュートリアルでは、次の要素を追加する手順について説明します。

  • 顧客を表示および編集するための RadDataGrid
  • 新しい顧客の初期値を設定する StackPanel

ViewModel

ViewModels\CustomerListPageViewModel.cs は、アプリの基本的なロジックが位置する場所です。 ビューで実行されたユーザー アクションは、処理のためにこのファイルに渡されます。 このチュートリアルでは、新しいコードを追加し、次のメソッドを実装します。

  • CreateNewCustomerAsync。新しい CustomerViewModel オブジェクトを初期化します。
  • DeleteNewCustomerAsync。UI に表示される前に新しい顧客を削除します。
  • DeleteAndUpdateAsync。削除ボタンのロジックを処理します。
  • GetCustomerListAsync。データベースから顧客の一覧を取得します。
  • SaveInitialChangesAsync。新しい顧客の情報をデータベースに追加します。
  • UpdateCustomersAsync。追加または削除された顧客を反映するように UI を更新します。

CustomerViewModel は、顧客情報のラッパーであり、最近変更されたかどうかを追跡します。 このクラスに何も追加する必要はありませんが、他の場所に追加するコードの一部はそれを参照します。

サンプルの作成方法の詳細については、「アプリ構造の概要」をご覧ください。

パート 2: DataGrid を追加する

顧客データの操作を開始する前に、それらの顧客を表示するための UI コントロールを追加する必要があります。 これを行うには、事前に作成されたサードパーティの RadDataGrid コントロールを使用します。 Telerik.UI.for.UniversalWindowsPlatform NuGet パッケージは、このプロジェクトに既に含まれています。 プロジェクトにグリッドを追加しましょう。

  1. ソリューション エクスプローラーから Views\CustomerListPage.xaml を開きます。 [ページ] タグ内に次のコード行を追加して、データ グリッドを含む Telerik 名前空間へのマッピングを宣言します。

        xmlns:telerikGrid="using:Telerik.UI.Xaml.Controls.Grid"
    
  2. ビューのメイン RelativePanel 内の CommandBar の下に、いくつかの基本的な構成オプションを含む RadDataGrid コントロールを追加します。

    <Grid
        x:Name="CustomerListRoot"
        Background="{ThemeResource ApplicationPageBackgroundThemeBrush}">
        <RelativePanel>
            <CommandBar
                x:Name="mainCommandBar"
                HorizontalAlignment="Stretch"
                Background="AliceBlue">
                <!--CommandBar content-->
            </CommandBar>
            <telerikGrid:RadDataGrid
                x:Name="DataGrid"
                BorderThickness="0"
                ColumnDataOperationsMode="Flyout"
                GridLinesVisibility="None"
                GroupPanelPosition="Left"
                RelativePanel.AlignLeftWithPanel="True"
                RelativePanel.AlignRightWithPanel="True"
                RelativePanel.Below="mainCommandBar" />
        </RelativePanel>
    </Grid>
    
  3. データ グリッドを追加しましたが、表示するデータが必要です。 次のコード行を追加します。

    ItemsSource="{x:Bind ViewModel.Customers}"
    UserEditMode="Inline"
    

    表示するデータのソースを定義したので、RadDataGrid によってほとんどの UI ロジックが自動的に処理されます。 ただし、プロジェクトを実行しても、データはディスプレイには表示されません。 これは、ViewModel がまだ読み込み中ではないためです。

空のアプリ (顧客なし)

パート 3: 顧客の読み取り

初期化されると、ViewModels\CustomerListPageViewModel.csGetCustomerListAsync メソッドを呼び出します。 このメソッドでは、チュートリアルに含まれている SQLite データベースからテスト顧客データを取得する必要があります。

  1. ViewModels\CustomerListPageViewModel.cs で、GetCustomerListAsync メソッドを次のコードで更新します。

    public async Task GetCustomerListAsync()
    {
        var customers = await App.Repository.Customers.GetAsync(); 
        if (customers == null)
        {
            return;
        }
        await DispatcherHelper.ExecuteOnUIThreadAsync(() =>
        {
            Customers.Clear();
            foreach (var c in customers)
            {
                Customers.Add(new CustomerViewModel(c));
            }
        });
    }
    

    GetCustomerListAsync メソッドは、ViewModel が読み込まれるときに呼び出されますが、この手順の前には何も行っていません。 ここでは、Repository/SqlCustomerRepositoryGetAsync メソッドへの呼び出しを追加しました。 これにより、リポジトリに接続して、顧客オブジェクトの列挙可能なコレクションを取得できます。 次に、内部の ObservableCollection に追加する前に、それらを個々のオブジェクトに解析して、表示および編集できるようにします。

  2. アプリの実行 - 顧客の一覧を表示するデータ グリッドが表示されます。

顧客の初期リスト

パート 4: 顧客を編集する

データ グリッドのエントリはダブルクリックして編集できますが、UI で行った変更は、分離コード内の顧客のコレクションにも反映されていることを確認する必要があります。 つまり、2 つの方法でデータ バインディングを実装する必要があります。 詳細については、データ バインディングの概要に関するページを参照してください。

  1. まず、ViewModels\CustomerListPageViewModel.csINotifyPropertyChanged インターフェイスが実装されることを宣言します。

    public class CustomerListPageViewModel : INotifyPropertyChanged
    
  2. 次に、クラスの本体内に、次のイベントとメソッドを追加します。

    public event PropertyChangedEventHandler PropertyChanged;
    
    public void OnPropertyChanged([CallerMemberName] string propertyName = null) =>
        PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName));
    

    OnPropertyChanged メソッドを使用すると、setter で PropertyChanged イベントを簡単に発生させることができます。これは、2 つの方法のデータ バインディングに必要です。

  3. 次の関数呼び出しを使用して、SelectedCustomer の setter を更新します。

    public CustomerViewModel SelectedCustomer
    {
        get => _selectedCustomer;
        set
        {
            if (_selectedCustomer != value)
            {
                _selectedCustomer = value;
                OnPropertyChanged();
            }
        }
    }
    
  4. Views\CustomerListPage.xaml で、SelectedCustomer プロパティをデータ グリッドに追加します。

    SelectedItem="{x:Bind ViewModel.SelectedCustomer, Mode=TwoWay}"
    

    これにより、データ グリッド内のユーザーの選択が、分離コード内の対応する顧客オブジェクトに関連付けられます。 TwoWay バインディング モードを使用すると、UI で行われた変更をそのオブジェクトに反映できます。

  5. アプリを実行します。 これで、グリッドに表示される顧客を確認し、UI を使用して基になるデータを変更できます。

データ グリッドでの顧客の編集

パート 5: 顧客を更新する

顧客を表示および編集できるようになったので、変更をデータベースにプッシュし、他のユーザーが行った更新をプルできるようにする必要があります。

  1. ViewModels\CustomerListPageViewModel.cs に戻り、UpdateCustomersAsync メソッドに移動します。 このコードを使用して更新し、変更をデータベースにプッシュし、新しい情報を取得します。

    public async Task UpdateCustomersAsync()
    {
        foreach (var modifiedCustomer in Customers
            .Where(x => x.IsModified).Select(x => x.Model))
        {
            await App.Repository.Customers.UpsertAsync(modifiedCustomer);
        }
        await GetCustomerListAsync();
    }
    

    このコードでは、ViewModels\CustomerViewModel.csIsModified プロパティを利用します。これは、顧客が変更されるたびに自動的に更新されます。 これにより、不要な呼び出しを回避し、更新された顧客からの変更のみをデータベースにプッシュできます。これにより、

パート 6: 新しい顧客を作成する

プロパティの値を指定する前に顧客を UI に追加すると、顧客は空白行として表示されるため、新しい顧客を追加することは課題となります。 これは問題ではありませんが、ここでは、顧客の初期値を簡単に設定できるようにします。 このチュートリアルでは、簡単な折りたたみ可能なパネルを追加しますが、追加する情報が多い場合は、この目的のために別のページを作成できます。

分離コードを更新する

  1. ViewModels\CustomerListPageViewModel.cs に新しいプライベート フィールドとパブリック プロパティを追加します。 これは、パネルが表示されるかどうかを制御するために使用されます。

    private bool _addingNewCustomer = false;
    
    public bool AddingNewCustomer
    {
        get => _addingNewCustomer;
        set
        {
            if (_addingNewCustomer != value)
            {
                _addingNewCustomer = value;
                OnPropertyChanged();
            }
        }
    }
    
  2. AddingNewCustomer の値の逆である新しいパブリック プロパティを ViewModel に追加します。 これは、パネルが表示されているときに通常のコマンド バー ボタンを無効にする場合に使用されます。

    public bool EnableCommandBar => !AddingNewCustomer;
    

    次に、折りたたみ可能なパネルを表示し、その中で編集する顧客を作成する方法が必要になります。

  3. 新しく作成された顧客を保持するために、新しいプライベート fiend プロパティとパブリック プロパティを ViewModel に追加します。

    private CustomerViewModel _newCustomer;
    
    public CustomerViewModel NewCustomer
    {
        get => _newCustomer;
        set
        {
            if (_newCustomer != value)
            {
                _newCustomer = value;
                OnPropertyChanged();
            }
        }
    }
    
  4. CreateNewCustomerAsync メソッドを更新して新しい顧客を作成し、それをリポジトリに追加して、選択した顧客として設定します。

    public async Task CreateNewCustomerAsync()
    {
        CustomerViewModel newCustomer = new CustomerViewModel(new Models.Customer());
        NewCustomer = newCustomer;
        await App.Repository.Customers.UpsertAsync(NewCustomer.Model);
        AddingNewCustomer = true;
    }
    
  5. SaveInitialChangesAsync メソッドを更新して、新しく作成された顧客をリポジトリに追加し、UI を更新して、パネルを閉じます。

    public async Task SaveInitialChangesAsync()
    {
        await App.Repository.Customers.UpsertAsync(NewCustomer.Model);
        await UpdateCustomersAsync();
        AddingNewCustomer = false;
    }
    
  6. AddingNewCustomer の setter の最後の行として、次のコード行を追加します。

    OnPropertyChanged(nameof(EnableCommandBar));
    

    これにより、AddingNewCustomer が変更されるたびに EnableCommandBar が自動的に更新されます。

UI の更新

  1. Views\CustomerListPage.xaml に戻り、CommandBar とデータ グリッドの間に次のプロパティを持つ StackPanel を追加します。

    <StackPanel
        x:Name="newCustomerPanel"
        Orientation="Horizontal"
        x:Load="{x:Bind ViewModel.AddingNewCustomer, Mode=OneWay}"
        RelativePanel.Below="mainCommandBar">
    </StackPanel>
    

    x:Load 属性を使用すると、新しい顧客を追加するときにのみこのパネルが表示されるようになります。

  2. データ グリッドの位置を次のように変更して、新しいパネルが表示されたときに下に移動するようにします。

    RelativePanel.Below="newCustomerPanel"
    
  3. 4 つの TextBox コントロールを使用して、スタック パネルを更新します。 これらは新しい顧客の個々のプロパティにバインドされ、データグリッドに追加する前にその値を編集することができます。

    <StackPanel
        x:Name="newCustomerPanel"
        Orientation="Horizontal"
        x:Load="{x:Bind ViewModel.AddingNewCustomer, Mode=OneWay}"
        RelativePanel.Below="mainCommandBar">
        <TextBox
            Header="First name"
            PlaceholderText="First"
            Margin="8,8,16,8"
            MinWidth="120"
            Text="{x:Bind ViewModel.NewCustomer.FirstName, Mode=TwoWay, UpdateSourceTrigger=PropertyChanged}"/>
        <TextBox
            Header="Last name"
            PlaceholderText="Last"
            Margin="0,8,16,8"
            MinWidth="120"
            Text="{x:Bind ViewModel.NewCustomer.LastName, Mode=TwoWay, UpdateSourceTrigger=PropertyChanged}"/>
        <TextBox
            Header="Address"
            PlaceholderText="1234 Address St, Redmond WA 00000"
            Margin="0,8,16,8"
            MinWidth="280"
            Text="{x:Bind ViewModel.NewCustomer.Address, Mode=TwoWay, UpdateSourceTrigger=PropertyChanged}"/>
        <TextBox
            Header="Company"
            PlaceholderText="Company"
            Margin="0,8,16,8"
            MinWidth="120"
            Text="{x:Bind ViewModel.NewCustomer.Company, Mode=TwoWay, UpdateSourceTrigger=PropertyChanged}"/>
    </StackPanel>
    
  4. 新しいスタック パネルに単純なボタンを追加して、新しく作成した顧客を保存します。

    <StackPanel>
        <!--Text boxes from step 3-->
        <AppBarButton
            x:Name="SaveNewCustomer"
            Click="{x:Bind ViewModel.SaveInitialChangesAsync}"
            Icon="Save"/>
    </StackPanel>
    
  5. CommandBar を更新すると、スタック パネルが表示されているときに、通常の [作成]、[削除]、[更新] のボタンが無効になります。

    <CommandBar
        x:Name="mainCommandBar"
        HorizontalAlignment="Stretch"
        IsEnabled="{x:Bind ViewModel.EnableCommandBar, Mode=OneWay}"
        Background="AliceBlue">
        <!--App bar buttons-->
    </CommandBar>
    
  6. アプリを実行します。 これで、顧客を作成し、そのデータをスタック パネルに入力できるようになりました。

新しい顧客の作成

パート 7: 顧客を削除する

顧客の削除は、実装する必要のある最終的な基本操作です。 データ グリッド内で選択した顧客を削除する場合は、UI を更新するために UpdateCustomersAsync をすぐに呼び出す必要があります。 ただし、作成したばかりの顧客を削除する場合は、そのメソッドを呼び出す必要はありません。

  1. ViewModels\CustomerListPageViewModel.cs に移動し、DeleteAndUpdateAsync メソッドを更新します。

    public async void DeleteAndUpdateAsync()
    {
        if (SelectedCustomer != null)
        {
            await App.Repository.Customers.DeleteAsync(_selectedCustomer.Model.Id);
        }
        await UpdateCustomersAsync();
    }
    
  2. Views\CustomerListPage.xaml で、新しい顧客を追加するためにスタック パネルを更新して、2 番目のボタンが含まれるようにします。

    <StackPanel>
        <!--Text boxes for adding a new customer-->
        <AppBarButton
            x:Name="DeleteNewCustomer"
            Click="{x:Bind ViewModel.DeleteNewCustomerAsync}"
            Icon="Cancel"/>
        <AppBarButton
            x:Name="SaveNewCustomer"
            Click="{x:Bind ViewModel.SaveInitialChangesAsync}"
            Icon="Save"/>
    </StackPanel>
    
  3. ViewModels\CustomerListPageViewModel.cs で、DeleteNewCustomerAsync メソッドを更新して、新しい顧客を削除します。

    public async Task DeleteNewCustomerAsync()
    {
        if (NewCustomer != null)
        {
            await App.Repository.Customers.DeleteAsync(_newCustomer.Model.Id);
            AddingNewCustomer = false;
        }
    }
    
  4. アプリを実行します。 データ グリッドまたはスタック パネル内の顧客を削除できるようになりました。

新しい顧客を削除する

まとめ

お疲れさまでした。 これがすべて完了すると、アプリはすべてのローカル データベース操作を実行できるようになります。 UI 内で顧客の作成、読み取り、更新、削除を行うことができます。これらの変更はデータベースに保存され、アプリのさまざまな起動において保持されます。

これで完了です。次の点を考慮してください。

または、挑戦したい場合は、先に進むことができます...

追加情報: リモート データベースへの接続

ローカルの SQLite データベースに対してこれらの呼び出しを実装する方法の手順について説明しました。 では、代わりにリモート データベースを使用する場合はどうすればよいでしょうか。

これを試してみるには、独自の Azure Active Directory (AAD) アカウントと、独自のデータ ソースをホストする機能が必要です。

REST 呼び出しを処理するための認証と関数を追加し、対話するためのリモート データベースを作成する必要があります。 完全な顧客注文データベース サンプルには、必要な操作ごとに参照できるコードが用意されています。

設定と構成

独自のリモート データベースに接続するために必要な手順については、サンプルの Readme をご覧ください。 次の手順の実行が必要になります。

  • Azure アカウントのクライアント ID を Constants.cs に指定します。
  • リモート データベースの URL を Constants.cs に指定します。
  • データベースの接続ストリングを Constants.cs に指定します。
  • アプリを Microsoft Store に関連付けます。
  • サービス プロジェクトをアプリにコピーし、Azure にデプロイします。

認証

認証シーケンスを開始するためのボタンと、ユーザーの情報を収集するためのポップアップまたは別のページを作成する必要があります。 これを作成したら、ユーザーの情報を要求し、それを使用してアクセス トークンを取得するコードを指定する必要があります。 顧客注文データベース サンプルでは、Microsoft Graph 呼び出しを WebAccountManager ライブラリでラップして、トークンを取得し、AAD アカウントへの認証を処理します。

REST 呼び出し

REST 呼び出しを実装するために、このチュートリアルで追加したコードを変更する必要はありません。 代わりに、次の操作を行う必要があります。

  • ICustomerRepository インターフェイスと ITutorialRepository インターフェイスの新しい実装を作成し、SQLite ではなく REST を使用して同じ関数のセットを実装します。 JSON をシリアル化および逆シリアル化する必要があります。必要に応じて、別の HttpHelper クラスで REST 呼び出しをラップすることができます。 特定の情報については、完全なサンプルを参照してください。
  • App.xaml.cs で、REST リポジトリを初期化する新しい関数を作成し、アプリの初期化時に SqliteDatabase の代わりに呼び出します。 ここでも、完全なサンプルを参照してください。

これら 3 つの手順がすべて完了したら、アプリを介して AAD アカウントに認証できるはずです。 リモート データベースへの REST 呼び出しでは、ローカルの SQLite 呼び出しが置き換えられますが、ユーザー エクスペリエンスは同じである必要があります。 さらに挑戦してみたい場合は、設定ページを追加して、ユーザーがこの 2 つを動的に切り替えることができるようにすることもできます。