[C#/WPF]Setting.settingを使用して、簡易的にアプリの設定値を保存/読出しする

# やりたいこと
WPFのアプリにて、例えばアプリの「設定画面」などでユーザーが設定した内容を、簡単に保存/読み出ししたい。
自前でファイルに書き出して保存し、それを読み出すこともできるとは思うが、めんどくさいのでもっと簡単にやりたい。

# 今回のやり方
VisualStudioの「プロジェクトデザイナーの設定ページ」というものを使う。
(→[こちら参照](https://docs.microsoft.com/ja-jp/visualstudio/ide/reference/settings-page-project-designer?f1url=https%3A%2F%2Fmsdn.microsoft.com%2Fquery%2Fdev15.query%3FappId%3DDev15IDEF1%26l%3DJA-JP%26k%3Dk(ApplicationSettingsOverview);k(TargetFrameworkMoniker-.NETFramework,Version%3Dv4.7)%26rd%3Dtrue&view=vs-2019))

ソリューションエクスプローラーの「Setting.setting」を開くか、
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/352601/dad17756-0e22-641b-39bb-0e453a5520ed.png)


プロジェクトのプロパティの中の「設定」を選択すると、「設定値」を設定するための画面が開く。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/352601/ac9d8424-dbd6-0e9e-1d07-bf64318c2283.png)

とりあえず上記のように4つの項目を作成して、それぞれの「名前」「種類」「値」をセットする。

**※スコープを「アプリケーション」にすると、その設定項目は読み取り専用となる。この画面で値をセットはできるが、プログラム動作中に書き換えたりできなくなるため、今回の目的では使えない。(アプリの設定を一括して持たせておく目的には便利)**

そうすると、下記のような書き方で、設定を読み書きすることができる。


```読み.cs
// MySetting1はstring型
string str = Properties.Settings.Default.MySetting1;
```

```書き.cs
// MySetting2はint型
Properties.Settings.Default.MySetting1 = 123;
Properties.Settings.Default.Save();
```

読み込みは、`Properties.Settings.Default.設定名`で読める。
書き込みは、その設定名に書き込んだあと、`Properties.Settings.Default.Save();`を行う。
Save()をすると、その後アプリを一度終了して再度起動したときも、Saveした値を覚えている。
※一つ書き込むたびにSaveせずに、いろいろな設定を全部設定し終わった後にSave()すると、設定した値をすべて一括にSaveできる。

**これを利用すると、アプリの設定画面でユーザーが入力した設定を、次回起動時に復元する、ということが実現できる。**

# 実験のためのサンプルコード
設定の実験のために、サンプルを作成した。下のように、各種設定とコード記述を行う。

### ■動作時画面
実験のため、下記のような動作をさせる。

- 起動時、Setting.settingに設定した値(4つ)を、画面表示する。
- テキストboxの横のボタンを押すと、各設定を保存する。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/352601/22bae8a6-6270-d304-1af8-65724ef39981.png)

### ■アプリケーション設定・アセンブリ設定
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/352601/51f78975-9ab7-793e-c952-e52501c43386.png)
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/352601/362e1bd1-9be3-ee98-86ef-da8f79a65b83.png)
※この辺の設定は、設定の保存先のパスにかかわってくる。

### ■Setting.setting
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/352601/3796b365-097c-b2e9-cd1a-bd9c813367f5.png)

### ■画面コード(xaml)

```MainWindows.xaml
<Window x:Class="DefaultNamespace.MainWindow"
        xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
        xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
        xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
        xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
        xmlns:local="clr-namespace:WpfApp30"
        mc:Ignorable="d"
        Title="MainWindow" Height="150" Width="300">
    <Grid>
        <Grid.RowDefinitions>
            <RowDefinition Height="*"/>
            <RowDefinition Height="*"/>
            <RowDefinition Height="*"/>
            <RowDefinition Height="*"/>
        </Grid.RowDefinitions>
        <Grid.ColumnDefinitions>
            <ColumnDefinition Width="3*"/>
            <ColumnDefinition Width="1*"/>
        </Grid.ColumnDefinitions>

        <TextBox Name="txt1" Grid.Row="0"/>
        <TextBox Name="txt2" Grid.Row="1"/>
        <TextBox Name="txt3" Grid.Row="2"/>
        <TextBox Name="txt4" Grid.Row="3"/>

        <Button Name="bt1" Grid.Row="0" Grid.Column="1" Content="string" Click="bt1_Click"/>
        <Button Name="bt2" Grid.Row="1" Grid.Column="1" Content="int" Click="bt2_Click"/>
        <Button Name="bt3" Grid.Row="2" Grid.Column="1" Content="bool" Click="bt3_Click"/>
        <Button Name="bt4" Grid.Row="3" Grid.Column="1" Content="double" Click="bt4_Click"/>
    </Grid>
</Window>
```

### ■画面コード(cs)

```MainWindow.xaml.cs
using System.Windows;
namespace DefaultNamespace
{
    /// <summary>
    /// MainWindow.xaml の相互作用ロジック
    /// </summary>
    public partial class MainWindow : Window
    {
        //設定値を読み書きするためのプロパティを作成

        private string MyString
        {
            get { return Properties.Settings.Default.MySetting1; }
            set { Properties.Settings.Default.MySetting1 = value; }
        }
        private int MyInt
        {
            get { return Properties.Settings.Default.MySetting2; }
            set { Properties.Settings.Default.MySetting2 = value; }
        }
        private bool MyBool
        {
            get { return Properties.Settings.Default.MySetting3; }
            set { Properties.Settings.Default.MySetting3 = value; }
        }
        private double MyDouble
        {
            get { return Properties.Settings.Default.MySetting4; }
            set { Properties.Settings.Default.MySetting4 = value; }
        }

        // 起動時に設定の値を読み込み、画面表示
        public MainWindow()
        {
            InitializeComponent();

            txt1.Text = MyString;
            txt2.Text = MyInt.ToString();
            txt3.Text = MyBool.ToString();
            txt4.Text = MyDouble.ToString();
        }

        // 画面に入力した値を保存する

        private void bt1_Click(object sender, RoutedEventArgs e)
        {
            MyString = txt1.Text;
            Properties.Settings.Default.Save();
        }

        private void bt2_Click(object sender, RoutedEventArgs e)
        {
            MyInt = int.Parse(txt2.Text);
            Properties.Settings.Default.Save();
        }

        private void bt3_Click(object sender, RoutedEventArgs e)
        {
            MyBool = bool.Parse(txt3.Text);
            Properties.Settings.Default.Save();
        }

        private void bt4_Click(object sender, RoutedEventArgs e)
        {
            MyDouble = double.Parse(txt4.Text);
            Properties.Settings.Default.Save();
        }
    }
}
```

# Save()したものはどこに保存されている？
下記に、user.configファイルとして保存されている。

### ■ファイルパス
`C:\Users\<ユーザー名>\AppData\Local\<会社名>\<アプリ名>.exe_Url_<ハッシュ値>\<バージョン>\user.config`

### ■ファイルの内容

```xml
<?xml version="1.0" encoding="utf-8"?>
<configuration>
    <configSections>
        <sectionGroup name="userSettings">
            <section name="DefaultNamespace.Properties.Settings" type="System.Configuration.ClientSettingsSection, System, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089" allowExeDefinition="MachineToLocalUser" requirePermission="false" />
        </sectionGroup>
    </configSections>
    <userSettings>
        <DefaultNamespace.Properties.Settings>
            <setting name="MySetting1" serializeAs="String">
                <value>DEF</value>
            </setting>
            <setting name="MySetting3" serializeAs="String">
                <value>True</value>
            </setting>
            <setting name="MySetting2" serializeAs="String">
                <value>10</value>
            </setting>
            <setting name="MySetting4" serializeAs="String">
                <value>999.3</value>
            </setting>
        </DefaultNamespace.Properties.Settings>
    </userSettings>
</configuration>
```

### ■ファイルの動き
アプリ起動時やSave()で保存したときの動きを見る限り、下記のような動作をしている。

- アプリの初回起動時は、そのパスにファイルは存在しない。(フォルダも存在してない)
- ファイルがないときは、画面で設定した初期値が使われる。(user.config)削除すると、初期値にもどる。
- 最初に`Properties.Settings.Default.Save();`を実行したときに、ファイル、フォルダができる。
- user.configの中には、すべての設定項目があるわけではない。
- 値が代入された設定値だけ、ファイルに保存される。(もちろん代入した後にSave()された時だけファイルに保存される)
- ファイル内に値の記述がない設定値は、初期値扱いになる。
- 元の値と同じ値を代入後Saveしたときも、ファイルに保存される。

### ■注意
アプリ(exe)のバージョンが上がると、user.configファイルの保存場所が変わる。
例えばバージョンが「1.2.3.4」から「1.2.3.5」に上がったとすると、
`C:\Users\<ユーザー名>\AppData\Local\CompanyName\AssemblyName.exe_Url_offobezk1v5z2srsyclwkbtlysxbum4f\1.2.3.4\user.config`
から
`C:\Users\<ユーザー名>\AppData\Local\CompanyName\AssemblyName.exe_Url_offobezk1v5z2srsyclwkbtlysxbum4f\1.2.3.5\user.config`
に変わる。

そのため、
#### このやり方では、**「ソフトのバージョンが上がった場合にもユーザーの設定を引き継ぎたい」**という要求は実現することができない。要注意。

# 参考
Settings page, Project Designer（msdocsのページ）
https://docs.microsoft.com/ja-jp/visualstudio/ide/reference/settings-page-project-designer?f1url=https%3A%2F%2Fmsdn.microsoft.com%2Fquery%2Fdev15.query%3FappId%3DDev15IDEF1%26l%3DJA-JP%26k%3Dk(ApplicationSettingsOverview);k(TargetFrameworkMoniker-.NETFramework,Version%3Dv4.7)%26rd%3Dtrue&view=vs-2019