c # - подход к сохранению пользовательских настроек в приложении WPF?

85

Какой подход вы рекомендуете для сохранения пользовательских настроек в приложении WPF для Windows (для настольных ПК)? Обратите внимание, что идея состоит в том, что пользователь может изменить свои настройки во время выполнения, а затем может закрыть приложение, а затем при запуске приложения позже приложение будет использовать текущие настройки. Фактически тогда это будет выглядеть так, как будто настройки приложения не меняются.

Q1 - База данных или другой подход? У меня есть база данных sqlite, которую я буду использовать в любом случае, поэтому использование таблицы в базе данных было бы так же хорошо, как и любой другой подход?

Q2 - Если база данных: какой дизайн таблицы базы данных? Одна таблица со столбцами для разных типов данных, которые могут быть (например string, longи DateTimeт. Д.) ИЛИ просто таблица со строкой для значения, по которому вам нужно сериализовать и десериализовать значения? Думаю, первое было бы проще, а если настроек не так много, накладных расходов не так много?

Q3 - Могут ли для этого использоваться настройки приложения? Если да, то требуются ли какие-то особые задачи для включения персистентности? И что в этом случае произойдет с использованием значения «по умолчанию» в конструкторе параметров приложения? Будет ли значение по умолчанию отменять любые настройки, которые были сохранены между запуском приложения? (или вам НЕ нужно использовать значение по умолчанию)

Грег
источник
@ Все новые пользователи. Желательно, чтобы вы могли задавать отдельные вопросы вместо того, чтобы объединять свои вопросы в один. Таким образом, это поможет людям, отвечающим на ваш вопрос, а также другим, ищущим хотя бы один из ваших вопросов. Благодаря!
Hille

Ответы:

81

Для этого вы можете использовать настройки приложения , использование базы данных - не лучший вариант, учитывая время, затрачиваемое на чтение и запись настроек (особенно если вы используете веб-службы).

Вот несколько ссылок, которые объясняют, как этого добиться и использовать их в WPF:

Настройки пользователя в WPF

Быстрый совет по WPF: как выполнить привязку к ресурсам и настройкам приложения WPF?

Настраиваемое окно для WPF

Акджоши
источник
23

Обновление : в настоящее время я бы использовал JSON.

Я также предпочитаю использовать сериализацию в файл. Файлы XML удовлетворяют практически всем требованиям. Вы можете использовать эту ApplicationSettingsсборку, но у них есть некоторые ограничения и определенное, но (для меня) очень странное поведение, где они хранятся. Я их много использовал, и они работают. Но если вы хотите полностью контролировать, как и где они хранятся, я использую другой подход.

  1. Сделайте где-нибудь класс со всеми своими настройками. Я назвал этоMySettings
  2. Реализуйте сохранение и чтение для сохранения
  3. Используйте их в своем коде приложения

Преимущества:

  • Очень простой подход.
  • Один класс для настроек. Загрузить. Сохранить.
  • Все ваши настройки безопасны по типу.
  • Вы можете упростить или расширить логику в соответствии с вашими потребностями (управление версиями, множество профилей на пользователя и т. Д.)
  • Он работает очень хорошо в любом случае (База данных, WinForms, WPF, Сервис и т. Д.)
  • Вы можете определить, где хранить файлы XML.
  • Вы можете найти их и управлять ими с помощью кода или вручную.
  • Он работает для любого метода развертывания, который я могу себе представить.

Недостатки: - Вы должны думать, где хранить файлы настроек. (Но вы можете просто использовать свою установочную папку)

Вот простой пример (не проверенный) -

public class MySettings
{
    public string Setting1 { get; set; }
    public List<string> Setting2 { get; set; }

    public void Save(string filename)
    {
        using (StreamWriter sw = new StreamWriter(filename))
        {
            XmlSerializer xmls = new XmlSerializer(typeof(MySettings));
            xmls.Serialize(sw, this);
        }
    }
    public MySettings Read(string filename)
    {
        using (StreamReader sw = new StreamReader(filename))
        {
            XmlSerializer xmls = new XmlSerializer(typeof(MySettings));
            return xmls.Deserialize(sw) as MySettings;
        }
    }
}

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

public class MyApplicationLogic
{
    public const string UserSettingsFilename = "settings.xml";
    public string _DefaultSettingspath = 
        Assembly.GetEntryAssembly().Location + 
        "\\Settings\\" + UserSettingsFilename;

    public string _UserSettingsPath = 
        Assembly.GetEntryAssembly().Location + 
        "\\Settings\\UserSettings\\" + 
        UserSettingsFilename;

    public MyApplicationLogic()
    {
        // if default settings exist
        if (File.Exists(_UserSettingsPath))
            this.Settings = Settings.Read(_UserSettingsPath);
        else
            this.Settings = Settings.Read(_DefaultSettingspath);
    }
    public MySettings Settings { get; private set; }

    public void SaveUserSettings()
    {
        Settings.Save(_UserSettingsPath);
    }
}

может быть, кого-то вдохновит такой подход. Я так делаю уже много лет, и меня это вполне устраивает.

Мат
источник
1
Недостатком будет то, что у вас больше нет конструктора настроек, поэтому он будет немного менее удобным для пользователя, когда оба будут работать.
Phil 1970,
3
Полностью согласен с "очень странным поведением там, где они хранятся", я использую ваш подход именно из-за этого. +1.
Hannish
Если у вас есть НОВЫЙ вопрос, задайте его, нажав кнопку « Задать вопрос» .
Мат,
12

Вы можете сохранить информацию о своих настройках в Stringsформате XML в формате Settings.Default. Создайте несколько классов для хранения данных конфигурации и убедитесь, что они есть [Serializable]. Затем с помощью следующих помощников вы можете сериализовать экземпляры этих объектов - или List<T>(или массивы T[]и т. Д.) Из них - в String. Сохраните каждую из этих различных строк в соответствующем Settings.Defaultслоте в вашем приложении WPF Settings.

Чтобы восстановить объекты при следующем запуске приложения, прочитайте Settingsинтересующую строку и Deserializeожидаемый тип T(который на этот раз должен быть явно указан в качестве аргумента типа Deserialize<T>).

public static String Serialize<T>(T t)
{
    using (StringWriter sw = new StringWriter())
    using (XmlWriter xw = XmlWriter.Create(sw))
    {
        new XmlSerializer(typeof(T)).Serialize(xw, t);
        return sw.GetStringBuilder().ToString();
    }
}

public static T Deserialize<T>(String s_xml)
{
    using (XmlReader xw = XmlReader.Create(new StringReader(s_xml)))
        return (T)new XmlSerializer(typeof(T)).Deserialize(xw);
}
Гленн Слейден
источник
6

Наиболее распространенный и наиболее типичный подход к этому вопросу: изолированное хранилище.

Сериализуйте состояние элемента управления в XML или другом формате (особенно легко, если вы сохраняете свойства зависимостей с помощью WPF), а затем сохраните файл в изолированном хранилище пользователя.

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

class SettingsManager
{
    public static void LoadSettings(FrameworkElement sender, Dictionary<FrameworkElement, DependencyProperty> savedElements)
    {
        EnsureProperties(sender, savedElements);
        foreach (FrameworkElement element in savedElements.Keys)
        {
            try
            {
                element.SetValue(savedElements[element], Properties.Settings.Default[sender.Name + "." + element.Name]);
            }
            catch (Exception ex) { }
        }
    }

    public static void SaveSettings(FrameworkElement sender, Dictionary<FrameworkElement, DependencyProperty> savedElements)
    {
        EnsureProperties(sender, savedElements);
        foreach (FrameworkElement element in savedElements.Keys)
        {
            Properties.Settings.Default[sender.Name + "." + element.Name] = element.GetValue(savedElements[element]);
        }
        Properties.Settings.Default.Save();
    }

    public static void EnsureProperties(FrameworkElement sender, Dictionary<FrameworkElement, DependencyProperty> savedElements)
    {
        foreach (FrameworkElement element in savedElements.Keys)
        {
            bool hasProperty =
                Properties.Settings.Default.Properties[sender.Name + "." + element.Name] != null;

            if (!hasProperty)
            {
                SettingsAttributeDictionary attributes = new SettingsAttributeDictionary();
                UserScopedSettingAttribute attribute = new UserScopedSettingAttribute();
                attributes.Add(attribute.GetType(), attribute);

                SettingsProperty property = new SettingsProperty(sender.Name + "." + element.Name,
                    savedElements[element].DefaultMetadata.DefaultValue.GetType(), Properties.Settings.Default.Providers["LocalFileSettingsProvider"], false, null, SettingsSerializeAs.String, attributes, true, true);
                Properties.Settings.Default.Properties.Add(property);
            }
        }
        Properties.Settings.Default.Reload();
    }
}

.....и....

  Dictionary<FrameworkElement, DependencyProperty> savedElements = new Dictionary<FrameworkElement, DependencyProperty>();

public Window_Load(object sender, EventArgs e) {
           savedElements.Add(firstNameText, TextBox.TextProperty);
                savedElements.Add(lastNameText, TextBox.TextProperty);

            SettingsManager.LoadSettings(this, savedElements);
}

private void Window_Closing(object sender, System.ComponentModel.CancelEventArgs e)
        {
            SettingsManager.SaveSettings(this, savedElements);
        }
Джефф
источник
5

Помимо базы данных, вы также можете иметь следующие параметры для сохранения настроек, связанных с пользователем

  1. реестр под HKEY_CURRENT_USER

  2. в файле в AppDataпапке

  3. используя Settingsфайл в WPF и установив его область действия как Пользователь

ajay_whiz
источник
2
Предложение 1 - причина, по которой приложения замедляют работу Windows, лучше не заполнять ключи реестра чем-то, что лучше сделать в файле IMO.
Console
1
@Console, запись файла на диск, замедление (износ) SSD, запись данных в базу данных, база данных замедлений. Какой у вас тогда вариант? Реестр Windows предназначен для использования как одно из мест для сохранения настроек .
Sinatr
1
Вы правы, я думаю, важно упомянуть, что реестр имеет некоторые недостатки, если каждое приложение сохраняет там сотни пользовательских настроек.
Console
@Sinatr Изначально реестр предназначался для этой цели ... но он не предназначался для обработки большого количества данных, поэтому в какой-то момент истории Microsoft рекомендовала прекратить его использование. Насколько мне известно, Windows загружает весь реестр при входе в систему, а также делает копию либо для роуминга, либо для загрузки последней удачной конфигурации после серьезного сбоя. Таким образом, использование реестра влияет на систему, даже если приложение никогда не используется.
Phil1970 02
Кроме того, в реестре есть ограничение на размер, и если он все еще использовался приложением, возможно, этот предел будет превышен на большинстве компьютеров. Реестр был разработан в то время, когда в системе было меньше памяти в мегабайтах, чем сегодня на компьютере в гигабайтах. Реестр не был рассчитан на такой большой размер, и поэтому, несмотря на увеличение ограничений, он не оптимизирован для наших текущих нужд.
Phil1970 02
3

По моему опыту, лучшим решением является сохранение всех настроек в таблице базы данных. Даже не беспокойтесь о производительности. Современные базы данных работают быстро и легко могут хранить тысячи столбцов в таблице. Я усвоил это на собственном горьком опыте - до того, как я начал сериализацию / десериализацию - кошмар. Хранение его в локальном файле или реестре имеет одну большую проблему - если вам нужно поддерживать свое приложение, а компьютер выключен - пользователь не находится перед ним - вы ничего не можете сделать .... если настройки находятся в БД - вы можете поменял их и альт, не говоря уже о том, что можно сравнить настройки ....

Адам
источник
И их удаленное хранение также имеет одну большую проблему, когда соединение недоступно ... Многие приложения, написанные для работы в сети, имеют далеко не идеальный опыт при работе в автономном режиме или иногда даже имеют ошибки, из-за которых некоторые функции не работают в автономном режиме, даже если это должно быть не иметь никакого влияния, кроме "слежки" за использованием устройства.
Phil 1970,
1

Обычно я делаю такие вещи, определяя собственный Serializableкласс настроек [ ] и просто сериализуя его на диск. В вашем случае вы могли бы так же легко сохранить его как строковый blob в своей базе данных SQLite.

Дуби
источник
0
  1. Во всех местах, где я работал, база данных была обязательной из-за поддержки приложений. Как сказал Адам, пользователь может не находиться за своим столом, или машина может быть выключена, или вы можете быстро изменить чью-то конфигурацию или назначить новому участнику конфигурацию по умолчанию (или конфигурацию члена команды).

  2. Если параметры, вероятно, будут расти по мере выпуска новых версий приложения, вы можете захотеть сохранить данные в виде больших двоичных объектов, которые затем могут быть десериализованы приложением. Это особенно полезно, если вы используете что-то вроде Prism, которое обнаруживает модули, поскольку вы не можете знать, какие настройки вернет модуль. Для больших двоичных объектов можно использовать составной ключ имени пользователя и компьютера. Таким образом, у вас могут быть разные настройки для каждой машины.

  3. Я не особо использовал встроенный класс настроек, поэтому воздержусь от комментариев. :)

Will_Piuvans
источник
0

Я хотел использовать файл управления xml на основе класса для моего настольного приложения WPF VB.net. Приведенный выше код, позволяющий сделать все это в одном, превосходен и указал мне правильное направление. Если кто-то ищет решение VB.net, вот созданный мной класс:

Imports System.IO
Imports System.Xml.Serialization

Public Class XControl

Private _person_ID As Integer
Private _person_UID As Guid

'load from file
Public Function XCRead(filename As String) As XControl
    Using sr As StreamReader = New StreamReader(filename)
        Dim xmls As New XmlSerializer(GetType(XControl))
        Return CType(xmls.Deserialize(sr), XControl)
    End Using
End Function

'save to file
Public Sub XCSave(filename As String)
    Using sw As StreamWriter = New StreamWriter(filename)
        Dim xmls As New XmlSerializer(GetType(XControl))
        xmls.Serialize(sw, Me)
    End Using
End Sub

'all the get/set is below here

Public Property Person_ID() As Integer
    Get
        Return _person_ID
    End Get
    Set(value As Integer)
        _person_ID = value
    End Set
End Property

Public Property Person_UID As Guid
    Get
        Return _person_UID
    End Get
    Set(value As Guid)
        _person_UID = value
    End Set
End Property

End Class
Скотт
источник