c # - підхід до збереження налаштувань користувача у програмі WPF?


84

Який підхід ви рекомендуєте для постійних налаштувань користувача у програмі WPF для Windows (на робочому столі)? Зауважте, що ідея полягає в тому, що користувач може змінити свої налаштування під час запуску, а потім закрити програму, тоді при запуску програми пізніше програма використовуватиме поточні налаштування. Тоді це буде виглядати так, ніби налаштування програми не змінюються.

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

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

Q3 - Чи можна для цього використовувати налаштування програми? Якщо так, чи є якісь спеціальні завдання, необхідні для забезпечення постійності тут? Також що могло б статися з використанням значення "за замовчуванням" у конструкторі налаштувань програми в цьому випадку? Чи замінить за замовчуванням будь-які налаштування, збережені між запуском програми? (або вам НЕ потрібно використовувати значення за замовчуванням)


@ Всі нові користувачі Переважно, якщо ви можете розміщувати окремі запитання замість того, щоб поєднувати свої запитання в одне. Таким чином, це допомагає людям, які відповідають на ваше запитання, а також іншим, хто шукає принаймні одне з ваших запитань. Дякую!
Хілле

Відповіді:


80

Ви можете використовувати для цього Налаштування додатків, оскільки використання бази даних - не найкращий варіант, враховуючи час, який витрачається на читання та запис параметрів (особливо, якщо ви використовуєте веб-сервіси).

Ось кілька посилань, які пояснюють, як цього досягти та використовувати їх у WPF -

Налаштування користувача у WPF

Швидка порада WPF: Як прив’язати до ресурсів та налаштувань програми WPF?

Настроюване вікно для WPF


22

Оновлення : Сьогодні я б використовував JSON.

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

  1. Зробіть клас десь із усіма налаштуваннями. Я назвав йогоMySettings
  2. Впроваджуйте Save and Read для наполегливості
  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
Недоліком може бути те, що у вас більше немає дизайнера налаштувань, тому це трохи менш зручно для користувачів, коли обидва будуть працювати.
Phil1970

3
Повністю згоден з "дуже дивною поведінкою там, де вони зберігаються", я використовую ваш підхід саме через це. +1.
Ганніш,

Якщо у вас є НОВЕ запитання, будь ласка, задайте його, натиснувши кнопку Задати питання .
Mat

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), а потім збережіть файл в ізольованому сховищі користувача.

Якщо ви хочете пройти маршрут налаштування програми, я спробував щось подібне в один момент ... хоча наступний підхід можна легко адаптувати для використання Isolated Storage:

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 та встановлення його обсягу як Користувача


2
Пропозиція 1 - причина, чому Програми уповільнюють роботу Windows, краще не заповнювати ключі реєстру чимось, що краще зробити у файлі IMO.
Консоль

1
@Console, запис файлу на диск сповільнюється (зношується) SSD, запис даних у базу даних повільно падає. Який тоді ваш варіант? Реєстр Windows призначений для використання як одного з місць збереження налаштувань .
Сінатр

1
Ви маєте рацію, це так, я вважаю важливим згадати, що реєстр має деякі недоліки, якщо кожна програма зберігає там переваги більш жорстких користувачів.
Консоль

@Sinatr Спочатку реєстр був призначений для цієї мети ... але він не був розроблений для обробки великої кількості даних, тому в якийсь момент історії Microsoft рекомендувала припинити його використання. Наскільки мені відомо, Windows завантажує весь реєстр під час входу, а також робить копію або для роумінгу, або для того, щоб мати змогу завантажити останню відому хорошу конфігурацію після великої аварії. Таким чином, використання реєстру впливає на систему, навіть якщо програма ніколи не використовується.
Phil1970

Також реєстр має обмеження розміру, і якщо він все ще використовувався додатком, ймовірно, цей ліміт буде перевищений на більшості комп'ютерів. Реєстр був розроблений у той час, коли система має менше пам'яті в МБ, ніж комп'ютер сьогодні в ГБ. Реєстр не був розроблений таким великим, і, незважаючи на те, що обмеження зросли, він не оптимізований для наших поточних потреб.
Phil1970

3

На моєму досвіді найкращим рішенням є зберігання всіх налаштувань у таблиці бази даних. Навіть не турбуйтеся про продуктивність. Сучасні бази даних швидкі і можуть легко зберігати тисячі стовпців у таблиці. Я дізнався про це важким шляхом - до того, як здійснити серилізацію / десеріалізацію - кошмар. Зберігання його в локальному файлі або реєстрі має одну велику проблему - якщо вам потрібно підтримувати програму, а комп’ютер вимкнено - користувач не знаходиться перед ним - ви нічого не можете зробити .... змінив їх і альт, не кажучи вже про те, що ви можете порівняти налаштування ....


І їх віддалене зберігання також має одну велику проблему, коли підключення недоступне ... Багато додатків, написаних для роботи в Інтернеті, мають менш ніж ідеальний досвід роботи в автономному режимі або іноді навіть мають помилки, через які деякі функції не працюють в автономному режимі, навіть якщо це потрібно не мати жодного впливу, крім "шпигунства", як використовується пристрій.
Phil1970

1

Я зазвичай роблю подібні дії, визначаючи власний Serializableклас налаштувань [ ] і просто серіалізуючи його на диск. У вашому випадку ви можете з легкістю зберегти його як рядок у вашій базі даних SQLite.


0
  1. У всіх місцях, де я працював, база даних була обов’язковою через підтримку додатків. Як сказав Адам, користувач може бути не за своїм столом, або машина може бути вимкнена, або ви можете швидко змінити чиюсь конфігурацію або призначити новому столяру конфігурацію за замовчуванням (або конфігурацію члена команди).

  2. Якщо параметри, ймовірно, зростатимуть із випуском нових версій програми, можливо, ви захочете зберегти дані у вигляді великих крапок, які потім програма може десериалізувати. Це особливо корисно, якщо ви використовуєте щось на зразок Prism, яка виявляє модулі, оскільки ви не можете знати, які налаштування модуль поверне. Краплі можна ввести за допомогою комбінованого ключа користувача / машини. Таким чином ви можете мати різні налаштування для кожної машини.

  3. Я не надто використовував вбудований клас "Налаштування", тому утримаюся від коментарів. :)


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
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.