当前位置:  开发笔记 > 编程语言 > 正文

在Windows窗体应用程序中保存应用程序设置的最佳实践

如何解决《在Windows窗体应用程序中保存应用程序设置的最佳实践》经验,为你挑选了9个好方法。

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

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

我理解有三种选择:

ConfigurationSettings文件(appname.exe.config)

注册处

自定义XML文件

我读到.NET配置文件没有预见到将值保存回来.至于注册表,我想尽可能远离它.

这是否意味着我应该使用自定义XML文件来保存配置设置?如果是这样,我希望看到代码示例(C#).

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



1> aku..:

如果您使用Visual Studio,那么很容易获得可持久的设置.在Solution Explorer中右键单击项目,选择Properties.选择"设置"选项卡,如果设置不存在,请单击超链接.使用"设置"选项卡创建应用程序设置.Visual Studio创建文件Settings.settingsSettings.Designer.settings包含Settings从ApplicationSettingsBase继承 的单例类.您可以从代码中访问此类以读取/写入应用程序设置:

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

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

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


@Four:我这里有一个.NET 4.0 WinApp项目,我的SomeProperty不是只读的.`Settings.Default.SomeProperty ='value'; Settings.Default.Save();`就像一个魅力.或者是因为我有用户设置?
截至目前,使用.NET 3.5,您可以使用Settings.Default.SomeProperty来分配值并获得强大的类型转换.另外,为了节省其他时间(花了一些时间来解决这个问题),您需要键入Properties.Settings.Default,或使用YourProjectNameSpace.Settings添加到文件的顶部.单独的"设置"未定义/找到.
@Four:当我将设置从User更改为Application-scope并保存文件时,我在生成的代码中看到setter消失了.这也适用于客户端配置文件4.0 ...
@Four:很棒的链接,虽然你的声明`Settings.Default.Save()`什么都不做是不正确的.正如@aku在答案中所述,app-scope设置是只读的:save对它们无效.使用该自定义[PortableSettingsProvider](http://www.geek-republic.com/2010/11/c-portable-settings-provider/)将用户范围设置保存到位于exe所在的app.config而不是用户的AppData文件夹中的那个.不,通常不是很好,但我在开发过程中使用它从编译到编译使用相同的设置(没有它,它们在每次编译时都会使用新的唯一用户文件夹).
如果我有解决方案,这是否适用于整个解决方案或每个项目?
默认情况下,`Settings.Default.Save()`方法不执行任何操作.您必须使用不同的提供者.请参阅:http://www.geek-republic.com/2010/11/08/c-portable-settings-provider/
更好/更简单的答案(不,不是反垃圾邮件尝试 - 只是把它放在这里,因为这是这个问题的最流行的版本):http://stackoverflow.com/a/8313682/409856

2> Trevor..:

如果您计划保存到与可执行文件相同的目录中的文件,这是一个使用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
        {
            public string myString = "Hello World";
            public int myInteger = 1;
        }
    }

    public class AppSettings 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(File.ReadAllText(fileName));
            return t;
        }
    }
}


如果你还没有,你需要引用`System.Web.Extensions.dll`.
我已经基于这个答案创建了一个完整的库,并进行了许多改进,并在nuget中提供了它:https://github.com/Nucs/JsonSettings

3> Frederik Ghe..:

注册表是禁止的.您不确定使用您的应用程序的用户是否有足够的权限写入注册表.

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

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

接下来,从.NET 2.0开始,可以将值存储回app.config文件.


注册表是不可移植的
@thenonhacker:或者使用Environment.GetFolderPath(Environment.SpecialFolder.ApplicationData)
但是,如果您需要按登录/用户设置,请使用注册表.
可以写入用户注册表(许多程序在那里写入信息,用户权限永远不会成为问题).使用注册表而不是使用设置的优点是,如果您有多个应用程序共享同一个文件夹(例如,安装程序和应用程序),它们将不会共享相同的设置.

4> Hans Passant..:

ApplicationSettings类不支持设置保存到app.config文件.这非常依赖于设计,使用正确安全的用户帐户(想想Vista UAC)运行的应用程序没有对程序安装文件夹的写入权限.

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


单独的程序需要清单来要求提升.谷歌'asinvoker要求管理员'找到正确的语法.编辑user.config是不实际的,也不是必需的.

5> anakic..:

我想分享一个我为此而建的图书馆.这是一个很小的库,但对.settings文件有很大改进(恕我直言).

该库名为Jot(GitHub),这是我写的关于它的旧代码项目文章.

以下是您如何使用它来跟踪窗口的大小和位置:

public MainWindow()
{
    InitializeComponent();

    _stateTracker.Configure(this)
        .IdentifyAs("MyMainWindow")
        .AddProperties(nameof(Height), nameof(Width), nameof(Left), nameof(Top), nameof(WindowState))
        .RegisterPersistTrigger(nameof(Closed))
        .Apply();
}

与.settings文件相比的好处:代码要少得多,而且容易出错,因为你只需要提一次每个属性.

使用设置文件,您需要提及五次每个属性:一次是在显式创建属性时,另外四次是在代码中来回复制值.

存储,序列化等是完全可配置的.当目标对象由IOC容器创建时,您可以[挂钩] []以便它自动将跟踪应用于它所解析的所有对象,这样您需要做的就是使属性持久化是一个[Trackable]属性在上面.

它是高度可配置的,您可以配置: - 数据持久化并应用于全局或每个跟踪对象 - 如何序列化 - 存储位置(例如文件,数据库,在线,隔离存储,注册表) - 可以取消应用/持久化的规则财产的数据

相信我,图书馆是一流的!



6> 小智..:

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 items = new Dictionary();
        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 items = new Dictionary();
        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>(reader.ReadToEnd());
            }
        }
        catch (Exception) { return; }   // If fails - just don't use preferences.

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



7> Dieter Meemk..:

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

以下是存储表单位置和大小的示例.

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

[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; }
    }
}

用于保存和加载的经理类:

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();
        }
    }
}

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

    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();
    }

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



  
    70
    278
  
  
    253
    229
  



8> gatapia..:

我不喜欢使用web.config或提议的解决方案app.config.尝试阅读自己的XML.看看XML设置文件 - 不再需要web.config.



9> kite..:

除了使用自定义XML文件以外,我们还可以使用其他更友好的文件格式:JSON或YAML文件。

如果您使用.NET 4.0 dynamic,那么此库确实非常易于使用(序列化,反序列化,嵌套对象支持和按需排序输出顺序+将多个设置合并为一个)JsonConfig(用法等效于ApplicationSettingsBase)

对于.NET YAML配置库...我还没有找到一个像JsonConfig一样易于使用的库

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

获取特殊文件夹路径的示例:C#获取%AppData%的路径

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

推荐阅读
喜生-Da
这个屌丝很懒,什么也没留下!
DevBox开发工具箱 | 专业的在线开发工具网站    京公网安备 11010802040832号  |  京ICP备19059560号-6
Copyright © 1998 - 2020 DevBox.CN. All Rights Reserved devBox.cn 开发工具箱 版权所有