Windows GUIプログラミング入門11 設定保存(2)

# ■はじめに

今回は`exe`と同じ場所に独自の設定ファイルを作成するタイプのプログラムを作ります。
このタイプは`Program Files`配下へのインストール禁止です。
`Program Files`配下は設定ファイルやログなど、インストール後に更新するファイルを置いてはいけません。

キーワード：シングルユーザー設定, JSON, JavaScriptSerializer, Calendar

[注意]
これまでの回で説明済みの操作方法等は、説明を省略したり簡略化している場合があります。

# ■開発環境
* Windows 10
* Visual Studio Community 2017
* .NET Framework 4.x

# ■作ってみる

画面の入力内容をexeと同じ場所にjsonファイルとして保存するプログラムを作ります。
ログインユーザー毎に設定を持つほどではない、小さなツールなどに向いています。

## ◇コントロールの配置、プロパティ設定

`TextBox`を3つ、`Calendar`を1つ配置します。
![wbg11-01.png](https://qiita-image-store.s3.amazonaws.com/0/142879/cb7fe420-5a8a-9fb7-8b2e-0ad8dab8d1c8.png)

4つのコントロールの`Name`プロパティ、`TextBox`の`Text`プロパティを更新します。
![wbg11-02.png](https://qiita-image-store.s3.amazonaws.com/0/142879/00883c4a-b148-6b6c-1d04-db182e4f47e6.png)

## ◇データ型追加

プロジェクトにクラスを追加します。
名前は`Person.cs`にします。

```CSharp:Person.cs
using System;

namespace WpfApp11
{
    /// <summary>
    /// 人クラス
    /// </summary>
    public class Person
    {
        /// <summary>
        /// 名前
        /// </summary>
        public string Name { get; set; }

        /// <summary>
        /// 歳
        /// </summary>
        public int Age { get; set; }

        /// <summary>
        /// 誕生日
        /// </summary>
        public DateTime Birthday { get; set; }
    }
}
```

## ◇参照設定の追加

「参照」の右クリックメニューから「参照の追加」を選択します。
![wbg11-03.png](https://qiita-image-store.s3.amazonaws.com/0/142879/3426c42d-e0a6-c998-3a38-ce94e91c520c.png)

`JavaScriptSerializer`を使うために`System.Web.Extensions`を追加します。
![wbg11-04.png](https://qiita-image-store.s3.amazonaws.com/0/142879/15fad739-cbc8-6ace-41c0-b69ab21513d0.png)

## ◇設定読み書きクラス追加

プロジェクトにもう1つクラスを追加します。
名前は`Config.cs`にします。
`Person`プロパティと`Memo`プロパティが画面の項目と対応します。

```CSharp:Config.cs
using System.IO;
using System.Text;

namespace WpfApp11
{
    /// <summary>
    /// 設定クラス
    /// </summary>
    public class Config
    {
        #region フィールド定義

        /// <summary>
        /// 人
        /// </summary>
        public Person Person { get; set; }

        /// <summary>
        /// メモ
        /// </summary>
        public string Memo { get; set; }
        
        #endregion

        /// <summary>
        /// コンストラクタ
        /// </summary>
        public Config()
        {
            this.Person = new Person();
        }

        /// <summary>
        /// 設定ファイルのフルパスを取得
        /// </summary>
        /// <returns>設定ファイルのフルパス</returns>
        public static string GetConfigFilePath()
        {
            // 実行ファイルのフルパスを取得
            string appFilePath = System.Reflection.Assembly.GetEntryAssembly().Location;

            // 実行ファイルのフルパス末尾（拡張子）を変えて返す
            return System.Text.RegularExpressions.Regex.Replace(
                appFilePath,
                ".exe",
                ".json",
                System.Text.RegularExpressions.RegexOptions.IgnoreCase);
        }

        /// <summary>
        /// 設定読み込み
        /// </summary>
        /// <returns></returns>
        public static Config ReadConfig()
        {
            // 設定ファイルのフルパスを取得
            string configFile = GetConfigFilePath();

            if (File.Exists(configFile) == false)
            {
                // 設定ファイルなし
                return null;
            }

            using (var reader = new StreamReader(configFile, Encoding.UTF8))
            {
                // 設定ファイル読み込み
                string buf = reader.ReadToEnd();

                // デシリアライズして返す
                var js = new System.Web.Script.Serialization.JavaScriptSerializer();
                return js.Deserialize<Config>(buf);
            }
        }

        /// <summary>
        /// 設定書き込み
        /// </summary>
        /// <param name="cfg"></param>
        public static void WriteConfig(Config cfg)
        {
            // シリアライズ
            var js = new System.Web.Script.Serialization.JavaScriptSerializer();
            string buf = js.Serialize(cfg);

            // 設定ファイルのフルパス取得
            string configFile = GetConfigFilePath();

            using (var writer = new StreamWriter(configFile, false, Encoding.UTF8))
            {
                // 設定ファイルに書き込む
                writer.Write(buf);
            }
        }

    }
}
```

## ◇設定読み込み、保存処理作成

コードビハインドに、設定ファイルを読み込んで画面に反映する処理を追加します。

```CSharp:MainWindow.xaml.cs
/// <summary>
/// 設定読み込み、画面に反映
/// </summary>
private void LoadFromConfig()
{
    // 設定ファイルから読み込み
    var cfg = Config.ReadConfig();
    if (cfg == null)
    {
        // 設定ファイルなし
        return;
    }

    // 画面に反映
    nameText.Text = cfg.Person.Name;
    ageText.Text = cfg.Person.Age.ToString();
    cal.SelectedDate = cfg.Person.Birthday.ToLocalTime();
    memoText.Text = cfg.Memo;
}
```

コードビハインドに、画面内容を設定ファイルに保存する処理を追加します。

```CSharp
/// <summary>
/// 画面の内容を設定保存
/// </summary>
private void SaveToConfig()
{
    var cfg = new Config();

    cfg.Person.Name = nameText.Text;
    int age;
    if (int.TryParse(ageText.Text, out age) == false)
    {
        // 数値に変換できなかったら0にしておく
        age = 0;
    }
    cfg.Person.Age = age;
    cfg.Person.Birthday = (DateTime)cal.SelectedDate;
    cfg.Memo = memoText.Text;

    // 画面内容を設定ファイルに保存
    Config.WriteConfig(cfg);
}
```

`Window`の起動時に設定読み込み処理を呼ぶようにします。

```CSharp
/// <summary>
/// ウィンドウのコンテンツレンダリング後
/// </summary>
/// <param name="sender"></param>
/// <param name="e"></param>
private void Window_ContentRendered(object sender, EventArgs e)
{
    // 設定読み込み
    LoadFromConfig();
}
```

`Window`終了時に設定を保存する処理を呼ぶようにします。

```CSharp
/// <summary>
/// ウィンドウ閉じる時
/// </summary>
/// <param name="sender"></param>
/// <param name="e"></param>
private void Window_Closed(object sender, EventArgs e)
{
    // 設定保存
    SaveToConfig();
}
```

# ■動かしてみる

`Release`ビルドし、exeを起動してみます。
![wbg11-05.png](https://qiita-image-store.s3.amazonaws.com/0/142879/760f68c1-abf5-d84a-f471-300ca5e10252.png)

`TextBox`の内容を変更し、`Calendar`の日付を選択し、`×`ボタンで画面を閉じます。
![wbg11-06.png](https://qiita-image-store.s3.amazonaws.com/0/142879/c74e790e-955d-32bb-6691-cb42e1b24a66.png)

jsonファイルが作成されました。
![wbg11-07.png](https://qiita-image-store.s3.amazonaws.com/0/142879/5234f853-e156-38ef-0cfd-2ee0be99ae47.png)

jsonファイルをテキストエディタで開いてみます。
![wbg11-08.png](https://qiita-image-store.s3.amazonaws.com/0/142879/9341df75-c609-16e1-70f8-66618c50df6d.png)

再度exeを起動してみます。きちんと入力値が復元されました。
![wbg11-09.png](https://qiita-image-store.s3.amazonaws.com/0/142879/518468d5-a13b-f87e-1c8b-57ec70757a13.png)

# ■別の設定ファイル形式

jsonではなく、TOML形式を使う場合は [こちら](https://qiita.com/Kosen-amai/items/107f74cd29faa6b5002b "C#でTOML読み書き")


おしまい

---

[＜＜ 最初の記事](http://qiita.com/Kosen-amai/items/f9e3df2aa80363f5af5b "Windows GUIプログラミング入門1 メッセージボックス表示") 　 [＜ 前の記事](http://qiita.com/Kosen-amai/items/792c59392e72e65316bd "Windows GUIプログラミング入門10 設定保存(1)") 　 [次の記事 ＞](http://qiita.com/Kosen-amai/items/def339ea71cc69eeb9d0 "Windows GUIプログラミング入門12 リストビュー(1)")