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

# ■はじめに
今回は設定の保存/読み込みをやります。

キーワード：Settings, マルチユーザー設定, user.config, Local, Roaming, Style, 初期フォーカス, スタイル

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

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

# ■作ってみる

## ◇Grid分割、StackPanel配置

新規プロジェクトを作成したら`Grid`を横に区切りましょう。
やり方を忘れた場合は[第5回](http://qiita.com/Kosen-amai/items/f2451acb2addd221193f "Windows GUIプログラミング入門5 画面レイアウト(2)、画面遷移(1)")に戻りましょう。

![wbg10-01.png](https://qiita-image-store.s3.amazonaws.com/0/142879/c7dfb0ab-5645-386b-fc11-454c6f27bef9.png)

区切ったそれぞれの行に`StackPanel`をドラッグ＆ドロップします。
![wbg10-02.png](https://qiita-image-store.s3.amazonaws.com/0/142879/4de1ce35-5e25-ef4d-401b-f7d21e343b65.png)

ドキュメントアウトラインで`StackPanel`を<kbd>Ctrl</kbd> + クリックで2つとも選択状態にし、右クリック「レイアウト」 - 「すべてリセット」を選択します。
![wbg10-03.png](https://qiita-image-store.s3.amazonaws.com/0/142879/6962bd69-abd6-dc1b-e3e2-8511a951ad52.png)

`StackPanel`が画面いっぱいに広がりました。
![wbg10-04.png](https://qiita-image-store.s3.amazonaws.com/0/142879/e9a8569f-9440-6d06-f3e0-94c2fff00672.png)

下側の`StackPanel`の`Orientation`を`Horizontal`に、`HorizontalAlignment`を`Right`にします。
これで中に配置するコントロールが横並びかつ右寄せになります。
![wbg10-05.png](https://qiita-image-store.s3.amazonaws.com/0/142879/ca56ca4f-463a-c083-4757-547ed3f8bcf7.png)

## ◇ボタン配置、スタイル設定

ドキュメントアウトラインで下側の`StackPanel`を選択状態にし、`Button`を配置します。
![wbg10-06.png](https://qiita-image-store.s3.amazonaws.com/0/142879/be7404e7-cf27-5310-1986-c6e2c8c6e84f.png)

`Grid`の行高さの設定を変更します。
ドロップダウンから`Auto`を選択します。
![wbg10-07.png](https://qiita-image-store.s3.amazonaws.com/0/142879/77b56d53-918f-22e3-b941-5960a797cf91.png)

`Window`タグのすぐ下に`Window.Resources`タグを作り、`Button`のスタイルを定義します。
![wbg10-08.png](https://qiita-image-store.s3.amazonaws.com/0/142879/352edae3-8704-cf9d-2b52-658be3ce2bcd.png)

```Xml:ボタンのスタイル定義
<Window.Resources>
    <Style TargetType="Button">
        <Setter Property="Margin" Value="0,10,10,10"/>
        <Setter Property="Width" Value="100"/>
        <Setter Property="Height" Value="30"/>
    </Style>
</Window.Resources>
```

`Button`を1つ追加し、各種設定をします。
![wbg10-09.png](https://qiita-image-store.s3.amazonaws.com/0/142879/95e57588-ed5d-cbc4-a940-a5499cdc0413.png)

```Xml:ボタン定義
<StackPanel Grid.Row="1" Orientation="Horizontal" HorizontalAlignment="Right">
    <Button Content="ボタン"/>
    <Button x:Name="closeButton" Content="閉じる(_C)"/>
</StackPanel>
```

閉じるボタンの処理を書きます。

```CSharp
private void closeButton_Click(object sender, RoutedEventArgs e)
{
    this.Close();
}
```

## ◇入力項目配置、スタイル設定

ドキュメントアウトラインで上側の`StackPanel`を選択し、`Label`, `TextBox`, `Label`, `TextBox`, `Label`, `ComboBox`の順に追加します。
![wbg10-10.png](https://qiita-image-store.s3.amazonaws.com/0/142879/cea3f9ae-f1ff-2a3a-29b4-503d799859da.png)

各種設定をします。

```Xml
<StackPanel>
    <Label Content="名前(_N)"/>
    <TextBox x:Name="nameText" Text="トメ"/>
    <Label Content="歳(_A)"/>
    <TextBox x:Name="ageText" Text="88"/>
    <Label Content="血液型(_B)"/>
    <ComboBox x:Name="blood" Margin="5,0" Width="100" HorizontalAlignment="Left"/>
</StackPanel>
```

![wbg10-11.png](https://qiita-image-store.s3.amazonaws.com/0/142879/eb34120d-c837-31ed-f628-265af3403451.png)

`Window.Resources`に`Label`と`TextBox`のスタイル定義を追加します。
今度は`x:Key`でスタイルの名前を付けます。

```Xml
<Style x:Key="InputTitleStyle" TargetType="Label">
    <Setter Property="Margin" Value="5,15,5,0"/>
</Style>
<Style x:Key="InputTextStyle" TargetType="TextBox">
    <Setter Property="Margin" Value="5,0"/>
    <Setter Property="Width" Value="300"/>
    <Setter Property="HorizontalAlignment" Value="Left"/>
</Style>
```

利用する側は`Style`プロパティで適用したいスタイルの`Key`を指定します。

```Xml
<Label Content="名前(_N)" Target="{Binding ElementName=nameText}" Style="{StaticResource InputTitleStyle}"/>
<TextBox x:Name="nameText" Text="トメ" Style="{StaticResource InputTextStyle}"/>
<Label Content="歳(_A)" Target="{Binding ElementName=ageText}" Style="{StaticResource InputTitleStyle}"/>
<TextBox x:Name="ageText" Text="88" Style="{StaticResource InputTextStyle}"/>
<Label Content="血液型(_B)" Target="{Binding ElementName=blood}" Style="{StaticResource InputTitleStyle}"/>
```

`ComboBox`の`Items`プロパティ横の「`...`」からコレクションエディターを開き、`ComboBoxItem`を追加します。
![wbg10-12.png](https://qiita-image-store.s3.amazonaws.com/0/142879/1c444c68-7212-c472-3ca5-ff17b029e64b.png)

4つの`ComboBoxItem`の`Content`にそれぞれ`A`, `B`, `O`, `AB`を設定します。
![wbg10-13.png](https://qiita-image-store.s3.amazonaws.com/0/142879/effdf560-52dd-49db-9446-dd438cd0f2e7.png)

```Xml
<ComboBox x:Name="blood" Margin="5,0" Width="100" HorizontalAlignment="Left">
    <ComboBoxItem Content="A"/>
    <ComboBoxItem Content="B"/>
    <ComboBoxItem Content="O"/>
    <ComboBoxItem Content="AB"/>
</ComboBox>
```

画面起動時の最初のフォーカス位置を設定します。
![wbg10-13_2.png](https://qiita-image-store.s3.amazonaws.com/0/142879/72c69c50-ac85-a197-d444-a5099d3fcab8.png)

```Xml
<Grid FocusManager.FocusedElement="{Binding ElementName=nameText}">
```

## ◇設定項目

ソリューションエクスプローラーで`Properties`の下の`Settings.settings`をダブルクリックします。
![wbg10-14.png](https://qiita-image-store.s3.amazonaws.com/0/142879/6605b8ae-c716-f703-9f31-668b02d799be.png)

以下の表のように入力します。

| 名前 | 種類 | スコープ | 値 |
|:--|:--|:--|:--|
| WindowTitle  | string  | アプリケーション  | 設定ファイルテスト  |
| UserName  | string  | ユーザー  |   |
| Age  | int  | ユーザー  | 0  |
| Blood  | int  | ユーザー  | -1  |
| ProfileConfig1  | string  | ユーザー  | あああ  |

`ProfileConfig1`の`Roaming`プロパティは`True`にします。
![wbg10-15.png](https://qiita-image-store.s3.amazonaws.com/0/142879/b6d2453a-e564-0448-dd4a-64a247fee386.png)

**アプリケーションスコープ**は、全ユーザーで共通的な固定値に使用します。
**ユーザースコープ**は、ログインユーザー毎に保持する設定で使用します。
ユーザースコープは端末依存/非依存で2種類あります。
既定値は`Roaming`=`False`の端末依存です。

まとめると以下のようになります。

* 全ユーザーで共通の固定値は、アプリケーションスコープにする。
* ユーザー固有で端末に依存する設定（画面解像度や色数、ドライブ・フォルダパス等）は、ユーザースコープで`Roaming`を`False`にする。
* ユーザー固有で端末に依存しない設定は、ユーザースコープで`Roaming`を`True`にする。

| スコープ | Roaming | 読取専用 | 端末依存 | ユーザー固有 | 保存先 |
|:--|:--|:--|:--|:--|:--|:--|
| アプリケーション  | `False`  | 〇 |  |  | `〇〇〇.exe.config`  |
| ユーザー  | `False`  |   | 〇 | 〇 | `Local\～\user.config`  |
| ユーザー  | `True`  |   |  | 〇 | `Roaming\～\user.config`  |


設定読み込み/保存処理を書きます。
`Window`のイベント、`ContentRendered`に設定読み込み処理を、`Closed`に設定保存処理を書きます。
![wbg10-16.png](https://qiita-image-store.s3.amazonaws.com/0/142879/b37ab523-4117-9191-9e9a-603a62a74854.png)

```CSharp
private void Window_ContentRendered(object sender, EventArgs e)
{
    // 設定読み込み
    this.Title = Properties.Settings.Default.WindowTitle;
    nameText.Text = Properties.Settings.Default.UserName;
    ageText.Text = Properties.Settings.Default.Age.ToString();
    blood.SelectedIndex = Properties.Settings.Default.Blood;
}

private void Window_Closed(object sender, EventArgs e)
{
    // 設定保存
    Properties.Settings.Default.UserName = nameText.Text;
    Properties.Settings.Default.Age = int.Parse(ageText.Text);
    Properties.Settings.Default.Blood = blood.SelectedIndex;
    Properties.Settings.Default.Save();
}
```

閉じるボタンの左に配置したボタンのクリック処理を書きます。

```CSharp
private void Button_Click(object sender, RoutedEventArgs e)
{
    // 設定読み込み
    string config1 = Properties.Settings.Default.ProfileConfig1;
    // 表示
    MessageBox.Show(config1);
    // 設定更新
    Properties.Settings.Default.ProfileConfig1 = config1 + "a";
}
```

## ◇プロジェクトプロパティ設定
プロジェクトのプロパティで「アプリケーション」の「アセンブリ情報」ボタンを押します。
![wbg10-30.png](https://qiita-image-store.s3.amazonaws.com/0/142879/105e707c-45df-9657-ff16-8dfc244cd93a.png)

「会社」の欄を入力します。
![wbg10-31.png](https://qiita-image-store.s3.amazonaws.com/0/142879/3c1fffb0-5a25-813a-1deb-ef8968253a30.png)

ここでは`TestCompany1`と入力しました。
![wbg10-32.png](https://qiita-image-store.s3.amazonaws.com/0/142879/635f0198-baa6-aa01-f5e1-e7167087da31.png)

## ◇実行

`Release`ビルドします。
![wbg10-17.png](https://qiita-image-store.s3.amazonaws.com/0/142879/c7a4eba9-b9fa-8662-deb3-d32a724dbba9.png)

`Release`フォルダを開きます。
配布時は`〇〇〇.exe`と`〇〇〇.exe.config`を渡してください。

exeを起動してみます。
![wbg10-18.png](https://qiita-image-store.s3.amazonaws.com/0/142879/015c06c5-6681-1890-6829-50d3a873c1bf.png)

「ボタン」ボタンを押すとメッセージが表示されます。
テキストボックスを入力、コンボボックスを選択し、閉じるボタンで終了します。
ウィンドウが終了したのでconfigが更新されているはずです。
![wbg10-19.png](https://qiita-image-store.s3.amazonaws.com/0/142879/8958acd0-b333-4580-cf40-df5ae3764eea.png)

exeと同じ場所にある`〇〇〇.exe.config`ファイルは初期値で、ユーザー設定があればそちらが優先されます。
実行時にここの値が書き換わることはありません。

```Xml:〇〇〇.exe.config（抜粋）
<userSettings>
    <WpfApp10.Properties.Settings>
        <setting name="UserName" serializeAs="String">
            <value />
        </setting>
        <setting name="Age" serializeAs="String">
            <value>0</value>
        </setting>
        <setting name="Blood" serializeAs="String">
            <value>-1</value>
        </setting>
        <setting name="ProfileConfig1" serializeAs="String">
            <value>あああ</value>
        </setting>
    </WpfApp10.Properties.Settings>
</userSettings>
<applicationSettings>
    <WpfApp10.Properties.Settings>
        <setting name="WindowTitle" serializeAs="String">
            <value>設定ファイルテスト</value>
        </setting>
    </WpfApp10.Properties.Settings>
</applicationSettings>
```

## ◇Localフォルダ

エクスプローラーで隠しファイルを表示するようにしておいて、`ユーザー\【ユーザー名】\AppData`（`Users\【ユーザー名】\AppData`）フォルダを開きます。
`Local`フォルダ配下にはスコープ：ユーザー、`Roaming`:`False`の設定が、
`Roaming`フォルダ配下にはスコープ：ユーザー、`Roaming`:`True`の設定が格納されています。
![wbg10-20.png](https://qiita-image-store.s3.amazonaws.com/0/142879/e0376b40-5af3-b2bd-c60e-7e1859cb947c.png)

`Local\【会社名】\【アプリ名.exe_長い文字列】\バージョン番号`
（アセンブリ情報で「会社」を未入力の場合は`Local\【アプリ名】\【アプリ名.exe_長い文字列】\バージョン番号`）フォルダにある`user.config`（ローカルユーザープロファイル）を開きます。
![wbg10-21.png](https://qiita-image-store.s3.amazonaws.com/0/142879/e45392d0-d6f0-aea1-0000-977812e9a20c.png)


画面で入力された内容で更新されています。

```Xml:Localのuser.config
<?xml version="1.0" encoding="utf-8"?>
<configuration>
    <userSettings>
        <WpfApp10.Properties.Settings>
            <setting name="UserName" serializeAs="String">
                <value>なまえ★なまえ</value>
            </setting>
            <setting name="Age" serializeAs="String">
                <value>999</value>
            </setting>
            <setting name="Blood" serializeAs="String">
                <value>3</value>
            </setting>
        </WpfApp10.Properties.Settings>
    </userSettings>
</configuration>
```

## ◇Roamingフォルダ

`Roaming\【会社名】\【アプリ名.exe_長い文字列】\バージョン番号`
（アセンブリ情報で「会社」を未入力の場合は`Roaming\【アプリ名】\【アプリ名.exe_長い文字列】\バージョン番号`）フォルダにある`user.config`（移動ユーザープロファイル）を開きます。
![wbg10-22.png](https://qiita-image-store.s3.amazonaws.com/0/142879/b135900f-b224-effb-c24f-fabc43203805.png)


「ボタン」ボタンを押してメッセージを表示したので内容が更新されています。

```Xml:Roamingのuser.config
<?xml version="1.0" encoding="utf-8"?>
<configuration>
    <userSettings>
        <WpfApp10.Properties.Settings>
            <setting name="ProfileConfig1" serializeAs="String">
                <value>あああa</value>
            </setting>
        </WpfApp10.Properties.Settings>
    </userSettings>
</configuration>
```

## ◇config読み込み

再度exeを起動してみます。
configの内容が復元されました。
![wbg10-23.png](https://qiita-image-store.s3.amazonaws.com/0/142879/bdc25b26-770b-62ab-a164-d0dbf97f18a8.png)


おしまい

---

[＜＜ 最初の記事](http://qiita.com/Kosen-amai/items/f9e3df2aa80363f5af5b "Windows GUIプログラミング入門1 メッセージボックス表示") 　 [＜ 前の記事](http://qiita.com/Kosen-amai/items/2c03e1342fb6f042effc "Windows GUIプログラミング入門9 ラベル、テキスト、選択系") 　 [次の記事 ＞](http://qiita.com/Kosen-amai/items/7835aac8df7cc925eddd "Windows GUIプログラミング入門11 設定保存(2)")