画面を伴うプログラムを自作していて、画面から入力した値を保存しておき、次に起動したときに復元させたい時があります。
そのようなときに使える方法として、アプリケーション構成ファイル(App.config) がありますが、値を保存/復元したいコントロール1つ1つに対して、App.configへの保存/読み出しコードを記述しなければならず、それなりに面倒です。
そこで、プログラム起動時にコントロールのリストを定義しておき、一括して保存/復元できる便利クラスを作ってみましたので、紹介したいと思います。
概要
今回の自作クラス(AppSettings)は、内部で ConfigurationManager を利用しており、インスタンス生成時(new AppSettingsのとき)にApp.config から値を読み込み、コントロールに値を復元します。
また、AppSettingsの Saveメソッドを呼んだタイミングで、コントロールの値をApp.config に保存します。
ConfigurationManagerを使ったApp.configへの保存/復元方法については、「【すぐに使える】C#で app.configに設定値を保存・取得する(実用サンプル付き)」の記事で詳しく紹介していますので、興味のある方はご一読ください。
仕組み
インスタンス生成のタイミングで読み込んだApp.Configの値は、辞書(Dictionary)としてクラス内部に保持しています。
また、Saveメソッドにより辞書の内容をApp.config に保存しています。
引数にコントロール配列を渡した場合は、そのままクラス内部に保持すると同時に、コントロール名(XAMLに記述した x:Name の値)と辞書と突き合わせ、値を復元しています。
Saveメソッドが呼ばれると、クラス内部に保持されたコントロール配列からコントロールごとの値を取り出し、いったん辞書に反映させた後で、辞書をまるごとApp.Configに保存しています。
以上の説明から分かるように、インスタンス生成時に辞書が作成され、Saveメソッド呼び出し時にコントロールの値で辞書が上書かれるため、途中で辞書の内容を変更しても、App.Configには反映されないのでご注意ください。
保存/復元可能なコントロールの値について
コントロールが持つ全ての値を保存/復元できる訳ではありません。
現時点では、Textプロパティと、IsCheckedプロパティの値のみ保存/復元が可能です。
例えば、TextBox、TextBlock、ComboBox、CheckBox などが該当します。
これ以外のコントロール、例えばLabelやDataGrid、ListBox、DatePickerなどについても対応させたい場合は、適宜必要に応じてコードの追加をしてください。
具体的は方法は、ソースコード紹介の後で記載しています。
ユーザーコントロールの値を保存/復元するには
自作のユーザーコントロールが持つ値を保存/復元したい場合は、Textプロパティを利用する方法がおすすめです。
Textプロパティが参照された時は、ユーザーコントロール内に存在する子のコントロールの値を、カンマ又はタブコードなどで区切り、1つの文字列として返します。
Textプロパティに代入された時は、その値をSplit等で分割し、子のコントロールのプロパティに代入します。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 |
public partial class MyUserControl : UserControl { //Textプロパティ public string Text { get {return Get(); } set { Set(value); }} public MyUserControl() { InitializeComponent(); } //引数の文字列を分解して、子のコントロールのプロパティに代入 public void Set(string text) { var values = text.Split(','); uxKey.Text = values[0]; uxShift.IsChecked = bool.Parse(values[1]); uxCtrl.IsChecked = bool.Parse(values[2]); } //子のコントロールのプロパティの値を結合して1つの文字列にする public string Get() { return String.Join(",", new string[] { uxKey.Text, uxShift.IsChecked.ToString(), uxCtrl.IsChecked.ToString(), }); } } |
任意のキーによる値の保存/復元
クラス内部に保持した辞書に任意のキーで値を登録すると、それも合わせて自動で保存/復元します。
従って、コントロール以外の値(ただしテキストのみ)を管理することも可能です。
クラス内部の辞書にアクセスするには、通常の辞書と同様にインデクサを使うことが出来ます。
1 2 3 4 5 6 7 8 9 |
//インスタンスの生成 var settings = new AppSettings(); //"Posision"で文字列"999"を登録 settings["Posision"] = "999"; //"Posision"の値を取得 string str = settings["Posision"]; |
使い方
使い方は簡単で、値の保存/復元が必要なコントロールの配列を作成し、それを引数にしてAppSettingsのインスタンスを new で生成します。
これだけでコントロールのText又はIsCheckedプロパティの値が復元されます。
また、画面操作で変更されたコントロールの値は、保存したいタイミングでSave メソッドを呼び出すだけで完了します。
下記はそのサンプルコードです。個人的な命名規則によりコントロール名の先頭は ux で統一していますが、必ずしも ux から始める必要はありません。
1 2 3 4 5 6 7 8 9 10 11 |
var controls = new System.Windows.Controls.Control[] { uxAllScreen,uxActiveWindow,uxRectangle,uxClipboard }; //コントロール配列を引数としてインスタンスを生成 var settings = new AppSettings(controls); //コントロールの中身をapp.configに保存 settings.Save(); |
画面起動時に値を復元、終了時に値を保存するには
多くのケースでは、画面起動時にコントロールの値を復元し、画面終了時にコントロールの値を保存したいでしょう。
その場合、クラス変数として _settings を用意しておき、Loadedイベントハンドラ内でインスタンスを生成、_settings に格納しておきます。
そして、Closedイベントハンドラ内 にSaveメソッドを記述しておけば、画面終了時にコントロールの値が保存できます。
具体的には以下の様になります。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 |
public partial class MainWindow : Window { //異なるハンドラ(Loaded、Closed)で共通で使うための保存用 private AppSettings _settings; //コンストラクタ public MainWindow() { InitializeComponent(); } /// <summary> /// 画面 /// </summary> /// <param name="sender"></param> /// <param name="e"></param> private void Window_Loaded(object sender, RoutedEventArgs e) { //値を保存/復元したいコントロール配列を作成 var controls = new System.Windows.Controls.Control[] { uxAllScreen,uxActiveWindow,uxRectangle,uxClipboard }; //インスタンスを生成 _settings = new AppSettings(controls); } /// <summary> /// 画面の終了処理 /// </summary> /// <param name="sender"></param> /// <param name="e"></param> private void Window_Closed(object sender, EventArgs e) { //コントロールの内容をapp.configに保存 _settings.Save(); } } |
任意の値を保存、復元するには
任意の値をApp.configに保存したり、復元(取得)するには、クラス内部の辞書にアクセスする必要がありますが、以下の通りインデクサが利用可能です。
インスタンス変数[“任意のキー名”]
例えば、 以下の様に記述すると、”Age” というキーで “19”という文字列が登録されます。
辞書にキーがなければ新しく作られ、あれば追加されます。
また、削除するための Remove メソッドも用意しています。
1 2 3 4 5 |
var app = new AppSettings(); app["Age"] = "19"; //指定したキーで値を登録(又は変更) Console.WriteLine(app["Age"]); //指定したキーで値を取得 app.Remove("Age"); //指定したキーを削除 |
ConfigurationManager を使った App.config は文字列しか保存できませんので、取得する際に必要に応じて型変換を行います。
AppSettingsクラスのソースコード
以下が今回紹介したクラスのソースコードです。
Textプロパティを持つコントロールとチェックボックスに対応していますので、それで事足りる場合はそのままコピペしてお使いいただけます。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 |
using System; using System.Collections.Generic; using System.Configuration; using System.IO; using System.Linq; using System.Text; using System.Windows.Forms; using System.Windows.Input; namespace MyLib { public class AppSettings { private Configuration _configuration = ConfigurationManager.OpenExeConfiguration(ConfigurationUserLevel.None); private Dictionary<string, string> Dic { get; set; } = new Dictionary<string, string>(); public string this[string name] { get { return Get(name); } set { Set(name, value); } } public System.Windows.Controls.Control[] Controls { get; set; } = null; public AppSettings(System.Windows.Controls.Control[] controls = null) { //コントロール配列が指定されていたら、プロパティに保存 Controls = controls; //App.configに存在するキーと値を辞書に読み込む foreach (var key in _configuration.AppSettings.Settings.AllKeys) { //キーに対応する値を取得 var value = _configuration.AppSettings.Settings[key].Value; //辞書に登録 Dic.Add(key, value); } //引数にコントロールが指定されていたら、該当するプロパティを持つコントロールに値を設定 if(Controls != null) { RestoreProperty("Text"); RestoreProperty("IsChecked"); } } /// <summary> /// コントロールに設定値を復元 /// </summary> /// <param name="propertyName"></param> private void RestoreProperty(string propertyName) { foreach (var control in Controls) { //指定したコントロール名が辞書に存在する場合 if (Dic.ContainsKey(control.Name)) { //コントロールに指定したプロパティがあるかをチェック var property = control.GetType().GetProperty(propertyName); //プロパティが見つかった場合 if (property != null) { //AppSettingsから値を取り出してプロパティに代入 if (control.GetType() == typeof(System.Windows.Controls.CheckBox)) property.SetValue(control, bool.Parse(Get(control.Name))); else property.SetValue(control, Get(control.Name)); } } } } /// <summary> /// コントロールの内容を設定値として保存 /// </summary> /// <param name="propertyName"></param> private void StoreProperty(string propertyName) { foreach (var control in Controls) { //コントロールに指定したプロパティがあるかをチェック var property = control.GetType().GetProperty(propertyName); //プロパティが見つかった場合 if (property != null) { //プロパティから値を取得してAppSettingsに保存 Set(control.Name,property.GetValue(control)?.ToString()); } } } /// <summary> /// 指定したキー名で辞書から値を参照 /// キーが存在しなければ、値が空の新しいキーを登録し、その値を返す /// </summary> /// <param name="key"></param> /// <returns></returns> public string Get(string key) { if (!Dic.ContainsKey(key)) { Set(key, ""); } return Dic[key]; } /// <summary> /// 指定したキーで辞書とApp.configに値を登録 /// キーが存在しなければ、新しいキーを登録する /// </summary> /// <param name="key"></param> /// <param name="value"></param> public void Set(string key, string value) { if (!Dic.ContainsKey(key)) { Dic.Add(key, value); _configuration.AppSettings.Settings.Add(key, value); } Dic[key] = value; _configuration.AppSettings.Settings[key].Value = value; } /// <summary> /// 指定したキーを削除する /// </summary> /// <param name="key"></param> public void Remove(string key) { Dic.Remove(key); _configuration.AppSettings.Settings.Remove(key); } /// <summary> /// App.config に保存する /// </summary> public void Save() { if (Controls != null) { StoreProperty("Text"); StoreProperty("IsChecked"); } _configuration.Save(); } } } |
新しいコントロールに対応させるには
新しいコントロールに対応させるというより、新しいプロパティに対応させるといった方が適切かもしれません。
まず、コンストラクタとSaveメソッド内に、対応させたいプロパティ名を記述します。
次に、RestorePropertyメソッド内に、プロパティ毎の型変換の処理を追加します。
ここでは、コントロールがCheckBoxの場合、bool に変換していますが、これはIsCheckedのプロパティが bool 型だからです。
CheckBoxに対して複数のプロパティ(例えば IsChecked、Content)を保存したい場合、今のままでは全てbool型に変換されてしまうので、プロパティ名で分岐(例えば IsChecked なら bool 、Content なら文字列)するなどの対応が必要です。
もしRadioButton に対応させたいなら、if 文にCheckBoxとRadioButtonの2つを記述するか、あるいは コントロールを見るのではなく、プロパティ名を見て型変換を使うなどの工夫が必要です。
この辺は、ご自身の用途や好みで修正してお使いください。
まとめ
今回は、コントロールの値(=プロパティの値)をApp.configに保存/復元する際に便利な自作クラス「AppSettings」について、その仕組みと使い方の説明、ソースコード一式の紹介を行いました。
ConfigurationManager の機能を呼び出すだけの自作クラスではありますが、間に辞書を挟むことによって、コントロールの値の保存/復元がかなり便利になるかと思います。
また、任意の値についても App.config へ保存/復元が可能なので、変数の値をファイルで管理したい場合にも活用できます。
コントロールの値や変数の値をファイルに保存して、プログラム起動時に復元したい場合は、是非この記事をお役立てください。
コメント