我想要实现的非常简单:我有一个Windows窗体(。NET 3.5)应用程序使用路径来读取信息。用户可以使用我提供的选项表单修改此路径。

现在,我想将路径值保存到一个文件中以供以后使用。这将是保存到该文件中的众多设置之一。该文件将直接位于应用程序文件夹中。

我知道有三种选择:

配置文件(appname.exe.config) 注册表 自定义XML文件

我读到。net配置文件不能将值保存回配置文件。至于注册表,我想尽量远离它。

这是否意味着我应该使用自定义XML文件来保存配置设置?

如果是这样,我想看看代码的例子(c#)。

我看过其他关于这个问题的讨论,但我仍然不清楚。


当前回答

public static class SettingsExtensions
{
    public static bool TryGetValue<T>(this Settings settings, string key, out T value)
    {
        if (settings.Properties[key] != null)
        {
            value = (T) settings[key];
            return true;
        }

        value = default(T);
        return false;
    }

    public static bool ContainsKey(this Settings settings, string key)
    {
        return settings.Properties[key] != null;
    }

    public static void SetValue<T>(this Settings settings, string key, T value)
    {
        if (settings.Properties[key] == null)
        {
            var p = new SettingsProperty(key)
            {
                PropertyType = typeof(T),
                Provider = settings.Providers["LocalFileSettingsProvider"],
                SerializeAs = SettingsSerializeAs.Xml
            };
            p.Attributes.Add(typeof(UserScopedSettingAttribute), new UserScopedSettingAttribute());
            var v = new SettingsPropertyValue(p);
            settings.Properties.Add(p);
            settings.Reload();
        }
        settings[key] = value;
        settings.Save();
    }
}

其他回答

据我所知,.NET确实支持使用内置的应用程序设置功能持久化设置:

The Application Settings feature of Windows Forms makes it easy to create, store, and maintain custom application and user preferences on the client computer. With Windows Forms application settings, you can store not only application data such as database connection strings, but also user-specific data, such as user application preferences. Using Visual Studio or custom managed code, you can create new settings, read them from and write them to disk, bind them to properties on your forms, and validate settings data prior to loading and saving. - http://msdn.microsoft.com/en-us/library/k4s6c3a0.aspx

ApplicationSettings类不支持将设置保存到app.config文件中。这在很大程度上是故意的;使用安全用户帐户运行的应用程序(如Vista UAC)没有对程序安装文件夹的写访问权。

您可以使用ConfigurationManager类来对抗系统。但简单的解决方法是进入设置设计器并将设置的范围更改为User。如果这造成了困难(例如,设置与每个用户相关),则应该将Options特性放在单独的程序中,以便可以请求特权提升提示。或者放弃使用设置。

是的,可以保存配置——但这在很大程度上取决于您选择的保存方式。让我来描述一下技术上的差异,这样你就可以理解你有哪些选择:

首先,你需要区分,你是想在*.exe中使用applicationSettings还是AppSettings。config(也就是Visual Studio中的App.config)文件-这里描述的是基本的区别。

两者都提供了不同的保存更改的方法:

The AppSettings allow you to read and write directly into the config file via config.Save(ConfigurationSaveMode.Modified);, where config is defined as: config = ConfigurationManager.OpenExeConfiguration(ConfigurationUserLevel.None); The applicationSettings allow to read, but if you write changes (via Properties.Settings.Default.Save();) it will be written on a per-user basis, stored in a special place (e.g. C:\Documents and Settings\USERID\Local Settings\Application Data\FIRMNAME\WindowsFormsTestApplicati_Url_tdq2oylz33rzq00sxhvxucu5edw2oghw\1.0.0.0). As Hans Passant mentioned in his answer, this is because a user usually has restricted rights to Program Files and cannot write to it without invoking the UAC prompt. A disadvantage is if you're adding configuration keys in the future you need to synchronize them with every user profile.

但也有一些其他的选择:

Since .NET Core (and .NET 5 and 6) a 3rd option is the appsettings.json file which uses Microsoft's configuration abstraction (and also the secrets.json file which is stored in your user profile rather than in the assemblies directories). But usually WinForms isn't using it, so I am mentioning it just for completeness. However, here are some references how to read and write the values. Alternatively you can use Newtonsoft JSON to read and write the appsettings.json file, but it is not limited to that: you can also create your own json files with that method. As mentioned in the question, there is a 4th option: If you treat the configuration file as XML document, you can load, modify and save it by using the System.Xml.Linq.XDocument class. It is not required to use a custom XML file, you can read the existing config file; for querying elements, you can even use Linq queries. I have given an example here, check out the function GetApplicationSetting there in the answer. A 5th option is to store settings in the registry. How you can do it is described here. Last not least, there is a 6th option: You can store values in the environment (system environment or environment of your account). In Windows settings (the cogwheel in the Windows menu), type in "environment" in the search bar and add or edit them there. To read them, use var myValue = Environment.GetEnvironmentVariable("MyVariable");. Note that your application usually needs to be restarted to get the updated environment settings.

如果你需要加密来保护你的价值,看看这个答案。它描述了如何使用微软的DPAPI来存储加密的值。

如果你想支持你自己的文件,无论是XML还是JSON,知道程序集运行的目录可能会很有用:

var assemblyDLL = System.Reflection.Assembly.GetExecutingAssembly();
var assemblyDirectory = System.IO.Path.GetDirectoryName(assemblyDLL.Location);

您可以使用assemblyDirectory作为存储文件的基本目录。

如果你打算保存到与你的可执行文件相同目录下的文件,这里有一个使用JSON格式的很好的解决方案:

using System;
using System.IO;
using System.Web.Script.Serialization;

namespace MiscConsole
{
    class Program
    {
        static void Main(string[] args)
        {
            MySettings settings = MySettings.Load();
            Console.WriteLine("Current value of 'myInteger': " + settings.myInteger);
            Console.WriteLine("Incrementing 'myInteger'...");
            settings.myInteger++;
            Console.WriteLine("Saving settings...");
            settings.Save();
            Console.WriteLine("Done.");
            Console.ReadKey();
        }

        class MySettings : AppSettings<MySettings>
        {
            public string myString = "Hello World";
            public int myInteger = 1;
        }
    }

    public class AppSettings<T> where T : new()
    {
        private const string DEFAULT_FILENAME = "settings.json";

        public void Save(string fileName = DEFAULT_FILENAME)
        {
            File.WriteAllText(fileName, (new JavaScriptSerializer()).Serialize(this));
        }

        public static void Save(T pSettings, string fileName = DEFAULT_FILENAME)
        {
            File.WriteAllText(fileName, (new JavaScriptSerializer()).Serialize(pSettings));
        }

        public static T Load(string fileName = DEFAULT_FILENAME)
        {
            T t = new T();
            if(File.Exists(fileName))
                t = (new JavaScriptSerializer()).Deserialize<T>(File.ReadAllText(fileName));
            return t;
        }
    }
}

registry/configurationSettings/XML参数似乎仍然非常活跃。随着技术的进步,我都用过了,但我最喜欢的是基于Threed的系统和隔离存储的结合。

下面的示例允许将名为properties的对象存储到隔离存储中的文件中。如:

AppSettings.Save(myobject, "Prop1,Prop2", "myFile.jsn");

可以使用以下方法恢复属性:

AppSettings.Load(myobject, "myFile.jsn");

这只是一个示例,并不是最佳实践的建议。

internal static class AppSettings
{
    internal static void Save(object src, string targ, string fileName)
    {
        Dictionary<string, object> items = new Dictionary<string, object>();
        Type type = src.GetType();

        string[] paramList = targ.Split(new char[] { ',' });
        foreach (string paramName in paramList)
            items.Add(paramName, type.GetProperty(paramName.Trim()).GetValue(src, null));

        try
        {
            // GetUserStoreForApplication doesn't work - can't identify.
            // application unless published by ClickOnce or Silverlight
            IsolatedStorageFile storage = IsolatedStorageFile.GetUserStoreForAssembly();
            using (IsolatedStorageFileStream stream = new IsolatedStorageFileStream(fileName, FileMode.Create, storage))
            using (StreamWriter writer = new StreamWriter(stream))
            {
                writer.Write((new JavaScriptSerializer()).Serialize(items));
            }

        }
        catch (Exception) { }   // If fails - just don't use preferences
    }

    internal static void Load(object tar, string fileName)
    {
        Dictionary<string, object> items = new Dictionary<string, object>();
        Type type = tar.GetType();

        try
        {
            // GetUserStoreForApplication doesn't work - can't identify
            // application unless published by ClickOnce or Silverlight
            IsolatedStorageFile storage = IsolatedStorageFile.GetUserStoreForAssembly();
            using (IsolatedStorageFileStream stream = new IsolatedStorageFileStream(fileName, FileMode.Open, storage))
            using (StreamReader reader = new StreamReader(stream))
            {
                items = (new JavaScriptSerializer()).Deserialize<Dictionary<string, object>>(reader.ReadToEnd());
            }
        }
        catch (Exception) { return; }   // If fails - just don't use preferences.

        foreach (KeyValuePair<string, object> obj in items)
        {
            try
            {
                tar.GetType().GetProperty(obj.Key).SetValue(tar, obj.Value, null);
            }
            catch (Exception) { }
        }
    }
}