注意:ここで紹介している方法は、.NET Framework 2.0以降でのみ有効です。
ここでは、SettingsProviderクラスから派生した設定プロバイダを自作する方法を紹介します。設定プロバイダを自作することにより、ApplicationSettingsBaseクラスを使用した設定の管理をどのようにするかを決めることができます。例えば、.NET Frameworkで用意されている設定プロバイダLocalFileSettingsProviderでは設定をファイルに保存しますが、設定プロバイダを自作することにより、レジストリに保存することもできるようになります。ここでも具体的に、アプリケーション設定をレジストリに保存する設定プロバイダを作成する方法を紹介します。
ヘルプでは、「アプリケーション設定アーキテクチャ」や「SettingsProvider クラス」でカスタム設定プロバイダに関する説明があります。さらに「RegistrySettingsProvider のサンプル」では、まさにレジストリに設定を保存する設定プロバイダのサンプルがあります。これらも参考にしてください。
設定プロバイダは、SettingsProviderクラスを継承して作成します。SettingsProviderクラスは抽象クラスで、ApplicationNameプロパティと、GetPropertyValues、SetPropertyValuesメソッドをオーバーライドする必要があります。
ApplicationNameプロパティでは、現在実行中のアプリケーションの名前を返すようにします。ヘルプでは、Getで「System.Reflection.Assembly.GetExecutingAssembly().GetName().Name」を返す例が書かれています(Setは空です)。また、「RegistrySettingsProvider のサンプル」では、Getで「Application.ProductName」を返しています。しかし、次のようにすることにより、アプリケーションの名前がApplicationNameに入るようです。
Private _applicationName As String = String.Empty Public Overrides Property ApplicationName() As String Get Return Me._applicationName End Get Set(ByVal value As String) Me._applicationName = value End Set End Property
private string _applicationName = string.Empty; public override string ApplicationName { get { return this._applicationName; } set { this._applicationName = value; } }
GetPropertyValuesメソッドは、データの保存先から設定を取得する必要があるときに呼び出されます。つまり、このメソッドで保存先のレジストリから設定を読み込みます。
SetPropertyValuesメソッドは、ApplicationSettingsBase.Saveメソッドにより設定が保存されるときに呼び出されます。つまり、このメソッドで設定をレジストリに保存します。
必ずオーバーライドしなければならない抽象メンバは以上です。しかし、Initializeメソッドもオーバーライドする必要があります。Initializeメソッドでは、基本クラスのInitializeメソッドを呼び出すだけでよいでしょう。このとき、設定プロバイダの名前を第一パラメータに指定します。
簡単な説明でしたが、具体的なコードを見ていただいたほうが早いでしょう。以下にレジストリに設定を保存する設定プロバイダの例を示します。参照にSystem.Configuration.dllを追加する必要があります。
Imports System.Windows.Forms Imports System.Configuration Imports Microsoft.Win32 Namespace Dobon.Sample.Configuration Public Class RegistrySettingsProvider Inherits SettingsProvider Private _applicationName As String ''' <summary> ''' 現在実行中のアプリケーションの名前 ''' </summary> Public Overrides Property ApplicationName() As String Get Return Me._applicationName End Get Set(ByVal value As String) Me._applicationName = value End Set End Property ''' <summary> ''' 設定プロバイダ名 ''' </summary> Private _name As String ''' <summary> ''' コンストラクタ ''' </summary> Public Sub New() Me._applicationName = String.Empty Me._name = "RegistrySettingsProvider" End Sub Public Overrides Function GetPropertyValues( _ ByVal context As SettingsContext, _ ByVal collection As SettingsPropertyCollection) _ As SettingsPropertyValueCollection Dim col As New SettingsPropertyValueCollection() Dim prop As SettingsProperty For Each prop In collection 'SettingsPropertyに基づいて、SettingsPropertyValueを作成 Dim val As New SettingsPropertyValue(prop) 'レジストリに保存されている設定の値を取得 'SerializedValueにシリアル化された値を格納 val.SerializedValue = Me.ReadSettingFromRegistry(context, prop) val.IsDirty = False 'コレクションに追加 col.Add(val) Next prop Return col End Function Public Overrides Sub SetPropertyValues( _ ByVal context As SettingsContext, _ ByVal collection As SettingsPropertyValueCollection) Dim spv As SettingsPropertyValue For Each spv In collection '値が変更されているか調べる If spv.IsDirty Then 'レジストリに書き込む Me.WriteSettingToRegistry(context, spv) spv.IsDirty = False End If Next spv End Sub Public Overrides Sub Initialize(ByVal pname As String, _ ByVal config As System.Collections.Specialized.NameValueCollection) '設定プロバイダ名を指定する MyBase.Initialize(Me._name, config) End Sub ''' <summary> ''' 指定された設定がユーザースコープか調べる ''' </summary> ''' <param name="setting">調べる設定プロパティ</param> ''' <returns>ユーザースコープならばTrue</returns> Private Function IsUserSetting(ByVal setting As SettingsProperty) _ As Boolean Dim isUser As Boolean = setting.Attributes.Contains( _ GetType(UserScopedSettingAttribute)) Dim isApp As Boolean = setting.Attributes.Contains( _ GetType(ApplicationScopedSettingAttribute)) If isUser And isApp Then Throw New ConfigurationErrorsException( _ "設定""" + setting.Name + _ """にApplicationScopedSettingAttributeと" + _ "UserScopedSettingAttributeの両方が指定されています。") End If If Not isUser And Not isApp Then Throw New ConfigurationErrorsException( _ "設定""" + setting.Name + _ """にApplicationScopedSettingAttributeまたは" + _ "UserScopedSettingAttributeを指定する必要があります。") End If Return isUser End Function ''' <summary> ''' レジストリから指定された設定を読み込む ''' </summary> ''' <param name="context">SettingsContext</param> ''' <param name="prop">読み込む設定プロパティ</param> ''' <returns>取得された値</returns> Private Function ReadSettingFromRegistry( _ ByVal context As SettingsContext, _ ByVal prop As SettingsProperty) As Object Dim regKey As RegistryKey = Me.GetSettingRegistryKey(context, prop) Try If regKey Is Nothing Then Return Nothing End If 'レジストリから値を読み込む '指定されたキーが無ければ、デフォルト値を返す Return regKey.GetValue(prop.Name, prop.DefaultValue) Finally regKey.Close() End Try End Function ''' <summary> ''' レジストリに設定の値をを保存する ''' </summary> ''' <param name="context">SettingsContext</param> ''' <param name="spv">書き込む設定プロパティ</param> Private Sub WriteSettingToRegistry( _ ByVal context As SettingsContext, _ ByVal spv As SettingsPropertyValue) Dim regKey As RegistryKey = _ Me.GetSettingRegistryKey(context, spv.Property) Try If regKey Is Nothing Then Return End If 'SerializedValueの値を書き込む regKey.SetValue(spv.Name, spv.SerializedValue) Finally regKey.Close() End Try End Sub ''' <summary> ''' 指定された設定プロパティに対応したレジストリキーを取得する ''' </summary> ''' <param name="context">SettingsContext</param> ''' <param name="prop">設定プロパティ</param> ''' <returns>レジストリキー</returns> Private Function GetSettingRegistryKey( _ ByVal context As SettingsContext, _ ByVal prop As SettingsProperty) As RegistryKey Dim pRegKey As RegistryKey If Me.IsUserSetting(prop) Then pRegKey = Application.UserAppDataRegistry Else pRegKey = Application.CommonAppDataRegistry End If 'セクション名のサブキーを作成 Dim secName As String = Me.GetSectionName(context) Dim regKey As RegistryKey = pRegKey.CreateSubKey(secName) pRegKey.Close() Return regKey End Function ''' <summary> ''' セッション名を取得する ''' </summary> ''' <param name="context">SettingsContext</param> ''' <returns>セッション名。"GroupName.SettingsKey"となる。</returns> Private Function GetSectionName( _ ByVal context As SettingsContext) As String Dim ret As String = CStr(context("GroupName")) Dim setKey As String = CStr(context("SettingsKey")) If Not String.IsNullOrEmpty(setKey) Then ret += "." + setKey End If Console.WriteLine(ret) Return ret End Function End Class End Namespace
using System.Windows.Forms; using System.Configuration; using Microsoft.Win32; namespace Dobon.Sample.Configuration { public class RegistrySettingsProvider : SettingsProvider { private string _applicationName; /// <summary> /// 現在実行中のアプリケーションの名前 /// </summary> public override string ApplicationName { get { return this._applicationName; } set { this._applicationName = value; } } /// <summary> /// 設定プロバイダ名 /// </summary> private string _name; /// <summary> /// コンストラクタ /// </summary> public RegistrySettingsProvider() { this._applicationName = string.Empty; this._name = "RegistrySettingsProvider"; } public override SettingsPropertyValueCollection GetPropertyValues( SettingsContext context, SettingsPropertyCollection collection) { SettingsPropertyValueCollection col = new SettingsPropertyValueCollection(); foreach (SettingsProperty prop in collection) { //SettingsPropertyに基づいて、SettingsPropertyValueを作成 SettingsPropertyValue val = new SettingsPropertyValue(prop); //レジストリに保存されている設定の値を取得 //SerializedValueにシリアル化された値を格納 val.SerializedValue = this.ReadSettingFromRegistry(context, prop); val.IsDirty = false; //コレクションに追加 col.Add(val); } return col; } public override void SetPropertyValues(SettingsContext context, SettingsPropertyValueCollection collection) { foreach (SettingsPropertyValue spv in collection) { //値が変更されているか調べる if (spv.IsDirty) { //レジストリに書き込む this.WriteSettingToRegistry(context, spv); spv.IsDirty = false; } } } public override void Initialize(string pname, System.Collections.Specialized.NameValueCollection config) { //設定プロバイダ名を指定する base.Initialize(this._name, config); } /// <summary> /// 指定された設定がユーザースコープか調べる /// </summary> /// <param name="setting">調べる設定プロパティ</param> /// <returns>ユーザースコープならばTrue</returns> private bool IsUserSetting(SettingsProperty setting) { bool isUser = setting.Attributes.Contains( typeof(UserScopedSettingAttribute)); bool isApp = setting.Attributes.Contains( typeof(ApplicationScopedSettingAttribute)); if (isUser && isApp) { throw new ConfigurationErrorsException("設定\"" + setting.Name + "\"にApplicationScopedSettingAttributeと" + "UserScopedSettingAttributeの両方が指定されています。"); } if (!isUser && !isApp) { throw new ConfigurationErrorsException("設定\"" + setting.Name + "\"にApplicationScopedSettingAttributeまたは" + "UserScopedSettingAttributeを指定する必要があります。"); } return isUser; } /// <summary> /// レジストリから指定された設定を読み込む /// </summary> /// <param name="context">SettingsContext</param> /// <param name="prop">読み込む設定プロパティ</param> /// <returns>取得された値</returns> private object ReadSettingFromRegistry(SettingsContext context, SettingsProperty prop) { using (RegistryKey regKey = this.GetSettingRegistryKey(context, prop)) { if (regKey == null) return null; //レジストリから値を読み込む //指定されたキーが無ければ、デフォルト値を返す return regKey.GetValue(prop.Name, prop.DefaultValue); } } /// <summary> /// レジストリに設定の値をを保存する /// </summary> /// <param name="context">SettingsContext</param> /// <param name="spv">書き込む設定プロパティ</param> private void WriteSettingToRegistry(SettingsContext context, SettingsPropertyValue spv) { using (RegistryKey regKey = this.GetSettingRegistryKey(context, spv.Property)) { if (regKey == null) return; //SerializedValueの値を書き込む regKey.SetValue(spv.Name, spv.SerializedValue); } } /// <summary> /// 指定された設定プロパティに対応したレジストリキーを取得する /// </summary> /// <param name="context">SettingsContext</param> /// <param name="prop">設定プロパティ</param> /// <returns>レジストリキー</returns> private RegistryKey GetSettingRegistryKey(SettingsContext context, SettingsProperty prop) { RegistryKey regKey; if (this.IsUserSetting(prop)) regKey = Application.UserAppDataRegistry; else regKey = Application.CommonAppDataRegistry; //セクション名のサブキーを作成 string secName = this.GetSectionName(context); if (!string.IsNullOrEmpty(secName)) regKey = regKey.CreateSubKey(secName); return regKey; } /// <summary> /// セッション名を取得する /// </summary> /// <param name="context">SettingsContext</param> /// <returns>セッション名。"GroupName.SettingsKey"となる。</returns> private string GetSectionName(SettingsContext context) { string ret = (string)context["GroupName"]; string setKey = (string)context["SettingsKey"]; if (!string.IsNullOrEmpty(setKey)) ret += "." + setKey; return ret; } } }
この設定プロバイダでは、ユーザースコープの設定をApplication.UserAppDataRegistryの返すレジストリキー(HKEY_CURRENT_USER\Software\CompanyName\ProductName\ProductVersion)以下に保存します。アプリケーションスコープの設定も、値の変更が可能で、Application.CommonAppDataRegistryの返すレジストリキー(HKEY_LOCAL_MACHINE\Software\CompanyName\ProductName\ProductVersion)以下に保存します。
「RegistrySettingsProvider のサンプル」との違いは、設定プロパティにApplicationScopedSettingAttributeとUserScopedSettingAttributeの両方が指定されているか、どちらも指定されていないときに例外をスローする点や、保存場所をセッション名で分けている点などです。
自作した設定プロバイダを設定クラスで使用するには、設定クラスにSettingsProviderAttribute属性を適用します。つまり、次の例のようにします。
<SettingsProvider(GetType(Dobon.Sample.Configuration.RegistrySettingsProvider))> _ Public Class TestSettings Inherits ApplicationSettingsBase End Class
using System.Configuration; [SettingsProvider(typeof(Dobon.Sample.Configuration.RegistrySettingsProvider))] public class TestSettings : ApplicationSettingsBase { }
さらにIApplicationSettingsProviderを実装することにより、ApplicationSettingsBaseのUpgrade、GetPreviousVersion、Resetメソッドによる、設定のアップグレードなどの機能が有効になります。詳しい方法は、後日紹介する予定です。