现在,我希望将此路径值保存到文件中以备后用。这将是保存到该文件的许多设置之一。该文件将直接位于应用程序文件夹中。
我了解到有三个选择:
ConfigurationSettings文件(appname.exe.config) 注册表 自定义XML文件
据我所知,.NET配置文件不适用于将值保存回去。至于注册表,我希望尽可能远离它。
这是否意味着我应该使用自定义XML文件来保存配置设置?
如果是这样,我想看到C#代码示例。
我看过其他关于此主题的讨论,但对我来说仍不清楚。
Settings
的Settings.settings
和Settings.Designer.settings
文件。您可以从代码中访问此类来读写应用程序设置:Properties.Settings.Default["SomeProperty"] = "Some Value";
Properties.Settings.Default.Save(); // Saves settings in application configuration file
这种技术适用于控制台、Windows Forms以及其他类型的项目。
请注意需要设置您的设置文件的作用域属性。如果选择应用程序作用域,则Settings.Default.<your property>将是只读的。
参考文献:使用C#在运行时编写用户设置的方法 - 微软文档
Settings.Default.SomeProperty = 'value'; Settings.Default.Save();
运行得很好。这是因为我有用户设置吗? - doekmanSettings.Default.Save()
不起作用是不正确的。正如 @aku 在回答中所说,应用程序范围的设置为只读:对它们进行保存是无效的。使用那个自定义的 PortableSettingsProvider 将用户范围的设置保存到 app.config 中,该文件位于 exe 所在的位置,而不是用户的 AppData 文件夹中。虽然并不是一般情况下的最佳实践,但我在开发过程中使用它来实现在每次编译时使用相同的设置(如果没有它,则每次编译都会产生新的唯一用户文件夹)。 - minnow如果您计划在与可执行文件相同的目录中保存文件,这里有一个使用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;
}
}
}
DEFAULT_FILENAME
,只需调用settings.Save(theFileToSaveTo)
即可;由于全大写,DEFAULT_FILENAME
应该是一个常量。如果你想要一个读写属性,请创建一个并让构造函数将其设置为DEFAULT_FILENAME
。然后将默认参数值设为null
,测试它并使用你的属性作为默认值。这样会多打一些字,但可以给你提供一个更标准的接口。 - Jesse ChisholmSystem.Web.Extensions.dll
,则需要引用它。 - TEK注册表不允许。您不确定使用您的应用程序的用户是否具有足够的权限可以写入注册表。
您可以使用app.config
文件保存应用程序级别的设置(对于每个使用您的应用程序的用户相同)。
我会将特定于用户的设置存储在XML文件中,并将其保存在隔离存储或SpecialFolder.ApplicationData目录中。
此外,从.NET 2.0开始,可以将值存储回app.config
文件。
ApplicationSettings
类不支持将设置保存到 app.config 文件中。这是有意设计的;以适当安全的用户帐户运行的应用程序(如 Vista UAC)无法写入程序的安装文件夹。
你可以使用 ConfigurationManager
类来绕过系统限制。但是,一个简单的解决方法是进入“设置”设计器并将设置范围更改为“用户”。如果这造成了困难(例如,该设置与每个用户相关),则应将您的选项功能放在一个单独的程序中,这样您就可以请求提升权限提示。或者放弃使用设置。
我想分享我建立的一个库。这是一个小型的库,但在我看来比.settings文件有很大的改进。
这个库叫做Jot (GitHub)。这里是我以前写的一个Code Project文章。
这是如何使用它来跟踪窗口的大小和位置:
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] 属性使其持久。
它可以高度配置,您可以配置: - 数据何时全局持久化和应用或针对每个跟踪的对象 - 序列化方式 - 存储位置(例如:文件、数据库、在线、隔离存储、注册表) - 可取消应用/持久化属性数据的规则
相信我,这个库非常出色!
注册表/配置设置/XML参数仍然非常活跃。随着技术的进步,我已经使用了它们所有,但我的最爱是基于Threed系统与独立存储相结合。
以下示例允许将名为“属性”的对象存储到独立存储中的文件中。例如:
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) { }
}
}
}
一个简单的方法是使用配置数据对象,将其保存为以应用程序名称命名的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 文件也是可读的:
<?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>
config.Save(ConfigurationSaveMode.Modified);
直接读写配置文件,其中config定义为:config = ConfigurationManager.OpenExeConfiguration(ConfigurationUserLevel.None);
Properties.Settings.Default.Save();
),它将按用户基础写入,存储在特定位置(例如C:\Documents and Settings\USERID\Local Settings\Application Data\FIRMNAME\WindowsFormsTestApplicati_Url_tdq2oylz33rzq00sxhvxucu5edw2oghw\1.0.0.0
)。正如Hans Passant在他的答案中提到的那样,这是因为用户通常对程序文件具有受限权限,不能在不调用UAC提示的情况下写入它。缺点是,如果将来要添加配置键,则需要与每个用户配置文件同步。但还有一些其他替代选项:
自从.NET Core(以及.NET 5和6)出现后,第三个选项是使用Microsoft的配置抽象的appsettings.json
文件(以及存储在用户资料中而不是程序集目录中的secrets.json
文件)。但通常WinForms不会使用它,所以我只是为了完整性而提到它。然而,这里有一些关于如何读取和写入这些值的参考。或者你可以使用Newtonsoft JSON来读取和写入appsettings.json
文件,但它不仅限于此:你还可以用这种方法创建自己的json文件。System.Xml.Linq.XDocument
类加载、修改和保存它。不需要使用自定义XML文件,你可以读取现有的配置文件;对于查询元素,甚至可以使用Linq查询。我在这里给出了一个示例,请查看答案中的GetApplicationSetting
函数。var myValue = Environment.GetEnvironmentVariable("MyVariable");
var assemblyDLL = System.Reflection.Assembly.GetExecutingAssembly();
var assemblyDirectory = System.IO.Path.GetDirectoryName(assemblyDLL.Location);
[General]
# a comment
SomeString = Hello World!
SomeInteger = 10 # an inline comment
var config = Configuration.LoadFromFile("sample.cfg");
var section = config["General"];
string someString = section["SomeString"].StringValue;
int someInteger = section["SomeInteger"].IntValue;