Как сохранить настройки приложения в приложении Windows Forms?
-
19-08-2019 - |
Вопрос
То, чего я хочу достичь, очень просто:У меня есть приложение Windows Forms (.NET 3.5), которое использует путь для чтения информации.Этот путь может быть изменен пользователем, используя форму опций, которую я предоставляю.
Теперь я хочу сохранить значение пути в файл для последующего использования.Это будет одна из многих настроек, сохраненных в этом файле.Этот файл будет находиться непосредственно в папке приложения.
Я понимаю, что доступны три варианта:
- Файл настроек конфигурации (appname.exe.config)
- Реестр
- Пользовательский XML-файл
Я читал, что в файле конфигурации .NET не предусмотрено сохранение значений обратно в него.Что касается реестра, я хотел бы уйти от него как можно дальше.
Означает ли это, что я должен использовать пользовательский XML-файл для сохранения параметров конфигурации?Если это так, я хотел бы увидеть пример кода этого (C #).
Я видел другие дискуссии на эту тему, но мне это все еще непонятно.
Решение
Если вы работаете с Visual Studio, то получить постоянные настройки довольно легко.Щелкните правой кнопкой мыши на проекте в Обозревателе решений, выберите Свойства.Перейдите на вкладку Настройки, нажмите на гиперссылку, если настройки не существуют.Используйте вкладку Настройки, чтобы создать настройки приложения.Visual Studio создает файлы Settings.settings
и Settings.Designer.settings
которые содержат одноэлементный класс Settings
унаследованный от База настроек приложений.Вы можете получить доступ к этому классу из своего кода для чтения / записи настроек приложения:
Properties.Settings.Default["SomeProperty"] = "Some Value";
Properties.Settings.Default.Save(); // Saves settings in application configuration file
Этот метод применим как для консоли, Windows Forms, так и для других типов проектов.
Обратите внимание, что вам необходимо установить область применения свойство ваших настроек.Если вы выберете Область применения, затем Настройки.По умолчанию.< ваше свойство > будет доступно только для чтения.
Другие советы
Если вы планируете сохранять в файл в том же каталоге, что и ваш исполняемый файл, вот хорошее решение, которое использует 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;
}
}
}
Доступ к реестру запрещен.Вы не уверены, обладает ли пользователь, который использует ваше приложение, достаточными правами для записи в реестр.
Вы можете использовать app.config
файл для сохранения настроек уровня приложения (которые одинаковы для каждого пользователя, использующего ваше приложение).
Я бы сохранил пользовательские настройки в XML-файле, который был бы сохранен в Изолированное Хранилище или в Специальная папка.Данные приложения справочник.
Кроме того, начиная с .NET 2.0, можно сохранять значения обратно в app.config
файл.
В ApplicationSettings
класс не поддерживает сохранение настроек в файл app.config.Это очень похоже на дизайн, приложения, которые запускаются с должным образом защищенной учетной записью пользователя (например, Vista UAC), не имеют доступа на запись к папке установки программы.
Вы можете бороться с системой с помощью ConfigurationManager
класс.Но тривиальный обходной путь заключается в том, чтобы зайти в конструктор настроек и изменить область действия параметра на User .Если это вызывает трудности (скажем, настройка актуальна для каждого пользователя), вам следует поместить функцию Настроек в отдельную программу, чтобы вы могли запросить запрос на повышение привилегий.Или откажитесь от использования настройки.
Аргумент registry /configurationSettings /XML по-прежнему кажется очень активным.Я использовал их все по мере развития технологии, но мой любимый основан на Система Threed's в сочетании с Изолированное Хранилище.
Следующий пример позволяет сохранять объекты с именем 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) { }
}
}
}
Я хотел поделиться библиотекой, которую я создал для этого.Это крошечная библиотека, но значительное улучшение (ИМХО) по сравнению с файлами .settings .
Библиотека называется Jot (ГитХаб), вот старый Статья проекта Кодекса Я писал об этом.
Вот как вы могли бы использовать его для отслеживания размера и местоположения окна:
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-файла с именем приложения в локальной папке и при запуске прочитать его обратно.
Вот пример сохранения положения и размера формы.
Объект данных конфигурации строго типизирован и прост в использовании:
[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>
Мне не нравится предложенное решение использования web.config
или app.config
.Попробуйте прочитать свой собственный XML.Взгляните на Файлы настроек XML – Больше нет web.config.
Другие варианты: вместо использования пользовательского XML-файла мы можем использовать более удобный для пользователя формат файла:Файл в формате JSON или YAML.
- Если вы используете .NET 4.0 dynamic, эта библиотека действительно проста в использовании (сериализация, десериализация, поддержка вложенных объектов и упорядочивание выходных данных по вашему желанию + объединение нескольких настроек в одну) JsonConfig (использование эквивалентно ApplicationSettingsBase)
- Для библиотеки конфигурации .NET YAML...Я не нашел такого, который был бы таким же простым в использовании, как JsonConfig
Вы можете сохранить файл настроек в нескольких специальных папках (для всех пользователей и для каждого пользователя), как указано здесь Окружающая среда.Перечисление специальных папок и несколько файлов (только для чтения по умолчанию, для каждой роли, для каждого пользователя и т.д.)
- Пример получения пути к специальной папке: C # получение пути к %AppData%
Если вы решите использовать несколько настроек, вы можете объединить эти настройки:Например, объединение настроек для default + BasicUser + AdminUser.Вы можете использовать свои собственные правила:последнее переопределяет значение и т.д.
"Означает ли это, что я должен использовать пользовательский 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/ Гитхаб: https://github.com/cemdervis/SharpConfig
Я надеюсь, что это поможет.
Насколько я могу судить, .NET поддерживает постоянные настройки с помощью встроенного средства настройки приложения:
Функция Настройки приложений Windows Forms упрощает создание, хранение и обслуживание пользовательских приложений и настроек пользователя на клиентском компьютере.С помощью настроек приложения Windows Forms можно хранить не только данные приложения, такие как строки подключения к базе данных, но и данные, относящиеся к конкретному пользователю, такие как настройки пользовательского приложения.Используя Visual Studio или пользовательский управляемый код, вы можете создавать новые параметры, считывать их и записывать на диск, привязывать к свойствам ваших форм и проверять данные настроек перед загрузкой и сохранением.- http://msdn.microsoft.com/en-us/library/k4s6c3a0.aspx
Иногда вы хотите избавиться от этих настроек, хранящихся в традиционном файле web.config или app.config.Вам нужен более детальный контроль над развертыванием ваших записей настроек и дизайном отдельных данных.Или требование состоит в том, чтобы включить добавление новых записей во время выполнения.
Я могу представить себе два хороших варианта:
- Строго типизированную версию и
- Объектно-ориентированная версия.
Преимуществом строго типизированной версии являются строго типизированные имена и значения настроек.Нет никакого риска смешивания имен или типов данных.Недостатком является то, что необходимо закодировать дополнительные настройки, которые не могут быть добавлены во время выполнения.
Преимущество объектно-ориентированной версии заключается в том, что новые настройки могут быть добавлены во время выполнения.Но у вас нет строго типизированных имен и значений.Необходимо быть осторожным со строковыми идентификаторами.Должен знать тип данных, сохраненный ранее при получении значения.
Вы можете найти код обеих полнофункциональных реализаций ЗДЕСЬ.
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();
}
}