先日公開したマップエディタアルファ1を皆様にご試用頂き、数名から感想と要望を頂きました。その中には最近使ったファイルを実装して欲しいというのがあり、私自身もいちいち [Ctrl]+[O] で読み込むのは面倒だなあと感じていた事もあり、対応する事にしました。

今まで、設定の保存は Properties.Settings.Default に行っていました。これ、時々よく分からないタイミングで初期化されるんですよね。さらに List<Point> が保存出来ない事もあり、自前実装に切り替える事にしました。保存場所は Application.UserAppDataPath ですが、そのままだとバージョン番号の場所に保存されるので、その一つ前の場所にします。

private static string GetFilePath() { var idx = Application.UserAppDataPath.LastIndexOf('\\'); var path = Application.UserAppDataPath [..idx]; return $"{path}\\Configuration.ini"; }
名前こそ ini ですが、中身は xml にします。xml のパースは私は Json.NET を好んで使っています。なんたって扱いやすいです。さて、Properties.Settings.Default の置き換えなので、なるべく使い方を同じにしたく、かつ、プロパティは  Json.NET で一発保存出来るように、まずは設定のプロパティ名を Properties.Settings.Default と全く同じ名前にして定義しました。

// 設定ファイル public string CultureName { get; set; } public Point MdiLocation { get; set; } public Size MdiSize { get; set; } public Point MdiLayerLocation { get; set; } public Size MdiLayerSize { get; set; } public Point FormLocation { get; set; } public Size FormSize { get; set; } public bool IsGrid { get; set; } public bool IsAreaLines { get; set; } public bool IsEditCursor { get; set; } public int ZoomCounter { get; set; } public bool IsDisplayOffResizing { get; set; } public bool IsOutOfArea { get; set; } public int MdiObjViewIndex { get; set; } public List<string> RecentlyUsedFiles { get; set; }
このプロパティを持つクラスを Configuration として作成しました。Configuration オブジェクトはメインフォームに readonly で起動直後にインスタンスしておきます。

public static readonly Configuration Config = new();
これで置き換え準備が出来ましたので、メインフォームに対して置換します。
置換
これでまとめて置き換わりました。が、いくつか当然エラーが出ます。また、以前と違い初期化も必要です。エラーの多くは未実装メソッドです。Properties.Settings.Default.Reset() などを同じ名前で実装してやります。最初に取りかかったのは、GetDefault<T>(string name) の実装です。プロパティのデフォルト値を取得する関数ですが、自前実装ならプロパティは確定しているので、ひとつずつ専用関数を作ってしまう事にしました。

// ------------------------------------------------------------- // デフォルト値を取得する // ------------------------------------------------------------- public static string DefaultCultureName => string.Empty; public static Point DefaultMdiLocation => Point.Empty; public static Size DefaultMdiSize => new(256, 300); public static Point DefaultMdiLayerLocation => new(0, 300); public static Size DefaultMdiLayerSize => new(256, 320); public static Point DefaultFormLocation => Point.Empty; public static Size DefaultFormSize => new(800, 600); public static bool DefaultIsGrid => true; public static bool DefaultIsAreaLines => true; public static bool DefaultIsEditCursor => true; public static int DefaultZoomCounter => 3; public static bool DefaultIsDisplayOffResizing => false; public static bool DefaultIsOutOfArea => false; public static int DefaultMdiObjViewIndex => -1;
これで例えば MDI子ウィンドウサイズのデフォルトが欲しければ Configuration.DefaultMdiSize を読み出すだけで済みます。これが出来たので続いて  Properties.Settings.Default.Reset() の置き換えメソッドを実装します。
※ これを買って使っています。安い割にはめっちゃ良い音しますよ!

// ------------------------------------------------------------- // プロパティを初期化する // ------------------------------------------------------------- public void Reset() { CultureName = DefaultCultureName; MdiLocation = DefaultMdiLocation; MdiSize = DefaultMdiSize; MdiLayerLocation = DefaultMdiLayerLocation; MdiLayerSize = DefaultMdiLayerSize; FormLocation = DefaultFormLocation; FormSize = DefaultFormSize; IsGrid = DefaultIsGrid; IsAreaLines = DefaultIsAreaLines; IsEditCursor = DefaultIsEditCursor; ZoomCounter = DefaultZoomCounter; IsDisplayOffResizing = DefaultIsDisplayOffResizing; IsOutOfArea = DefaultIsOutOfArea; MdiObjViewIndex = DefaultMdiObjViewIndex; RecentlyUsedFiles = new(); }
後は大事なプロパティの保存と読み込みです。読み込みは Properties.Settings.Default は自動で行われていますが、今回は自前実装なので、メインフォームのコンストラクタで読み込みを呼び出すようにしています。XMLパースは当然 Json.NET なので、ポンッと書くだけ。めちゃくちゃ簡単です。

// ------------------------------------------------------------- // プロパティを読み込む // ------------------------------------------------------------- public void Load() { try { var path = GetFilePath(); using StreamReader sw = new(path); string str = sw.ReadToEnd(); var config = JsonConvert.DeserializeObject<Configuration>(str); if (config is null) { Reset(); } else { CultureName = config.CultureName; MdiLocation = config.MdiLocation; MdiSize = config.MdiSize; MdiLayerLocation = config.MdiLayerLocation; MdiLayerSize = config.MdiLayerSize; FormLocation = config.FormLocation; FormSize = config.FormSize; IsGrid = config.IsGrid; IsAreaLines = config.IsAreaLines; IsEditCursor = config.IsEditCursor; ZoomCounter = config.ZoomCounter; IsDisplayOffResizing = config.IsDisplayOffResizing; IsOutOfArea = config.IsOutOfArea; MdiObjViewIndex = config.MdiObjViewIndex; RecentlyUsedFiles = new(config.RecentlyUsedFiles); } } catch { Reset(); } } // ------------------------------------------------------------- // プロパティを保存する // ------------------------------------------------------------- public void Save() { var path = GetFilePath(); using StreamWriter sw = new(path); var str = JsonConvert.SerializeObject(this); sw.WriteLine(format_json(str)); static string format_json(string json) { dynamic parsedJson = JsonConvert.DeserializeObject(json); return JsonConvert.SerializeObject(parsedJson, Formatting.Indented); } }
これでほぼ修正無しで、自前実装クラスに置き換える事が出来ました。

※PS2ソフトの格納に使っています。大容量で安定性もあり安価。超オススメです!