我使用VS2008中的设置设计器管理我的应用程序设置.
"user.config文件的确切路径如下所示:"
<Profile Directory>\<Company Name>\
<App Name>_<Evidence Type>_<Evidence Hash>\<Version>\user.config
Run Code Online (Sandbox Code Playgroud)
有没有办法定制这条路径?我更喜欢这样的东西:
<Profile Directory>\<Company Name>\
<App Name>\<Version>\user.config
Run Code Online (Sandbox Code Playgroud)
我注意到新创建的文件夹("Test Company" - >"Test_Company")中的"公司名称"中的下划线替换了空格.我真的希望关闭这种行为.
你知道,我可以写一个新的基于XML的设置处理程序,但我想使用设置设计器.
Chu*_*nce 38
要找到实现自定义设置提供程序的好信息并不容易,所以我在下面包含了一个完整的实现(底部).保留了user.config文件的格式,以及.settings设计器中的功能. .我确定有些东西可以清理一下,所以不要麻烦我:)
与其他人一样,我想更改user.config文件的位置,并且仍然可以在设计器中使用.settings文件获得乐趣和乐趣,包括为新安装创建默认值.重要的是,我们的应用程序还已经在我们已经决定的路径(appData\local\etc)中有其他已保存的设置对象,并且我们不希望在多个位置存在工件.
代码比我想要的要长得多,但我找不到简短的答案.虽然能够控制路径似乎有些痛苦,但创建自定义设置提供程序本身仍然非常强大.人们可以改变以下实现来存储数据,包括自定义加密文件,数据库或与Web服务交互.
根据我的阅读,Microsoft并不打算使配置文件的路径可配置.当他们说允许这会让人感到恐惧时,我会接受他们的话.见(本)帖子.唉,如果你想自己动手,你必须实现自己的SettingsProvider.
开始..
将项目中的引用添加到System.Configuration,您将需要它来实现SettingsProvider.
Easy bit ...创建一个实现SettingsProvider的类,使用ctrl +.帮助你.
class CustomSettingsProvider : SettingsProvider
Run Code Online (Sandbox Code Playgroud)
另一个简单的方法 ...转到.settings文件后面的代码(设计器中有一个按钮)并装饰类以指向它的实现.必须这样做才能覆盖内置功能,但它并没有改变设计师的工作方式.(抱歉这里的格式很奇怪)
[System.Configuration.SettingsProvider(typeof(YourCompany.YourProduct.CustomSettingsProvider))]
public sealed partial class Settings
{
//bla bla bla
}
Run Code Online (Sandbox Code Playgroud)
获取路径: 有一个名为"SettingsKey"的属性(例如Properties.Settings.Default.SettingsKey)我用它来存储路径.我做了以下财产.
/// <summary>
/// The key this is returning must set before the settings are used.
/// e.g. <c>Properties.Settings.Default.SettingsKey = @"C:\temp\user.config";</c>
/// </summary>
private string UserConfigPath
{
get
{
return Properties.Settings.Default.SettingsKey;
}
}
Run Code Online (Sandbox Code Playgroud)
存储设置值.我选择使用字典.这将被广泛使用.我创建了一个struct作为帮助器.
/// <summary>
/// In memory storage of the settings values
/// </summary>
private Dictionary<string, SettingStruct> SettingsDictionary { get; set; }
/// <summary>
/// Helper struct.
/// </summary>
internal struct SettingStruct
{
internal string name;
internal string serializeAs;
internal string value;
}
Run Code Online (Sandbox Code Playgroud)
魔法. 您必须覆盖2个方法,GetPropertyValues和SetPropertyValues.GetPropertyValues接收为您在设计器中看到的参数,您必须有机会更新值并返回新集合.当用户保存对在运行时生成的值的任何更改时,将调用SetPropertyValues,这是我更新字典并写出文件的位置.
/// <summary>
/// Must override this, this is the bit that matches up the designer properties to the dictionary values
/// </summary>
/// <param name="context"></param>
/// <param name="collection"></param>
/// <returns></returns>
public override SettingsPropertyValueCollection GetPropertyValues(SettingsContext context, SettingsPropertyCollection collection)
{
//load the file
if (!_loaded)
{
_loaded = true;
LoadValuesFromFile();
}
//collection that will be returned.
SettingsPropertyValueCollection values = new SettingsPropertyValueCollection();
//iterate thought the properties we get from the designer, checking to see if the setting is in the dictionary
foreach (SettingsProperty setting in collection)
{
SettingsPropertyValue value = new SettingsPropertyValue(setting);
value.IsDirty = false;
//need the type of the value for the strong typing
var t = Type.GetType(setting.PropertyType.FullName);
if (SettingsDictionary.ContainsKey(setting.Name))
{
value.SerializedValue = SettingsDictionary[setting.Name].value;
value.PropertyValue = Convert.ChangeType(SettingsDictionary[setting.Name].value, t);
}
else //use defaults in the case where there are no settings yet
{
value.SerializedValue = setting.DefaultValue;
value.PropertyValue = Convert.ChangeType(setting.DefaultValue, t);
}
values.Add(value);
}
return values;
}
/// <summary>
/// Must override this, this is the bit that does the saving to file. Called when Settings.Save() is called
/// </summary>
/// <param name="context"></param>
/// <param name="collection"></param>
public override void SetPropertyValues(SettingsContext context, SettingsPropertyValueCollection collection)
{
//grab the values from the collection parameter and update the values in our dictionary.
foreach (SettingsPropertyValue value in collection)
{
var setting = new SettingStruct()
{
value = (value.PropertyValue == null ? String.Empty : value.PropertyValue.ToString()),
name = value.Name,
serializeAs = value.Property.SerializeAs.ToString()
};
if (!SettingsDictionary.ContainsKey(value.Name))
{
SettingsDictionary.Add(value.Name, setting);
}
else
{
SettingsDictionary[value.Name] = setting;
}
}
//now that our local dictionary is up-to-date, save it to disk.
SaveValuesToFile();
}
Run Code Online (Sandbox Code Playgroud)
完整解决方案 所以这里是整个类,包括构造函数,Initialize和帮助器方法.随意剪切/粘贴切片和骰子.
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Configuration;
using System.Reflection;
using System.Xml.Linq;
using System.IO;
namespace YourCompany.YourProduct
{
class CustomSettingsProvider : SettingsProvider
{
const string NAME = "name";
const string SERIALIZE_AS = "serializeAs";
const string CONFIG = "configuration";
const string USER_SETTINGS = "userSettings";
const string SETTING = "setting";
/// <summary>
/// Loads the file into memory.
/// </summary>
public CustomSettingsProvider()
{
SettingsDictionary = new Dictionary<string, SettingStruct>();
}
/// <summary>
/// Override.
/// </summary>
public override void Initialize(string name, System.Collections.Specialized.NameValueCollection config)
{
base.Initialize(ApplicationName, config);
}
/// <summary>
/// Override.
/// </summary>
public override string ApplicationName
{
get
{
return System.Reflection.Assembly.GetExecutingAssembly().ManifestModule.Name;
}
set
{
//do nothing
}
}
/// <summary>
/// Must override this, this is the bit that matches up the designer properties to the dictionary values
/// </summary>
/// <param name="context"></param>
/// <param name="collection"></param>
/// <returns></returns>
public override SettingsPropertyValueCollection GetPropertyValues(SettingsContext context, SettingsPropertyCollection collection)
{
//load the file
if (!_loaded)
{
_loaded = true;
LoadValuesFromFile();
}
//collection that will be returned.
SettingsPropertyValueCollection values = new SettingsPropertyValueCollection();
//itterate thought the properties we get from the designer, checking to see if the setting is in the dictionary
foreach (SettingsProperty setting in collection)
{
SettingsPropertyValue value = new SettingsPropertyValue(setting);
value.IsDirty = false;
//need the type of the value for the strong typing
var t = Type.GetType(setting.PropertyType.FullName);
if (SettingsDictionary.ContainsKey(setting.Name))
{
value.SerializedValue = SettingsDictionary[setting.Name].value;
value.PropertyValue = Convert.ChangeType(SettingsDictionary[setting.Name].value, t);
}
else //use defaults in the case where there are no settings yet
{
value.SerializedValue = setting.DefaultValue;
value.PropertyValue = Convert.ChangeType(setting.DefaultValue, t);
}
values.Add(value);
}
return values;
}
/// <summary>
/// Must override this, this is the bit that does the saving to file. Called when Settings.Save() is called
/// </summary>
/// <param name="context"></param>
/// <param name="collection"></param>
public override void SetPropertyValues(SettingsContext context, SettingsPropertyValueCollection collection)
{
//grab the values from the collection parameter and update the values in our dictionary.
foreach (SettingsPropertyValue value in collection)
{
var setting = new SettingStruct()
{
value = (value.PropertyValue == null ? String.Empty : value.PropertyValue.ToString()),
name = value.Name,
serializeAs = value.Property.SerializeAs.ToString()
};
if (!SettingsDictionary.ContainsKey(value.Name))
{
SettingsDictionary.Add(value.Name, setting);
}
else
{
SettingsDictionary[value.Name] = setting;
}
}
//now that our local dictionary is up-to-date, save it to disk.
SaveValuesToFile();
}
/// <summary>
/// Loads the values of the file into memory.
/// </summary>
private void LoadValuesFromFile()
{
if (!File.Exists(UserConfigPath))
{
//if the config file is not where it's supposed to be create a new one.
CreateEmptyConfig();
}
//load the xml
var configXml = XDocument.Load(UserConfigPath);
//get all of the <setting name="..." serializeAs="..."> elements.
var settingElements = configXml.Element(CONFIG).Element(USER_SETTINGS).Element(typeof(Properties.Settings).FullName).Elements(SETTING);
//iterate through, adding them to the dictionary, (checking for nulls, xml no likey nulls)
//using "String" as default serializeAs...just in case, no real good reason.
foreach (var element in settingElements)
{
var newSetting = new SettingStruct()
{
name = element.Attribute(NAME) == null ? String.Empty : element.Attribute(NAME).Value,
serializeAs = element.Attribute(SERIALIZE_AS) == null ? "String" : element.Attribute(SERIALIZE_AS).Value,
value = element.Value ?? String.Empty
};
SettingsDictionary.Add(element.Attribute(NAME).Value, newSetting);
}
}
/// <summary>
/// Creates an empty user.config file...looks like the one MS creates.
/// This could be overkill a simple key/value pairing would probably do.
/// </summary>
private void CreateEmptyConfig()
{
var doc = new XDocument();
var declaration = new XDeclaration("1.0", "utf-8", "true");
var config = new XElement(CONFIG);
var userSettings = new XElement(USER_SETTINGS);
var group = new XElement(typeof(Properties.Settings).FullName);
userSettings.Add(group);
config.Add(userSettings);
doc.Add(config);
doc.Declaration = declaration;
doc.Save(UserConfigPath);
}
/// <summary>
/// Saves the in memory dictionary to the user config file
/// </summary>
private void SaveValuesToFile()
{
//load the current xml from the file.
var import = XDocument.Load(UserConfigPath);
//get the settings group (e.g. <Company.Project.Desktop.Settings>)
var settingsSection = import.Element(CONFIG).Element(USER_SETTINGS).Element(typeof(Properties.Settings).FullName);
//iterate though the dictionary, either updating the value or adding the new setting.
foreach (var entry in SettingsDictionary)
{
var setting = settingsSection.Elements().FirstOrDefault(e => e.Attribute(NAME).Value == entry.Key);
if (setting == null) //this can happen if a new setting is added via the .settings designer.
{
var newSetting = new XElement(SETTING);
newSetting.Add(new XAttribute(NAME, entry.Value.name));
newSetting.Add(new XAttribute(SERIALIZE_AS, entry.Value.serializeAs));
newSetting.Value = (entry.Value.value ?? String.Empty);
settingsSection.Add(newSetting);
}
else //update the value if it exists.
{
setting.Value = (entry.Value.value ?? String.Empty);
}
}
import.Save(UserConfigPath);
}
/// <summary>
/// The setting key this is returning must set before the settings are used.
/// e.g. <c>Properties.Settings.Default.SettingsKey = @"C:\temp\user.config";</c>
/// </summary>
private string UserConfigPath
{
get
{
return Properties.Settings.Default.SettingsKey;
}
}
/// <summary>
/// In memory storage of the settings values
/// </summary>
private Dictionary<string, SettingStruct> SettingsDictionary { get; set; }
/// <summary>
/// Helper struct.
/// </summary>
internal struct SettingStruct
{
internal string name;
internal string serializeAs;
internal string value;
}
bool _loaded;
}
}
Run Code Online (Sandbox Code Playgroud)
您必须实现自己的SettingsProvider来自定义路径.
请参阅此客户端设置FAQ
问:为什么路径如此模糊?有没有办法改变/定制它?
答:路径构造算法必须满足安全性,隔离性和鲁棒性方面的某些严格要求.虽然我们尝试通过使用友好的,应用程序提供的字符串尽可能轻松地发现路径,但是不可能保持路径完全简单而不会遇到与其他应用程序冲突,欺骗等问题.
LocalFileSettingsProvider不提供更改存储设置的文件的方法.请注意,提供程序本身并不首先确定配置文件位置 - 它是配置系统.如果由于某种原因需要将设置存储在其他位置,建议的方法是编写自己的SettingsProvider.这实现起来相当简单,您可以在.NET 2.0 SDK中找到演示如何执行此操作的示例.但请记住,您可能遇到上述相同的隔离问题.
| 归档时间: |
|
| 查看次数: |
18563 次 |
| 最近记录: |