WPFでMVVMでDB照会してDataGridに表示する

#概要
・MVVMモデルで簡単なDB照会を実装する。
・照会→DB更新→再照会で更新されるのを目標。
・DBはpostgresql。

##開発環境
・開発環境はVisualStudio

##詳細
**ソース一覧**

|フォルダ | ファイル | 概要 | 
|:-------------|:--------------------|:------------|
|Commands | SearchDataBtnCommand.cs   | DB照会時の処理を記述         |
|ViewModel| DBTestPageViewModel.cs    | DB照会ページのビューモデル                |
|Views    | DBTestPage.xaml           | DB照会のページ|
|Utils    | DBCommon.cs               | DB照会用クラス|

```c#:SearchDataBtnCommand.cs
using System;
using System.Collections.Generic;
using System.Collections.ObjectModel;
using System.Data;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Windows;
using System.Windows.Input;
using System.Windows.Navigation;

namespace LoginProject
{
    /// <summary>
    /// 照会ボタンのコマンド
    /// </summary>
    public class SearchDataBtnCommand : ICommand
    {
        /// <summary>
        /// コマンドを読み出す側のクラス（View Model）を保持するプロパティ
        /// </summary>
        private DBTestPageViewModel _view { get; set; }

        /// <summary>
        /// コンストラクタ
        /// 　対応するViewModelを引数に設定しておく
        /// </summary>
        /// <param name="view"></param>
        public SearchDataBtnCommand(DBTestPageViewModel view)
        {
            _view = view;
        }

        /// <summary>
        /// 必ず実装しておくメソッド
        /// </summary>
        public event EventHandler CanExecuteChanged
        {
            add { CommandManager.RequerySuggested += value; }
            remove { CommandManager.RequerySuggested -= value; }
        }

        /// <summary>
        /// 必ず実装しておくメソッド
        /// /// コマンドの有効／無効を判定するメソッド
        /// </summary>
        /// <param name="parameter"></param>
        /// <returns></returns>
        public bool CanExecute(object parameter)
        {
            return true;
        }

        /// <summary>
        /// 必ず実装しておくメソッド
        /// コマンドの動作を定義するメソッド
        /// </summary>
        /// <param name="parameter"></param>
        public void Execute(object parameter)
        {
            DBCommon db = new DBCommon();
            DataTable DbRet = db.searchAllTrnStock();
            ObservableCollection<DBTestPageViewModel.ViewData> data = new ObservableCollection<DBTestPageViewModel.ViewData>();
            foreach (DataRow dr in DbRet.Rows)
            {
                data.Add(new DBTestPageViewModel.ViewData() { 
                    商品番号 = dr["item_cd"].ToString()
                    ,商品名 = dr["item_name"].ToString()
                    ,在庫数 = dr["stock_num"].ToString()
                    ,作成日 = dr["create_date"].ToString()
                    ,更新日 = dr["update_date"].ToString()
                });   
            }
            _view.DataView = data;

    }
    }

}

```
```c#:DBTestPageViewModel.cs
using System;
using System.Collections.Generic;
using System.Collections.ObjectModel;
using System.ComponentModel;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Windows.Data;

namespace LoginProject
{
    /// <summary>
    /// DB照会画面のViewModelクラス
    /// </summary>
    public class DBTestPageViewModel : INotifyPropertyChanged
    {

        // とにかくVIewModelに実装するもの
        public event PropertyChangedEventHandler PropertyChanged;

        // コマンド格納プロパティ
        public SearchDataBtnCommand SearchDataBtnCommand { get; private set; } 

        /// <summary>
        /// コンストラクタ
        /// </summary>
        public DBTestPageViewModel()
        {
            // コマンドインスタンスの生成
            SearchDataBtnCommand = new SearchDataBtnCommand(this);
        }

        private ObservableCollection<ViewData> _dataView = new ObservableCollection<ViewData>();
        public ObservableCollection<ViewData> DataView
        {
            get { return _dataView; }
            set { _dataView = value;

                if (PropertyChanged != null)
                {
                    PropertyChanged(this, new PropertyChangedEventArgs("DataView"));
                }
            }
        }

        public class ViewData
        {
            public string 商品番号 { get; set; }
            public string 商品名 { get; set; }
            public string 在庫数 { get; set; }
            public string 作成日 { get; set; }
            public string 更新日 { get; set; }
        }

    }
}
```

```xml:DBTestPage.xaml
<Page x:Class="LoginProject.DBTestPage"
      xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
      xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
      xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006" 
      xmlns:d="http://schemas.microsoft.com/expression/blend/2008" 
      xmlns:local="clr-namespace:LoginProject"
      mc:Ignorable="d" 
      d:DesignHeight="450" d:DesignWidth="800"
      Title="DBTestPage">
    <Page.DataContext>
        <local:DBTestPageViewModel/>
    </Page.DataContext>
    <Grid Background="#FF27C22E">
        <Label Content="DBテスト画面" HorizontalAlignment="Left" Margin="278,49,0,0" VerticalAlignment="Top" Height="97" Width="218" FontSize="36" RenderTransformOrigin="0.5,0.5">
            <Label.RenderTransform>
                <TransformGroup>
                    <ScaleTransform/>
                    <SkewTransform AngleY="0.491"/>
                    <RotateTransform/>
                    <TranslateTransform Y="0.715"/>
                </TransformGroup>
            </Label.RenderTransform>
        </Label>
        <Button Content="照会"  Command="{Binding SearchDataBtnCommand}" HorizontalAlignment="Left" Margin="99,123,0,0" VerticalAlignment="Top" Height="50" Width="121" FontSize="24"/>
        <Button Content="ログアウト" Command="{Binding LogoutBtnCommand}" HorizontalAlignment="Left" Margin="616,40,0,0" VerticalAlignment="Top" Height="50" Width="121" FontSize="24"/>
        <DataGrid ItemsSource="{Binding DataView}" Margin="160,225,185,59" AutoGenerateColumns="True" IsReadOnly="True" CanUserAddRows="false" CanUserDeleteRows="False"  CanUserSortColumns="False"/>

    </Grid>
</Page>

```
```c#:DBCommon.cs
using log4net;
using Npgsql;
using System;
using System.Collections.Generic;
using System.Configuration;
using System.Data;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Transactions;

namespace LoginProject
{
    class DBCommon
    {

        // DB接続文字列
        private String DBConnectStr;
        private ILog log;

        // コンストラクタ
        public DBCommon()
        {
            // ログ準備
            log = LogManager.GetLogger(System.Reflection.MethodBase.GetCurrentMethod().Name);

            // DB接続準備
            var DBConfig = (DBConfigHandler)ConfigurationManager.GetSection("DBConfig");
            DBConnectStr = String.Format("Server={0};Port={1};User ID={2};Database={3};Password={4};Enlist={5}"
                , DBConfig.Connect.Address
                , DBConfig.Connect.Port
                , DBConfig.Connect.UserId
                , DBConfig.Connect.Database
                , DBConfig.Connect.Password
                , DBConfig.Connect.Enlist);
        }

        // 在庫テーブル全検索
        public DataTable searchAllTrnStock()
        {
            var SQL = "SELECT item_cd, item_name, stock_num, create_date, update_date FROM works.trn_stock order by item_cd; ";

            //SQL処理で用いる変数を予め宣言
            NpgsqlCommand cmd = null;
            DataTable dt = null;
            NpgsqlDataAdapter da = null;

            //TransactionScopeの利用
            using (TransactionScope ts = new TransactionScope())
            {
                //接続その1
                using (NpgsqlConnection conn = new NpgsqlConnection(DBConnectStr))
                {
                    conn.Open();

                    //SELECT処理
                    dt = new DataTable();
                    cmd = new NpgsqlCommand(SQL, conn);
                    da = new NpgsqlDataAdapter(cmd);
                    da.Fill(dt);

                }
                //トランザクション完了
                ts.Complete();
            }
            return dt;
        }
    }
}

```

##動作確認

・DB状態
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/834885/f563eedd-f1c1-8017-d9aa-661edeed4594.png)

・DB照会画面(初期)
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/834885/b2e582ea-d425-c786-3ef1-f2ad78e70850.png)

・DB照会画面(照会ボタン押下後)
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/834885/4f7c9719-8bf9-42b6-8a4d-b2503587a23b.png)

・DB更新
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/834885/30a4631f-08f9-2b7c-944e-2ea61be85640.png)

・DB照会画面(再度照会)
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/834885/75e3a6ae-9bac-a692-427b-be167e5548dd.png)

##感想

・思ったよりシンプルに書けたので満足。
・表示するカラムが固定ならこれでOKだけど、可変だったりすると別の方法が必要な気がする。