1.パッケージ追加
・microsoft.extensions.configuration.json
appsettings.jsonなど)を読み込むための構成プロバイダーパッケージのインストール
Visual Studioツール>NuGetマネージャー>パッケージ マネージャー コンソール以下を実行
PM> Install-Package Microsoft.Extensions.Configuration.Json
・microsoft.extensions.hosting
依存関係の注入 (DI)、構成管理 (Configuration)などを統合的に管理するためのライブラリ(汎用ホスト)パッケージのインストール
Visual Studioのツール>NuGetマネージャー>ソリューションのNuGet パッケージの管理
Microsoft.Extensions.Hostingを検索>選択>プロジェクトにチェック>インストール
2.appsettiongs.json追加
(ファイル名は他と干渉しなければ任意)
・追加されたappsettiongs.jsonを右クリック>プロパティー>ビルドアクション>ドロップダウンで[コンテンツ]に変更
・出力ディレクトリにコピー>ドロップダウンで[新しい場合はコピーする]に変更
・JSONの例{
"AppName": "MyApp", "MainWin": {
"Title": "MainWindow", "Width": 800, "Height": 500, "Left": -877, "Top": 202 }
3.Jsonに対応するクラス追加
appsettingsをいくつかに分割する前提で、全体を管理するクラスを作る。名前は任意。
分割した個々の要素(Section)に対応するクラスを作り、そのインスタンスをプロパティーとして設定する。
public class AppSettings {
public string AppName { get; set; } = string.Empty;
public MainWinSettings MainWin { get; set; } = new(); }
appsettings.jsonのセクション名と一致する名前でプロパティーを作る。
上記の例ではappsettings.jsonの中に”MainWin"というセクションがある。
4.JSONとクラスの依存関係設定
JSONとクラスの依存関係設定
・プロジェクトダブルクリック>csprojに次のようなtemGroup追加
<ItemGroup>
<Compile Update="AppSettings.cs">
<DependentUpon>appsettings.json</DependentUpon>
</Compile>
</ItemGroup>
この設定を行うことで、ビルド時に実行ファイル(.exe)と同じフォルダにJSONファイルが出力され、DIコンテナ(Microsoft.Extensions.Configuration など)から正しく読み込めるようになる。
App.xaml.csのようにAppコンストラクタでServiceを構成することで、入出力先を変更できる。
5.App.xaml変更
StartupUriの設定を削除。
StartupUri="MainWindow.xaml">6.App.xaml.cs変更
コンストラクタでDIから受け取るServiceを登録
public static IHost? AppHost { get; private set; } /// <summary> /// ホスト(Host)を構築し、設定ファイルとウインドウをDIコンテナに登録します。 /// </summary>public App() { AppHost = Host.CreateDefaultBuilder() .ConfigureAppConfiguration((context, config) => { // 1. 環境(Debug/Release)に応じて読み込み元を決定 string jsonFileName = AppSettings.FileName; // "appsettings.json"; string targetPath = StorageService.GetSettingsFilePath(jsonFileName, true); config.AddJsonFile(targetPath, optional: true, reloadOnChange: true); }) .ConfigureServices((context, services) => { services.AddSingleton<StorageService>(); services.Configure<AppSettings>(context.Configuration); var rootSettings = context.Configuration.Get<AppSettings>() ?? new AppSettings(); services.PostConfigure<AppSettings>(settings => { settings.PostConfigure(); }); // MainWindow や他の設定クラスに渡す AppSettings は、Options パターン(IOptions)を経由して取得したものをシングルトン登録する services.AddSingleton<AppSettings>(provider => { // ここで .Value を要求した瞬間に、AppSettingsが読み込まれ、PostConfigureが実行される var options = provider.GetRequiredService<Microsoft.Extensions.Options.IOptions<AppSettings>>(); return options.Value; }); // 画面をDIコンテナに登録 services.AddTransient<MainWindow>(); }) .Build(); }protected override async void OnStartup(StartupEventArgs e) { // ホストの起動 await AppHost.StartAsync(); // DIコンテナからMainWindowを取得(自動的にAppSettingsが注入される) var mainWindow = AppHost.Services.GetRequiredService<MainWindow>(); mainWindow.Show(); base.OnStartup(e); } protected override async void OnExit(ExitEventArgs e) { // アプリ終了時にホストをクリーンアップ
if (AppHost != null)
{
await AppHost.StopAsync();
}
AppHost.Dispose(); base.OnExit(e); }
7.MainWindow.xaml.cs変更
コンストラクタでDIから登録したServiceを受け取る。
MainWindowの設定に関わるものがある場合は、InitializeComponentの前に呼ぶ。
public MainWindow(AppSettings settings) { this.DataContext = settings.MainWin;
InitializeComponent(); }
8.StorageServiceの実装例
const string brandName = "MyComp";
const string appName = "MyApp";
/// <summary>
/// AppData/Local/MyComp/MyApp 配下の指定されたファイル名のパスを返す。
/// </summary>
/// <param name="fileName"></param>
/// <param name="copyIfNotExistsInAppData"></param>
/// <returns></returns>
public static string GetSettingsFilePath(string fileName, bool copyIfNotExistsInAppData)
{
#if DEBUG
// Debug時はexeと同じフォルダのファイルを返す
return Path.Combine(AppDomain.CurrentDomain.BaseDirectory, fileName);
#else
//Release(インストール後)はAppData配下の専用フォルダを返す
string appDataFolder = Environment.GetFolderPath(Environment.SpecialFolder.LocalApplicationData); // Local配下に作る場合はこちら
//string appDataFolder = Environment.GetFolderPath(Environment.SpecialFolder.ApplicationData); // Roaming配下に作る場合はこちら
appDataFolder = Path.Combine((Path.Combine(appDataFolder, Path.Combine(brandName, appName))));
// フォルダがなければ自動作成
if (!Directory.Exists(appDataFolder))
{
Directory.CreateDirectory(appDataFolder);
}
string targetPath = Path.Combine(appDataFolder, fileName);
// 初回起動時など、AppData にファイルがなければ exe 横からコピーする
if (!File.Exists(targetPath) && copyIfNotExistsInAppData)
{
string sourcePath = Path.Combine(AppDomain.CurrentDomain.BaseDirectory, fileName);
if (File.Exists(sourcePath))
{
File.Copy(sourcePath, targetPath);
}
}
return targetPath;
#endif
}
単純に保存すると日本語がのエスケープ付きバイナリ値(UTF16?)になる。次のようにするとUTF8で保存できる。
public void SaveJson<T>(T data, string fileName){
var options = new JsonSerializerOptions
{
Encoder = System.Text.Encodings.Web.JavaScriptEncoder.UnsafeRelaxedJsonEscaping,
WriteIndented = true
};
string filePath = GetSettingsFilePath(fileName, false);
var json = JsonSerializer.Serialize(data, options);
File.WriteAllText(filePath, json, Encoding.UTF8);
}