アプリケーション設定 (Application Settings)

設定の追加

ソリューション エクスプローラーでプロジェクトのプロパティを開き、そこにある[設定]ペインで追加できます。

スコープ (Scope)

設定にはスコープという項目がありますが、これは次のように使い分けます。

スコープ 用途 プログラムによる書き換え 保存場所
アプリケーション (Application) アプリケーション単位 不可 実行ファイルのフォルダ\AppName.exe.config
ユーザー (User) ユーザー単位 可能 %LOCALAPPDATA%Namespace\folder\Version\user.config
アプリケーション設定を活用するには?[2.0のみ、C#、VB] - @IT 一色政彦 (2007/02/22)

ユーザー スコープの実際の保存場所は、

System.Configuration.Configuration config = System.Configuration.ConfigurationManager.OpenExeConfiguration(
    System.Configuration.ConfigurationUserLevel.PerUserRoamingAndLocal);

string filePath = config.FilePath;

とすることで確認できます。ConfigurationManager.OpenExeConfiguration メソッド (ConfigurationUserLevel) (System.Configuration) | MSDN

設定値の取得

次の書式のプロパティから取得できます。

global::Namespace.Properties.Settings.Default.Name

設定の既定値の取得

Settingsクラス内からのみ取得できます。

string settingValue =
    Settings.defaultInstance.Properties[ "SettingName" ].DefaultValue.ToString();

StringCollection

文字列の配列はStringCollectionクラスで、XML形式で格納されます。

string settingValue =
    Settings.defaultInstance.Properties[ "SettingName" ].DefaultValue.ToString();

System.Xml.XmlDocument document = new System.Xml.XmlDocument();
document.LoadXml( settingValue );

foreach( System.Xml.XmlElement element in document.DocumentElement )
{
    string data = element.InnerText;
}

取得した設定値の検証

バージョンアップの影響などで無効な値が取得される可能性を考慮するならば、アプリケーション設定の取得時に値の検証を行います。それにはプロジェクトのプロパティにある設定デザイナ (Settings designer) で、[コードの表示]をクリックします。C# で設定を使用する | MSDN

するとSettings.csというファイル名で、次のようなSettingクラスが表示されます。

// このクラスでは設定クラスでの特定のイベントを処理できます:
//  SettingChanging イベントは、設定値が変更される前に発生します。
//  PropertyChanged イベントは、設定値が変更された後に発生します。
//  SettingsLoaded イベントは、設定値が読み込まれた後に発生します。
//  SettingsSaving イベントは、設定値が保存される前に発生します。
internal sealed partial class Settings {

    public Settings() {
        // 設定の保存と変更のイベントハンドラを追加するには、以下の行のコメントを解除します:
        //
        // this.SettingChanging += this.SettingChangingEventHandler;
        //
        // this.SettingsSaving += this.SettingsSavingEventHandler;
        //
    }

    private void SettingChangingEventHandler(object sender, System.Configuration.SettingChangingEventArgs e) {
        // SettingChangingEvent イベントを処理するコードをここに追加してください。
    }

    private void SettingsSavingEventHandler(object sender, System.ComponentModel.CancelEventArgs e) {
        // SettingsSaving イベントを処理するコードをここに追加してください。
    }
}

アプリケーション設定の読み込み時に処理するには、ここでSettingsLoadedイベントのハンドラを定義します。

internal sealed partial class Settings
{
    public Settings()
    {
        this.SettingsLoaded += this.SettingsLoadedEventHandler;
    }

    private void SettingsLoadedEventHandler( object sender, System.Configuration.SettingsLoadedEventArgs e )
    {
        // 値を検証する
        if( this.settingValue < 1.0 )
        {
            // 既定値を設定する。直接キャストできないため、文字列を介して解析する
            this.settingValue = Double.Parse( Settings.defaultInstance.Properties[ "SettingName" ].DefaultValue.ToString() );
        }
    }
}

前バージョンの設定値の引き継ぎ

アプリケーション設定はアプリケーションのバージョンごとに異なるフォルダに保存されるため、通常ではバージョンアップ時に設定は引き継がれません。もし設定を引き継ぐようにしたいならば、ApplicationSettingsBaseのUpgrade()メソッドを呼び出します。前バージョンの設定を取得する - .NET Tips

Properties.Settings.Default.Upgrade();
ApplicationSettingsBase.Upgrade メソッド (System.Configuration) | MSDN

またバージョンアップしたとき、初回の起動時にのみ設定を引き継ぐようにするには、更新したことを示すフラグをアプリケーション設定に記録しておきます。アプリケーション設定の Upgrade について - MSDN フォーラム

設定値の設定

global::Namespace.Properties.Settings.Default.Name = 123;

保存

Properties.Settings.Default.Save()を呼ぶことで保存できます。

public override void Save()
ApplicationSettingsBase.Save メソッド (System.Configuration) | MSDN

保存場所や保存方法の変更

保存場所や保存方法を変更するには、SettingsProviderを継承したクラスを作成し、それをSettingsクラスにSettingsProviderAttributeで指定します。

独自に定義したクラスは、プロパティ ウィンドウを表示した状態で設定デザイナで設定項目を選択し、[Provider]でクラス名を指定することでも適用できます。しかしこの方法では設定項目ごとに独自定義のクラスが呼ばれるため、Settingsクラスで指定するようにします。

SettingsProviderクラスの拡張

class CustomSettingsProvider : SettingsProvider
{
    private struct Setting
    {
        public string name;
        public string value;
        public SettingsSerializeAs serializeAs;
    }

    private readonly string userConfigPath;
    private Dictionary<string, Setting> settingsDictionary = null;

    public override string ApplicationName
    {
        get
        {
            System.Reflection.Assembly assembly = typeof(Program).Assembly;
            System.Reflection.AssemblyName assemblyName = assembly.GetName();
            return assemblyName.Name;
        }
        set
        {
        }
    }


    public CustomSettingsProvider()
    {
        this.userConfigPath = @"C:\user.config";
    }

    public override void Initialize(string name, System.Collections.Specialized.NameValueCollection config)
    {
        base.Initialize(ApplicationName, config);
    }

    public override SettingsPropertyValueCollection GetPropertyValues(SettingsContext context, SettingsPropertyCollection collection)
    {
        if (this.settingsDictionary == null)
        {
            this.settingsDictionary = LoadSettingValues(this.userConfigPath);
        }

        SettingsPropertyValueCollection propertyValues = new SettingsPropertyValueCollection();
        foreach (SettingsProperty property in collection)
        {
            SettingsPropertyValue propertyValue = new SettingsPropertyValue(property);

            object value;
            if (this.settingsDictionary.ContainsKey(property.Name))
            {
                value = this.settingsDictionary[property.Name].value;
            }
            else
            {
                value = property.DefaultValue;
            }
            propertyValue.SerializedValue = value;

            if (value != null)
            {
                string text = value.ToString();
                if (property.SerializeAs == SettingsSerializeAs.String)
                {
                    System.ComponentModel.TypeConverter typeConverter = System.ComponentModel.TypeDescriptor.GetConverter(property.PropertyType);
                    propertyValue.PropertyValue = typeConverter.ConvertFromString(text);
                }
                else if (property.SerializeAs == SettingsSerializeAs.Xml)
                {
                    using (System.IO.StringReader stringReader = new System.IO.StringReader(text))
                    using (System.Xml.XmlReader xmlReader = System.Xml.XmlReader.Create(stringReader))
                    {
                        System.Xml.Serialization.XmlSerializer serializer = new System.Xml.Serialization.XmlSerializer(property.PropertyType);
                        propertyValue.PropertyValue = serializer.Deserialize(xmlReader);
                    }
                }
                else
                {
                    throw new NotImplementedException();
                }
            }

            propertyValue.IsDirty = false;
            propertyValues.Add(propertyValue);
        }

        return propertyValues;
    }

    public override void SetPropertyValues(SettingsContext context, SettingsPropertyValueCollection collection)
    {
        foreach (SettingsPropertyValue propertyValue in collection)
        {
            if (!propertyValue.IsDirty) continue;
            if (!propertyValue.Property.Attributes.Contains(typeof(UserScopedSettingAttribute))) continue;

            Setting setting = new Setting()
            {
                name = propertyValue.Name,
                value = propertyValue.SerializedValue.ToString(),
                serializeAs = propertyValue.Property.SerializeAs
            };

            this.settingsDictionary[propertyValue.Name] = setting;
        }

        SaveSettingValues(this.userConfigPath, this.settingsDictionary);
    }


    private static Dictionary<string, Setting> LoadSettingValues(string path)
    {
        Dictionary<string, Setting> dictionary = new Dictionary<string, Setting>();
        if (!System.IO.File.Exists(path)) return dictionary;

        using (System.Xml.XmlReader reader = System.Xml.XmlReader.Create(path))
        {
            while (reader.ReadToFollowing("setting"))
            {
                string name = reader.GetAttribute("name");
                string serializeAs = reader.GetAttribute("serializeAs");
                string value = reader.ReadElementContentAsString();

                if (String.IsNullOrEmpty(name)) continue;

                Setting setting = new Setting()
                {
                    name = name,
                    value = value,
                    serializeAs = SettingsSerializeAs.String
                };

                SettingsSerializeAs settingsSerializeAs;
                if (Enum.TryParse(serializeAs, out settingsSerializeAs))
                {
                    setting.serializeAs = settingsSerializeAs;
                }

                if (!dictionary.ContainsKey(setting.name))
                {
                    dictionary.Add(setting.name, setting);
                }
            }
        }
        return dictionary;
    }

    private static void SaveSettingValues(string path, Dictionary<string, Setting> dictionary)
    {
        System.Xml.XmlWriterSettings settings = new System.Xml.XmlWriterSettings();
        settings.Indent = true;

        using (System.Xml.XmlWriter writer = System.Xml.XmlWriter.Create(path, settings))
        {
            writer.WriteStartElement("configuration");
            writer.WriteStartElement("userSettings");
            writer.WriteStartElement(typeof(Properties.Settings).FullName);

            foreach (Setting setting in dictionary.Values)
            {
                writer.WriteStartElement("setting");
                writer.WriteAttributeString("name", setting.name);
                writer.WriteAttributeString("serializeAs", setting.serializeAs.ToString());
                writer.WriteString(setting.value ?? String.Empty);
                writer.WriteEndElement();
            }

            writer.WriteEndElement();
            writer.WriteEndElement();
            writer.WriteEndElement();
        }
    }
}
SettingsProvider Class (System.Configuration) | Microsoft Docs

Settingsクラスへの適用

[global::System.Runtime.CompilerServices.CompilerGeneratedAttribute()]
[global::System.CodeDom.Compiler.GeneratedCodeAttribute("Microsoft.VisualStudio.Editors.SettingsDesigner.SettingsSingleFileGenerator", "*.0.0.0")]
[global::System.Configuration.SettingsProviderAttribute(typeof(CustomSettingsProvider))]
internal sealed partial class Settings : global::System.Configuration.ApplicationSettingsBase {

SettingsProviderAttribute Class (System.Configuration) | Microsoft Docs

関連するファイル

ファイル 作成時機
プロジェクトのフォルダ\App.config プロジェクトの作成時
実行ファイルのフォルダ\AppName.exe.config プロジェクトのビルド時
%LOCALAPPDATA%Namespace\folder\Version\user.config Properties.Settings.Default.Save() 呼び出し時

設定デザイナでの編集はApp.configに反映され、その内容はビルド時にAppName.exe.configにコピーされます。

参照される情報の優先順位

アプリケーション スコープの設定

  1. AppName.exe.config の既定値
  2. アセンブリ内の既定値

ユーザー スコープの設定

  1. user.config の設定値
  2. AppName.exe.config の既定値
  3. アセンブリ内の既定値

構成ファイル (Configuration Files)

<?xml version="1.0" encoding="utf-8" ?>
<configuration>
    <configSections>
        <sectionGroup name="userSettings" type="System.Configuration.UserSettingsGroup, System, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089" >
            <section name="WindowsFormsApplication1.Properties.Settings" type="System.Configuration.ClientSettingsSection, System, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089" allowExeDefinition="MachineToLocalUser" requirePermission="false" />
        </sectionGroup>
        <sectionGroup name="applicationSettings" type="System.Configuration.ApplicationSettingsGroup, System, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089" >
            <section name="WindowsFormsApplication1.Properties.Settings" type="System.Configuration.ClientSettingsSection, System, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089" requirePermission="false" />
        </sectionGroup>
    </configSections>

    <startup>
        <supportedRuntime version="v4.0" sku=".NETFramework,Version=v4.6" />
    </startup>

    <userSettings>
        <WindowsFormsApplication1.Properties.Settings>
            <setting name="location" serializeAs="String">
                <value>10, 20</value>
            </setting>
            <setting name="text" serializeAs="String">
                <value>Form1</value>
            </setting>
        </WindowsFormsApplication1.Properties.Settings>
    </userSettings>

    <applicationSettings>
        <WindowsFormsApplication1.Properties.Settings>
            <setting name="sample" serializeAs="String">
                <value>123</value>
            </setting>
        </WindowsFormsApplication1.Properties.Settings>
    </applicationSettings>
</configuration>

スキーマ

  内容
configuration すべての構成ファイルのルート要素
configSections 構成セクションの名前空間の定義 (sectionGroup) と構成セクションの宣言 (section)
構成セクション スキーマ | Microsoft Docs
  内容
startup アプリケーションを実行するのに必要な、共通言語ランタイムのバージョンを示す
supportedRuntime アプリケーションがサポートする共通言語ランタイムのバージョン。オプションで.NET Frameworkのバージョン
  • version属性 … 共通言語ランタイムのバージョン。.NET Framework 4.0~4.8ならば、"v4.0"とする
  • sku属性 … 最小在庫管理単位 (stock-keeping unit / SKU)。.NET Framework 4.8ならば、".NETFramework,Version=v4.8"とする
スタートアップ設定スキーマ | Microsoft Docs
  内容
userSettings 現在のユーザー固有のsetting要素
applicationSettings アプリケーション康祐のsetting要素
アプリケーション設定のスキーマ | Microsoft Docs
MSDN (Microsoft Developer Network) から検索