C# 如何在 Windows 窗体应用程序中保存应用程序设置?

声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow 原文地址: http://stackoverflow.com/questions/453161/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me): StackOverFlow

提示:将鼠标放在中文语句上可以显示对应的英文。显示中英文
时间:2020-08-04 03:57:46  来源:igfitidea点击:

How can I save application settings in a Windows Forms application?

c#xmlwinformsconfiguration-filesapplication-settings

提问by Fueled

What I want to achieve is very simple: I have a Windows Forms (.NET 3.5) application that uses a path for reading information. This path can be modified by the user, by using the options form I provide.

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

Now, I want to save the path value to a file for later use. This would be one of the many settings saved to this file. This file would sit directly in the application folder.

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

I understand three options are available:

我知道有三个选项可用:

  • ConfigurationSettings file (appname.exe.config)
  • Registry
  • Custom XML file
  • 配置设置文件 (appname.exe.config)
  • 注册表
  • 自定义 XML 文件

I read that the .NET configuration file is not foreseen for saving values back to it. As for the registry, I would like to get as far away from it as possible.

我读到 .NET 配置文件无法将值保存回它。至于注册表,我想尽可能远离它。

Does this mean that I should use a custom XML file to save configuration settings?

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

If so, I would like to see code example of that (C#).

如果是这样,我想看看它的代码示例(C#)。

I have seen other discussions on this subject, but it is still not clear to me.

我已经看到关于这个主题的其他讨论,但我仍然不清楚。

采纳答案by aku

If you work with Visual Studio then it is pretty easy to get persistable settings. Right click on the project in Solution Explorer and choose Properties. Select the Settings tab and click on the hyperlink if settings doesn't exist.

如果您使用 Visual Studio,那么很容易获得可持久化的设置。右键单击解决方案资源管理器中的项目并选择属性。如果设置不存在,请选择“设置”选项卡并单击超链接。

Use the Settings tab to create application settings. Visual Studio creates the files Settings.settingsand Settings.Designer.settingsthat contain the singleton class Settingsinherited from ApplicationSettingsBase. You can access this class from your code to read/write application settings:

使用设置选项卡创建应用程序设置。Visual Studio 创建文件Settings.settingsSettings.Designer.settings包含SettingsApplicationSettingsBase继承 的单例类。您可以从代码访问此类以读取/写入应用程序设置:

Properties.Settings.Default["SomeProperty"] = "Some Value";
Properties.Settings.Default.Save(); // Saves settings in application configuration file

This technique is applicable both for console, Windows Forms, and other project types.

此技术适用于控制台、Windows 窗体和其他项目类型。

Note that you need to set the scopeproperty of your settings. If you select Application scope then Settings.Default.<your property> will be read-only.

请注意,您需要设置设置的范围属性。如果您选择应用程序范围,则 Settings.Default.<your property> 将是只读的。

Reference: How To: Write User Settings at Run Time with C#- Microsoft Docs

参考:如何:使用 C# 在运行时编写用户设置- Microsoft Docs

回答by Frederik Gheysels

The registry is a no-go. You're not sure whether the user which uses your application, has sufficient rights to write to the registry.

注册表是不行的。您不确定使用您的应用程序的用户是否具有写入注册表的足够权限。

You can use the app.configfile to save application-level settings (that are the same for each user who uses your application).

您可以使用该app.config文件来保存应用程序级别的设置(对于使用您的应用程序的每个用户都是相同的)。

I would store user-specific settings in an XML file, which would be saved in Isolated Storageor in the SpecialFolder.ApplicationDatadirectory.

我会将用户特定的设置存储在 XML 文件中,该文件将保存在独立存储SpecialFolder.ApplicationData目录中。

Next to that, as from .NET 2.0, it is possible to store values back to the app.configfile.

其次,从 .NET 2.0 开始,可以将值存储回app.config文件。

回答by Jacob

As far as I can tell, .NET does support persisting settings using the built-in application settings facility:

据我所知,.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

Windows 窗体的应用程序设置功能可以轻松地在客户端计算机上创建、存储和维护自定义应用程序和用户首选项。使用 Windows 窗体应用程序设置,您不仅可以存储应用程序数据(如数据库连接字符串),还可以存储用户特定的数据,如用户应用程序首选项。使用 Visual Studio 或自定义托管代码,您可以创建新设置,从磁盘读取它们并将它们写入磁盘,将它们绑定到表单上的属性,并在加载和保存之前验证设置数据。- http://msdn.microsoft.com/en-us/library/k4s6c3a0.aspx

回答by Hans Passant

The ApplicationSettingsclass doesn't support saving settings to the app.configfile. That's very much by design; applications that run with a properly secured user account (think Vista UAC) do not have write access to the program's installation folder.

ApplicationSettings类不支持设置保存到的app.config文件。这在很大程度上是设计使然;使用适当保护的用户帐户(想想 Vista UAC)运行的应用程序没有对程序安装文件夹的写访问权限。

You can fight the system with the ConfigurationManagerclass. But the trivial workaround is to go into the Settings designer and change the setting's scope to User. If that causes hardships (say, the setting is relevant to every user), you should put your Options feature in a separate program so you can ask for the privilege elevation prompt. Or forego using a setting.

您可以与ConfigurationManager班级对抗系统。但简单的解决方法是进入设置设计器并将设置的范围更改为用户。如果这造成困难(例如,该设置与每个用户相关),您应该将您的选项功能放在一个单独的程序中,以便您可以要求权限提升提示。或者放弃使用设置。

回答by gatapia

I don't like the proposed solution of using web.configor app.config. Try reading your own XML. Have a look at XML Settings Files – No more web.config.

我不喜欢使用web.configor的建议解决方案app.config。尝试阅读您自己的 XML。看看XML 设置文件 - 不再是 web.config

回答by Trevor

If you are planning on saving to a file within the same directory as your executable, here's a nice solution that uses the JSONformat:

如果您打算保存到与可执行文件位于同一目录中的文件,这里有一个使用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;
        }
    }
}

回答by Boczek

The registry/configurationSettings/XML argument still seems very active. I've used them all, as the technology has progressed, but my favourite is based on Threed's systemcombined with Isolated Storage.

registry/configurationSettings/XML 参数似乎仍然非常活跃。随着技术的进步,我已经全部使用了它们,但我最喜欢的是基于Threed 的系统隔离存储

The following sample allows storage of an objects named properties to a file in isolated storage. Such as:

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

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

Properties may be recovered using:

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

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

It is just a sample, not suggestive of best practices.

这只是一个示例,并不暗示最佳实践。

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) { }
        }
    }
}

回答by user3130351

Sometimes you want to get rid of those settings kept in the traditional web.config or app.config file. You want more fine grained control over the deployment of your settings entries and separated data design. Or the requirement is to enable adding new entries at runtime.

有时您想摆脱保留在传统 web.config 或 app.config 文件中的那些设置。您希望对设置条目的部署和分离的数据设计进行更细粒度的控制。或者要求在运行时启用添加新条目。

I can imagine two good options:

我可以想象两个不错的选择:

  • The strongly typed version and
  • The object oriented version.
  • 强类型版本和
  • 面向对象的版本。

The advantage of the strongly typed version are the strongly typed settings names and values. There is no risk of intermixing names or data types. The disadvantage is that more settings have to be coded, cannot be added at runtime.

强类型版本的优点是强类型设置名称和值。没有混合名称或数据类型的风险。缺点是需要编码更多的设置,不能在运行时添加。

With the object oriented version the advantage is that new settings can be added at runtime. But you do not have strongly typed names and values. Must be careful with string identifiers. Must know data type saved earlier when getting a value.

面向对象版本的优点是可以在运行时添加新设置。但是您没有强类型名称和值。必须小心字符串标识符。获取值时必须知道之前保存的数据类型。

You can find the code of both fully functional implementations HERE.

您可以在此处找到两个全功能实现的代码。

回答by kite

Other options, instead of using a custom XML file, we can use a more user friendly file format: JSON or YAML file.

其他选项,而不是使用自定义 XML 文件,我们可以使用更用户友好的文件格式:JSON 或 YAML 文件。

  • If you use .NET 4.0 dynamic, this library is really easy to use (serialize, deserialize, nested objects support and ordering output as you wish + merging multiple settings to one) JsonConfig(usage is equivalent to ApplicationSettingsBase)
  • For .NET YAML configuration library... I haven't found one that is as easy to use as JsonConfig
  • 如果你使用 .NET 4.0 动态,这个库真的很容易使用(序列化,反序列化,嵌套对象支持和排序输出,如你所愿+将多个设置合并为一个)JsonConfig(用法相当于ApplicationSettingsBase)
  • 对于.NET YAML 配置库...我还没有找到像 JsonConfig 一样容易使用的

You can store your settings file in multiple special folders (for all users and per user) as listed here Environment.SpecialFolder Enumerationand multiple files (default read only, per role, per user, etc.)

您可以将设置文件存储在此处列出的多个特殊文件夹中(对于所有用户和每个用户)Environment.SpecialFolder Enumeration和多个文件(默认只读、每个角色、每个用户等)

If you choose to use multiple settings, you can merge those settings: For example, merging settings for default + BasicUser + AdminUser. You can use your own rules: the last one overrides the value, etc.

如果您选择使用多个设置,则可以合并这些设置:例如,合并 default + BasicUser + AdminUser 的设置。您可以使用自己的规则:最后一个覆盖值等。

回答by Dieter Meemken

A simple way is to use a configuration data object, save it as an XML file with the name of the application in the local Folder and on startup read it back.

一个简单的方法是使用一个配置数据对象,将它保存为一个 XML 文件,在本地文件夹中使用应用程序的名称,然后在启动时读回它。

Here is an example to store the position and size of a form.

这是一个存储表单位置和大小的示例。

The configuration dataobject is strongly typed and easy to use:

配置数据对象是强类型且易于使用的:

[Serializable()]
public class CConfigDO
{
    private System.Drawing.Point m_oStartPos;
    private System.Drawing.Size m_oStartSize;

    public System.Drawing.Point StartPos
    {
        get { return m_oStartPos; }
        set { m_oStartPos = value; }
    }

    public System.Drawing.Size StartSize
    {
        get { return m_oStartSize; }
        set { m_oStartSize = value; }
    }
}

A manager class for saving and loading:

用于保存和加载的管理器类:

public class CConfigMng
{
    private string m_sConfigFileName = System.IO.Path.GetFileNameWithoutExtension(System.Windows.Forms.Application.ExecutablePath) + ".xml";
    private CConfigDO m_oConfig = new CConfigDO();

    public CConfigDO Config
    {
        get { return m_oConfig; }
        set { m_oConfig = value; }
    }

    // Load configuration file
    public void LoadConfig()
    {
        if (System.IO.File.Exists(m_sConfigFileName))
        {
            System.IO.StreamReader srReader = System.IO.File.OpenText(m_sConfigFileName);
            Type tType = m_oConfig.GetType();
            System.Xml.Serialization.XmlSerializer xsSerializer = new System.Xml.Serialization.XmlSerializer(tType);
            object oData = xsSerializer.Deserialize(srReader);
            m_oConfig = (CConfigDO)oData;
            srReader.Close();
        }
    }

    // Save configuration file
    public void SaveConfig()
    {
        System.IO.StreamWriter swWriter = System.IO.File.CreateText(m_sConfigFileName);
        Type tType = m_oConfig.GetType();
        if (tType.IsSerializable)
        {
            System.Xml.Serialization.XmlSerializer xsSerializer = new System.Xml.Serialization.XmlSerializer(tType);
            xsSerializer.Serialize(swWriter, m_oConfig);
            swWriter.Close();
        }
    }
}

Now you can create an instance and use in your form's load and close events:

现在您可以创建一个实例并在表单的加载和关闭事件中使用:

    private CConfigMng oConfigMng = new CConfigMng();

    private void Form1_Load(object sender, EventArgs e)
    {
        // Load configuration
        oConfigMng.LoadConfig();
        if (oConfigMng.Config.StartPos.X != 0 || oConfigMng.Config.StartPos.Y != 0)
        {
            Location = oConfigMng.Config.StartPos;
            Size = oConfigMng.Config.StartSize;
        }
    }

    private void Form1_FormClosed(object sender, FormClosedEventArgs e)
    {
        // Save configuration
        oConfigMng.Config.StartPos = Location;
        oConfigMng.Config.StartSize = Size;
        oConfigMng.SaveConfig();
    }

And the produced XML file is also readable:

生成的 XML 文件也是可读的:

<?xml version="1.0" encoding="utf-8"?>
<CConfigDO xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:xsd="http://www.w3.org/2001/XMLSchema">
  <StartPos>
    <X>70</X>
    <Y>278</Y>
  </StartPos>
  <StartSize>
    <Width>253</Width>
    <Height>229</Height>
  </StartSize>
</CConfigDO>