пятница, 21 сентября 2007 г.

Сохранение настроек приложения

Решил поделиться опытом :) Проблема сохранения настроек своего приложения - вечная и каждый ее решает по-своему. Я рассмотрю три разных способа ее решения. Естественно, что я не претендую на истину в последней инстанции, но возможно, данная информация окажется кому-нибудь полезной.

1. Сохранение настроек в собственный файл используя сериализацию.

Данный подход обладает достаточно большой гибкостью и удобен в использовании. Однако требует наибольшее количество кода. Итак. Первое что нам понадобится - это класс с настройками. Для удобства использования, лучше всего если все члены класса будут статическими (Это, конечно, не самый лучший способ, однако, если наше приложение очень маленькое и не будет сильно расти/развиваться в дальнейшем, то можно и так. Более универсальный способ - это реализовать для класса с настройками паттерн singleton).
public class MySettings
{
   public static int Setting1;
   public static string Setting2;
}
Думаю - код класса понятен и проблем ни у кого не вызовет :) Для сохранения данных - сериализуем:
XmlSerializer ser = new XmlSerializer(typeof(MySettings));
TextWriter writer = new StreamWriter("settings.xml");
ser.Serialize(writer, MySettings);
writer.Close();
Упс!.. Ошибка: "'MySettings' is a 'type' but is used like a 'variable' (CS0118)" Переделываем:
MySettings settings=new MySettings();

XmlSerializer ser = new XmlSerializer(typeof(MySettings));
TextWriter writer = new StreamWriter("settings.xml");
ser.Serialize(writer, settings);
writer.Close();
Пожалуй стоит пояснить. Первой строкой мы создаем объект класса MySettings, но поскольку он содержит только статические члены - данные в переменных класса не изменятся, но мы получим объект (переменную), которую уже можно сериализовать :) Компилируем, запускаем, смотрим что получилось:

<?xml version="1.0" encoding="utf-8"?>
<MySettings xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:xsd="http://www.w3.org/2001/XMLSchema"/>
Упс!.. А где данные? Проблема в том, что сериализовать статические члены класса нельзя! (В дополнение: xml-сериализация работает только с public-членами класса). Делаем "финт ушами":
public class MySettings
{
    public static int Setting1;
    public static string Setting2;
    public int _Setting1
    {
        get{ return Setting1;}
        set{ Setting1=value;}
    }
    public string _Setting2
    {
        get{ return Setting2;}
        set{ Setting2=value;}
    }
}
Компилируем, запускаем, смотрим:
<?xml version="1.0" encoding="utf-8"?>
<MySettings xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:xsd="http://www.w3.org/2001/XMLSchema">
  <_Setting1>5</_Setting1>
  <_Setting2>test</_Setting2>
</MySettings>
Вот оно! :) Суть в том, что через не-статические public-члены класса сериализатор получает доступ к статическим. :) Ну а теперь - самое легкое: десериализация.
XmlSerializer deser = new XmlSerializer(typeof(MySettings));
TextReader reader = new StreamReader("settings.xml");
settings=((MySettings)ser.Deserialize(reader));
reader.Close();

2. "Ручное" сохранение в файл.

Поклонникам C++ рекомендуется :) В смысле - смотрите и завидуйте! Итак. В .Net Framework в пространстве имен System.IO есть один замечательный класс - File, которым мы и воспользуемся. В частности, статическими функциями ReadAllLines и WriteAllLines. Первое что на надо сделать для сохранения данных, это подготовить и заполнить массив строк:
string[] settings = new string[5];
settings[0] = "setting1=" + setting1.ToString();
settings[2] = "setting1=" + setting2.ToString();
Примечание: Многие увидевшие такой код наверняка захотят оторвать мне руки :)) Однако, сделано это специально, для того, чтобы показать, что делать если настройки сохранить надо, времени мало (лень), да и настроек 2-3 штуки. Естественно, что в серьезном приложении так делать нельзя! После чего пишем одну строку кода(!!!) и все:
File.WriteAllLines("settings.txt", settings);
При желании, можно указать и кодировку:
File.WriteAllLines("settings.txt", settings, Encoding.GetEncoding(1251));
Теперь перейдем к самому ответственному - чтению настроек. :)
string[] settings=File.ReadAllLines("settings.txt");
foreach(string s in settings)
{
    string[] tmp=s.Split(new char[] {'='}, StringSplitOptions.RemoveEmptyEntries);
    if(tmp.Length==2) //на всякий случай 
    {
        switch(tmp[0])
        {
            case "setting1":
                Int32.TryParse(tmp[1], out setting1); //тоже, чтобы не падало :)
                break;
            case "setting2":
                setting2 = tmp[1];
                break;
        }
    }
}
Вообщем-то и все.

3. Использование класса Settings.

Внимание: данный способ работает только в Visual Studio. По крайней мере в Sharp Delevop 2.2 этого точно нет. Более того: данный способ доступен только для WinForms или консольных проектов. Первое что нам надо сделать - это добавить те настройки, что мы хотим сохранять. Делается это в разделе Properties списка файлов нашего проекта. Там дважды кликаем на раздел Settings и получаем в результате окно с таблицей. В этой таблице придумываем и вводим имена перменных (в которых и будут храниться наши настройки) их тип и значение по-умолчанию. Доступ к настройкам осуществляется так:
Properties.Settings.Default.имя_нашей_перменной = значение;
После компиляции рядом .exe файлом будет лежать файл с таким же именем и расширением .exe.config. В этом файле хранятся значения по-умолчанию для наших настроек. Об этом надо помнить! Сами же настройки приложения хранятся в
Documents and Settings\имя_пользователя\Local Settings\Application Data\имя_компании_из_AssemblyInfo.cs\имя_приложения.exe_StrongName_<разные левые числа>\номер_версии"
Причем номер версии берется в AssemblyInfo.cs из атрибута [assembly: AssemblyVersion("1.0.0.0")] Если его изменить - то путь для настроек также изменится и, соответсвенно - наше приложение запустится с настройками по-умолчанию. (Тому, кто это придумал в Microsoft, по-хорошему следовало бы оторвать руки...) К счастью - атрибут [assembly: AssemblyFileVersion("1.0.0.0")] можно менять как хочется :) Самое веселое, что сохранять текущие настройки куда-либо помимо указанного пути стандартными средствами нельзя! Настройки загружаются автоматически при старте программы. Сохранять их надо вручную, вот так:
Properties.Settings.Default.Save();
В случае, если вам надо сбросить все настройки в их значения по умолчанию, используется команда:
Properties.Settings.Default.Reset();
Ну и хватит наверное :) Более подробно - читаем МСДН.

4 комментария:

  1. Этот комментарий был удален администратором блога.

    ОтветитьУдалить
  2. An unhandled exception of type 'System.StackOverflowException' occurred in ... выдаёт ошибку при попытке XmlSerializer deser = new XmlSerializer(typeof(MySettings));
    TextReader reader = new StreamReader(openFileDialog2.FileName);
    settings = ((MySettings)deser.Deserialize(reader));
    reader.Close();

    ОтветитьУдалить
  3. Проблема у вас в коде класса MySettings. Насколько я могу предположить - где-то там есть свойство, которое ссылается на само себя или что-то похожее.

    Вообщем, System.StackOverflowException говорит о том, что какая-то функция бесконечно вызывается по кругу (сама из себя).

    ОтветитьУдалить
  4. Спасибо большое, реально не знал уже как замутить сохранение конфига Понравился 2 и 3 способ, так как они самые понятные как для начинающего...

    ОтветитьУдалить