Лучшая практика сохранения настроек приложения в приложении Windows Forms

То, что я хочу достичь, очень просто: у меня есть приложение Windows Forms (.NET 3.5), которое использует путь для чтения информации. Этот путь может быть изменен пользователем, используя форму, которую я предоставляю.

Теперь я хочу сохранить значение пути в файл для последующего использования. Это будет одна из многих настроек, сохраненных в этом файле. Этот файл будет находиться непосредственно в папке приложения.

Я понимаю, что доступны три варианта:

  • Файл ConfigurationSettings (appname.exe.config)
  • реестр
  • Пользовательский XML-файл

Я читал, что конфигурационный файл .NET не предусмотрен для сохранения значений. Что касается реестра, я хотел бы получить как можно дальше от него.

Означает ли это, что я должен использовать собственный файл XML для сохранения настроек конфигурации? Если это так, я хотел бы увидеть пример кода (C #).

Я видел другие дискуссии по этому вопросу, но мне все еще не ясно.

Если вы работаете с Visual Studio, довольно легко получить устойчивые настройки. Щелкните правой кнопкой мыши по проекту в обозревателе решений, выберите «Свойства». Перейдите на вкладку «Настройки», нажмите гиперссылку, если настройки не существуют. Используйте вкладку «Настройки», чтобы создать настройки приложения. Visual Studio создает файлы Settings.settings и Settings.Designer.settings которые содержат параметры Singleton, унаследованные от ApplicationSettingsBase . Вы можете получить доступ к этому classу из своего кода для чтения / записи настроек приложения:

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

Этот метод применим как для консоли, Windows Forms, так и для других типов проектов.

Обратите внимание, что вам нужно установить свойство области ваших настроек. Если вы выберете область приложения, то Settings.Default. <Ваше свойство> будет доступно только для чтения.

Если вы планируете сохранить файл в том же каталоге, что и ваш исполняемый файл, вот хорошее решение, которое использует формат 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; } } } 

Реестр не работает. Вы не уверены, имеет ли пользователь, который использует ваше приложение, достаточные права на запись в реестр.

Файл app.config можно использовать для сохранения настроек уровня приложения (которые одинаковы для каждого пользователя, использующего ваше приложение).

Я бы сохранил пользовательские настройки в файле XML, который будет сохранен в изолированном хранилище или в каталоге SpecialFolder.ApplicationData .

Рядом с этим, как и с .NET 2.0, можно сохранить значения обратно в файл app.config .

Класс ApplicationSettings не поддерживает сохранение настроек в файле app.config. Это очень по дизайну, приложения, которые работают с правильно защищенной учетной записью пользователя (думаю, Vista UAC) не имеют права на запись в папку установки программы.

Вы можете бороться с системой с помощью classа ConfigurationManager . Но тривиальное обходное решение заключается в том, чтобы войти в конструктор настроек и изменить область настройки для пользователя. Если это вызывает трудности (скажем, настройка относится к каждому пользователю), вы должны поместить функцию «Параметры» в отдельную программу, чтобы вы могли запросить приглашение на повышение привилегий. Или отказаться от использования настройки.

Аргумент registry / configurationSettings / XML по-прежнему кажется очень активным. Я использовал их все, так как технология прогрессировала, но мой фаворит основан на системе Тридда в сочетании с изолированным хранилищем .

Следующий пример позволяет хранить объекты с именем 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) { } } } } 

Я хотел поделиться библиотекой, которую я создал для этого. Это небольшая библиотека, но большое улучшение (IMHO) над файлами .settings.

Библиотека называется Jot (GitHub) , вот старая статья Code Project, которую я написал об этом.

Вот как вы можете использовать его для отслеживания размера и местоположения windows:

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

Преимущество по сравнению с файлами .settings: значительно меньше кода, и он намного менее подвержен ошибкам, так как вам нужно только указать каждое свойство один раз .

С помощью файлов настроек вам нужно указать каждое свойство пять раз: один раз, когда вы явно создаете свойство и еще четыре раза в коде, который копирует значения взад и вперед.

Хранение, сериализация и т. Д. Полностью настраиваются. При использовании инверсии элемента управления вы можете подключить его так, чтобы он автоматически отслеживал все объекты, которые он разрешает, чтобы все, что вам нужно сделать, чтобы сохранить свойство, – это пощелкать на нем атрибут [Trackable].

Я пишу все это, потому что я считаю, что библиотека на высшем уровне, и я бы хотел популяризировать ее 🙂

Простым способом является использование объекта данных конфигурации, сохранение его как файла 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   

Мне не нравится предлагаемое решение для использования web.config или app.config . Попробуйте прочитать свой собственный XML. Посмотрите на файлы настроек XML – больше нет web.config .

Насколько я могу судить, .NET поддерживает постоянные настройки с помощью встроенного средства настройки приложений:

Функция «Параметры приложения» в Windows Forms упрощает создание, хранение и обслуживание пользовательских приложений и пользовательских настроек на клиентском компьютере. С настройками приложения Windows Forms вы можете хранить не только данные приложения, такие как строки подключения к базе данных, но и данные, специфичные для пользователя, такие как настройки пользовательского приложения. Используя Visual Studio или настраиваемый управляемый код, вы можете создавать новые настройки, читать их и записывать на диск, привязывать их к свойствам в своих формах и проверять данные настроек до загрузки и сохранения. – http://msdn.microsoft.com/en-us/library/k4s6c3a0.aspx

Другие варианты, вместо использования пользовательского XML-файла, мы можем использовать более удобный формат файла: JSON или YAML-файл.

  • Если вы используете динамический .NET 4.0, эта библиотека очень проста в использовании (сериализация, десериализация, поддержка вложенных объектов и упорядочение вывода по вашему желанию + объединение нескольких параметров в один) JsonConfig (использование эквивалентно ApplicationSettingsBase)
  • Для библиотеки конфигурации .NET YAML … Я не нашел такой простой, как JsonConfig

Вы можете сохранить файл настроек в нескольких специальных папках (для всех пользователей и для каждого пользователя), как указано здесь. Environment.SpecialFolder Enumeration и несколько файлов (только для чтения по умолчанию, для каждой роли, для каждого пользователя и т. Д.).

  • Образец для получения пути к специальной папке: C #, получающий путь от% AppData%

Если вы решите использовать несколько параметров, вы можете объединить эти настройки: например, слияние настроек по умолчанию + BasicUser + AdminUser. Вы можете использовать свои собственные правила: последний переопределяет значение и т. Д.

Иногда вы хотите избавиться от этих настроек, хранящихся в традиционном файле web.config или app.config. Вы хотите более мелкомасштабный контроль над развертыванием ваших записей настроек и разделенным дизайном данных. Или необходимо включить добавление новых записей во время выполнения.

Я могу представить два хороших варианта:

  • Строго типизированная версия и
  • Объектно-ориентированная версия.

Преимущество строго типизированной версии – это строго типизированные имена и значения параметров. Нет риска смешения имен или типов данных. Недостатком является то, что больше настроек должно быть закодировано, не может быть добавлено во время выполнения.

Преимуществом объектно-ориентированной версии является то, что во время выполнения можно добавлять новые настройки. Но у вас нет строго типизированных имен и значений. Будьте осторожны с строковыми идентификаторами. Должен знать тип данных, сохраненный ранее при получении значения.

Вы можете найти код как полнофункциональных реализаций ЗДЕСЬ .

«Означает ли это, что я должен использовать собственный файл XML для сохранения настроек конфигурации?» Нет, не обязательно. Для таких операций мы используем SharpConfig.

Например, если файл конфигурации подобен этому

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

Он совместим с .Net 2.0 и выше. Мы можем создавать конфигурационные файлы на лету, и мы можем сохранить их позже. Источник: http://sharpconfig.net/ Github: https://github.com/cemdervis/SharpConfig

Я надеюсь, что это помогает.

 public static class SettingsExtensions { public static bool TryGetValue(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(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(); } } 
Давайте будем гением компьютера.