Эквивалент UserSettings/ApplicationSettings в WPF .NET 5, .NET 6 или .Net Core

Каков предпочтительный способ сохранения пользовательских настроек для приложений WPF с .NET 5, .NET 6 или .Net Core >=3.0?

Создан проект WPF .Net Core 3.0 (VS2019 V16.3.1) Теперь я вижу, что раздела Properties.Settings больше нет.

Эквивалент UserSettings/ApplicationSettings в WPF .NET 5, .NET 6 или .Net Core

После поиска в Интернете начал погружаться в Microsoft.Extensions.Configuration.

Помимо раздутого кода для доступа к настройкам, теперь еще хуже -> Нет сохранения?
Параметры конфигурации пользователя в .NET Core

Fortunately or unfortunately the Microsoft.Extensions.Configuration does not support saving by design. Read more in this Github issue Why there is no save in ConfigurationProvider?


What is the prefered (and easy/fast/simple) way for persisting user settings for WPF applications with .Net Core >=3.0?
Before `
  • добавьте переменные в свойства. Эквивалент UserSettings/ApplicationSettings в WPF .NET 5, .NET 6 или .Net Core

  • Читать переменные при запуске
    var culture = new CultureInfo(Properties.Settings.Default.LanguageSettings);

  • когда переменная изменяется -> немедленно сохранить ее
    Properties.Settings.Default.LanguageSettings = selected.TwoLetterISOLanguageName; Properties.Settings.Default.Save();

Мне любопытно, что заставило вас изменить файлы конфигурации во время выполнения? Чем вызваны эти изменения? Вы пытаетесь использовать свою конфигурацию как некий профиль пользователя?

Nathan Cooper 02.07.2019 14:24

Я говорю о настройках для повышения удобства пользователя. В моем примере выше это похоже на настройки приложения, но мне нравится позволять пользователям выбирать свой язык. И почему я должен заставлять своих пользователей редактировать файл конфигурации? Почему бы не предоставить административную область для настройки? Почему бы не сделать это как Практическое руководство. Запись пользовательских настроек во время выполнения с помощью C# Я успешно использую/сохраняю эти пользовательские настройки около 15 лет. Должны ли мы вернуться к реестру? Вернуться к истокам?

MarkusEgle 02.07.2019 15:31

У меня сейчас нет "ответа". Но я ожидаю, что будет какая-то абстракция конфигурации, которая будет писать либо в LocalApplicationData, либо в ApplicationData, либо в CommonApplicationData, в зависимости от области пользовательских настроек. Я ожидаю, что эта абстракция запретит вам изменять конфигурацию вашего приложения на уровне приложения (по многим причинам, в том числе из-за того, что ваши уровни разрешений, вероятно, будут неадекватными). Это то, что я ожидал от кода конфигурации Microsoft для клиентских приложений. Документация, на которую вы ссылаетесь, - это документация asp.net, которая совершенно отличается.

Nathan Cooper 02.07.2019 18:04

Я предположил, что Microsoft.Extensions.Configuration подходит даже для WPF/WinForms <-> .Net Core, а не только для ASP.Net. Другой информации пока не нашел.

MarkusEgle 02.07.2019 19:45

Один ссылка, где я прочитал, что следует использовать Microsoft.Extensions.Configuration: stackoverflow.com/a/48866609/3090544

MarkusEgle 03.07.2019 11:22

Аааа, возможно, я ошибся, когда сказал, что это был asp.net только тогда. Надеюсь ответит знающий. Тем временем я бы решил, что это может быть просто последовательный файл в LocalApplicationData и просто игнорирование материала конфигурации Microsoft.

Nathan Cooper 03.07.2019 11:41

У меня были подобные расстройства. В конце концов я просто пошел с JSON.net. Супер просто

GazTheDestroyer 09.07.2019 11:42

Вы всегда можете использовать простой текстовый файл (JSON, XML, обычный ключ=значение;ключ=значение или форматирование ini-файла, если вам нужны разделы) или облегченную базу данных, такую ​​как SQLite. Вы также можете создавать простые объекты данных и сериализовать их. Настройка этого (может быть кроме SQLite и сериализации) делается очень быстро. XML и JSON (через API) могут изменять и искать данные без явного разбора. XML обеспечивает простой и легкий обход (например, LINQ to XML).

BionicCode 12.07.2019 21:50

Итак, мы вернулись к тому, чтобы «просто создать собственное (дерьмовое) решение» для самых простых вещей. Я думал, что мы оставили это позади около 20 лет назад.

user3700562 23.03.2021 19:38

Если я сверну свой собственный файл JSON, где я должен его сохранить?

Arrow_Raider 26.07.2021 16:23
Стоит ли изучать PHP в 2026-2027 годах?
Стоит ли изучать PHP в 2026-2027 годах?
Привет всем, сегодня я хочу высказать свои соображения по поводу вопроса, который я уже много раз получал в своем сообществе: "Стоит ли изучать PHP в...
Поведение ключевого слова "this" в стрелочной функции в сравнении с нормальной функцией
Поведение ключевого слова "this" в стрелочной функции в сравнении с нормальной функцией
В JavaScript одним из самых запутанных понятий является поведение ключевого слова "this" в стрелочной и обычной функциях.
Приемы CSS-макетирования - floats и Flexbox
Приемы CSS-макетирования - floats и Flexbox
Здравствуйте, друзья-студенты! Готовы совершенствовать свои навыки веб-дизайна? Сегодня в нашем путешествии мы рассмотрим приемы CSS-верстки - в...
Тестирование функциональных ngrx-эффектов в Angular 16 с помощью Jest
В системе управления состояниями ngrx, совместимой с Angular 16, появились функциональные эффекты. Это здорово и делает код определенно легче для...
Концепция локализации и ее применение в приложениях React ⚡️
Концепция локализации и ее применение в приложениях React ⚡️
Локализация - это процесс адаптации приложения к различным языкам и культурным требованиям. Это позволяет пользователям получить опыт, соответствующий...
Пользовательский скаляр GraphQL
Пользовательский скаляр GraphQL
Листовые узлы системы типов GraphQL называются скалярами. Достигнув скалярного типа, невозможно спуститься дальше по иерархии типов. Скалярный тип...
79
10
26 006
7
Перейти к ответу Данный вопрос помечен как решенный

Ответы 7

Как указано в сообщениях, на которые вы ссылались, API Microsoft.Extensions.Configuration предназначен для однократной настройки для вашего приложения или, по крайней мере, для чтения. Если ваша основная цель — сохранить пользовательские настройки легко/быстро/просто, вы можете что-то свернуть самостоятельно. Хранение настроек в папке ApplicationData, аналогично старому API.

public class SettingsManager<T> where T : class
{
    private readonly string _filePath;

    public SettingsManager(string fileName)
    {
        _filePath = GetLocalFilePath(fileName);
    }

    private string GetLocalFilePath(string fileName)
    {
        string appData = Environment.GetFolderPath(Environment.SpecialFolder.ApplicationData);
        return Path.Combine(appData, fileName);
    }

    public T LoadSettings() =>
        File.Exists(_filePath) ?
        JsonConvert.DeserializeObject<T>(File.ReadAllText(_filePath)) :
        null;

    public void SaveSettings(T settings)
    {
        string json = JsonConvert.SerializeObject(settings);
        File.WriteAllText(_filePath, json);
    }
}

Демонстрация с использованием самых основных UserSettings

public class UserSettings
{
    public string Name { get; set; }
}

Я не буду приводить полный пример MVVM, тем не менее у нас будет экземпляр в памяти, ссылка _userSettings. Как только вы загрузите настройки, свойства демо по умолчанию будут переопределены. В производственной среде, конечно, вы не будете указывать значения по умолчанию при запуске. Это просто для иллюстрации.

public partial class MainWindow : Window
{
    private readonly SettingsManager<UserSettings> _settingsManager;
    private UserSettings _userSettings;

    public MainWindow()
    {
        InitializeComponent();

        _userSettings = new UserSettings() { Name = "Funk" };
        _settingsManager = new SettingsManager<UserSettings>("UserSettings.json");
    }

    private void Button_FromMemory(object sender, RoutedEventArgs e)
    {
        Apply(_userSettings);
    }

    private void Button_LoadSettings(object sender, RoutedEventArgs e)
    {
        _userSettings = _settingsManager.LoadSettings();
        Apply(_userSettings);
    }

    private void Button_SaveSettings(object sender, RoutedEventArgs e)
    {
        _userSettings.Name = textBox.Text;
        _settingsManager.SaveSettings(_userSettings);
    }

    private void Apply(UserSettings userSettings)
    {
        textBox.Text = userSettings?.Name ?? "No settings found";
    }
}

XAML

<Window x:Class = "WpfApp.MainWindow"
        xmlns = "http://schemas.microsoft.com/winfx/2006/xaml/presentation"
        xmlns:x = "http://schemas.microsoft.com/winfx/2006/xaml"
        xmlns:d = "http://schemas.microsoft.com/expression/blend/2008"
        xmlns:mc = "http://schemas.openxmlformats.org/markup-compatibility/2006"
        xmlns:local = "clr-namespace:WpfApp"
        mc:Ignorable = "d"
        Title = "MainWindow" Height = "450" Width = "800">
    <Window.Resources>
        <Style TargetType = "Button">
            <Setter Property = "Margin" Value = "10"/>
        </Style> 
    </Window.Resources>
    <Grid Margin = "10">
        <Grid.RowDefinitions>
            <RowDefinition/>
            <RowDefinition Height = "Auto"/>
            <RowDefinition Height = "Auto"/>
            <RowDefinition Height = "Auto"/>
        </Grid.RowDefinitions>
        <TextBox Grid.Row = "0" x:Name = "textBox" Width = "150" HorizontalAlignment = "Center" VerticalAlignment = "Center"/>
        <Button Grid.Row = "1" Click = "Button_FromMemory">From Memory</Button>
        <Button Grid.Row = "2" Click = "Button_LoadSettings">Load Settings</Button>
        <Button Grid.Row = "3" Click = "Button_SaveSettings">Save Settings</Button>
    </Grid>
</Window>

Предложите переключиться на System.Text.Json, а не на newtonsoft

Sam Mackrill 07.02.2020 11:34

Это лучшее решение! Использование «старого» подхода к параметрам в сочетании с MSIX приводит к перезаписыванию параметров приложения при каждом автоматическом обновлении.

Mr. Muh 08.02.2020 22:32

API Property.Settings имел метод сохранения. это было не только для чтения. Было здорово и очень просто хранить и загружать пользовательские настройки в локальные данные приложения, где такие настройки начинаются. Это было ТИПИЧНО БЕЗОПАСНО, не полагаясь на дрянные строковые вещи, такие как настройки ["LeftHotkey"].

Blechdose 23.05.2020 20:12

@Blechdose Вы хоть читали OP, мы говорили о Microsoft.Extensions.Configuration, у которого нет метода сохранения. Предлагаемое мной решение является безопасным по типу и сохраняет настройки в локальных данных приложения или в любом другом месте.

Funk 28.05.2020 08:09

Сообщение @Funk OP посвящено Property.Settings, которых ему не хватает в .NET Core, и вы сказали «API конфигурации», который затем ссылается на Property.Settings. Хотя вы правы, я пропустил часть «сообщения, на которые вы ссылаетесь» (вы имеете в виду ссылки?). Но это все еще сбивает с толку. Не могли бы вы изменить его с «API конфигурации» на «Microsoft.Extensions.Configuration»?

Blechdose 28.05.2020 08:42

Как, черт возьми, этот прогресс? Переход от хорошо работающей и зарекомендовавшей себя системы хранения пользовательских настроек, простой в использовании и интегрированной в IDE, к «да ладно, просто приготовьте что-то свое, сохраните его в txt-файле где-нибудь или что-то в этом роде». Весь этот проект ядра .net представляет собой такой огромный шаг назад по сравнению с .NET Framework, что это действительно ошеломляет.

user3700562 23.03.2021 19:32

Вы можете использовать пакет Nuget System.Configuration.ConfigurationManager. Он совместим с .Net Standard 2.0, поэтому его можно использовать в приложении .Net Core.

Для этого нет дизайнера, но в остальном он работает так же, как и версия .Net, и вы сможете просто скопировать код из своего Settings.Designer.cs. Кроме того, вы можете переопределить OnPropertyChanged, поэтому не нужно вызывать Save.

Вот пример из рабочего проекта .Net Standard:

public class WatchConfig: ApplicationSettingsBase
{
    static WatchConfig _defaultInstance = (WatchConfig)Synchronized(new WatchConfig());

    public static WatchConfig Default { get => _defaultInstance; }

    protected override void OnPropertyChanged(object sender, PropertyChangedEventArgs e)
    {
        Save();
        base.OnPropertyChanged(sender, e);
    }

    [UserScopedSetting]
    [global::System.Configuration.DefaultSettingValueAttribute(
    @"<?xml    version = ""1.0"" encoding = ""utf-16""?>
    <ArrayOfString>
      <string>C:\temp</string>
     <string>..\otherdir</string>
     </ArrayOfString>")]
    public StringCollection Directories
    {
        get { return (StringCollection)this[nameof(Directories)]; }
        set { this[nameof(Directories)] = value; }
    }
}

Для меня это актуальная простейшая замена отсутствующим пользовательским настройкам. Он сохранит схему сохранения в ApplicationData, как это было до того, как вам не нужно было создавать app.config. Успешно протестировано с VS 2019 Preview 16.2.0 Preview 3.0 и .Net Core SDK 3.0.0-preview6-27804 и пакетом Nuget System.Configuration.ConfigurationManager 4.6.0-preview6.199303.8

MarkusEgle 16.07.2019 09:06

Просто добавьте этот «класс пользовательских настроек» в свой проект, где вы должны добавить переменные настроек в качестве свойств с некоторыми аннотациями, где вы можете посмотреть, как это должно выглядеть в других старых Settings.Designer.cs.

MarkusEgle 16.07.2019 09:17

Просто дважды щелкните файл Settings.settings в вашем проекте. Он по-прежнему будет открываться в дизайнере, как и раньше. Просто его больше нет в списке свойств.

И где мне найти этот файл настроек? В решении для приложения WPF .Net Core такого файла нет, даже не на уровне папки. Только что снова проверил с недавно созданным проектом с VS2019 V16.3.3

MarkusEgle 11.10.2019 12:11

«Файл настроек» находится в списке типов файлов, когда вы щелкаете проект правой кнопкой мыши и выбираете Добавить новый элемент. Однако этот файл настроек доступен только для чтения, поскольку в нем нет метода Сохранять(). (Вы также не можете вызвать Обновление() или Перезагрузить(), поэтому вы не можете переместить настройки из старой версии в новую.)

skst 19.10.2019 02:16
Ответ принят как подходящий

Вы можете добавить тот же старый добрый файл настроек, например. щелкните правой кнопкой мыши Свойства -> Добавить -> Новый элемент и найдите «Настройки». Файл можно редактировать в дизайнере настроек и использовать, как и в проектах .net framework ранее (работает ConfigurationManager, Settings.Default.Upgrade(), Settings.Default.Save и т.д.).

Добавьте также файл app.config в корневую папку проекта (так же через Add -> New Item), еще раз сохраните настройки, скомпилируйте проект и в выходной папке вы найдете файл .dll.config. Теперь вы можете изменить значения приложения по умолчанию, как и раньше.

Протестировано с Visual Studio 1.16.3.5 и проектом .net core 3.0 WPF.

Может быть так просто! Это должен быть ответ с щедростью... Протестировано с VS2019 V16.3.6

MarkusEgle 25.10.2019 10:18

И где править <exe-файл>.config при развертывании? Или они переименованы/перемещены? До настройки .Net Core наш конфиг редактировался в той же директории, что и exe-файл.

MarkusEgle 25.10.2019 10:59

Теперь он заменен файлом <executable..dll.config>. Могу я получить награду, пожалуйста?

Alexander Zwitbaum 07.11.2019 23:22

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

MarkusEgle 09.11.2019 17:31

В моем проекте WPF Core 3.1 обозреватель решений выглядит как в вопросе, а не как в ответе. Свойства отсутствуют, то есть я также не могу щелкнуть их правой кнопкой мыши.

Peter Huber 07.01.2020 09:20

Вам также необходимо установить пакет NuGet System.Configuration.ConfigurationManager

A_Binary_Story 18.01.2020 10:50

@PeterHuber, если вы не можете найти папку «Свойства», просто создайте их, а затем добавьте в нее файл .settings.

VT Chiew 03.02.2020 03:44

Работает идеально. Но я получаю много сообщений IntelliSense в списке ошибок. «Не удалось найти информацию о схеме для элемента «userSetting»», «Не удалось найти информацию о схеме для элемента «setting»» и т. д.

Quergo 26.02.2020 16:06

Последующие добавления в файл настроек в пользовательском интерфейсе с последующим сохранением приводят к диалоговому окну с ошибкой: Произошла ошибка при сохранении значений в файле app.config. Файл может быть поврежден или содержать недопустимый XML. Я использую .net core 3.0 в консольном приложении.

PBMe_HikeIt 10.04.2020 19:47

После добавления app.config, как мне сохранить ..? Кроме того, app.settings больше не поддерживает тег usersettings. Как данные из настроек попадают в файл app.config.

Rye bread 01.12.2020 10:51

Спасибо. Автоматически создает <myproject>.dll.config из App.Config, который можно изменять извне. К сожалению, это не удается при создании одного исполняемого файла. Какие-либо предложения?

TomB 22.12.2020 15:41

@PeterHuber мой проект изначально не включал папку «Свойства». мне пришлось создать профиль публикации, который создал папку свойств с профилем публикации под ней. затем были добавлены настройки, как было предложено в ответе.

Aaron. S 23.01.2021 17:23

Для Wpf Net.Core

Проект щелкните правой кнопкой мыши -> Добавить новый элемент -> Файл настроек (Общие)

Использовать

Settings1.Default.Height = this.Height;
Settings1.Default.Width = this.Width;

this.Height = Settings1.Default.Height;
this.Width = Settings1.Default.Width;

Settings1.Default.Save();

Где «Settings1» создал имя файла

ПРИМЕР

Дважды щелкните файл «Настройки1.Настройки» и выберите «Редактировать».

private void MainWindowRoot_SourceInitialized(object sender, EventArgs e)
{
    this.Top = Settings1.Default.Top;
    this.Left = Settings1.Default.Left;
    this.Height = Settings1.Default.Height;
    this.Width = Settings1.Default.Width;
    // Very quick and dirty - but it does the job
    if (Settings1.Default.Maximized)
    {
        WindowState = WindowState.Maximized;
    }
}

private void MainWindowRoot_Closing(object sender, System.ComponentModel.CancelEventArgs e)
{
    if (WindowState == WindowState.Maximized)
    {
        // Use the RestoreBounds as the current values will be 0, 0 and the size of the screen
        Settings1.Default.Top = RestoreBounds.Top;
        Settings1.Default.Left = RestoreBounds.Left;
        Settings1.Default.Height = RestoreBounds.Height;
        Settings1.Default.Width = RestoreBounds.Width;
        Settings1.Default.Maximized = true;
    }
    else
    {
        Settings1.Default.Top = this.Top;
        Settings1.Default.Left = this.Left;
        Settings1.Default.Height = this.Height;
        Settings1.Default.Width = this.Width;
        Settings1.Default.Maximized = false;
    }

    Settings1.Default.Save();
}

интересно, это работает в WPF (Net Core 3.1), но не в консольном приложении (Net Core 3.1). Версия консольного приложения просто не имеет метода .Save()

Blechdose 29.05.2020 14:16

На основе отвечать Funk здесь представлен абстрактный общий вариант в стиле singleton, который удаляет часть администрирования вокруг SettingsManager и максимально упрощает создание дополнительных классов настроек и их использование:

Класс типизированных настроек:

//Use System.Text.Json attributes to control serialization and defaults
public class MySettings : SettingsManager<MySettings>
{
    public bool SomeBoolean { get; set; }
    public string MyText { get; set; }
}

Применение:

//Loading and reading values
MySettings.Load();
var theText = MySettings.Instance.MyText;
var theBool = MySettings.Instance.SomeBoolean;

//Updating values
MySettings.Instance.MyText = "SomeNewText"
MySettings.Save();

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

Базовый класс определяет, где хранятся настройки, и позволяет использовать только один файл настроек для каждого подкласса MySettings — его расположение определяют имена сборки и класса. Для замены файла свойств этого достаточно.

using System;
using System.IO;
using System.Linq;
using System.Reflection;

public abstract class SettingsManager<T> where T : SettingsManager<T>, new()
{
    private static readonly string filePath = GetLocalFilePath($"{typeof(T).Name}.json");

    public static T Instance { get; private set; }

    private static string GetLocalFilePath(string fileName)
    {
        string appData = Environment.GetFolderPath(Environment.SpecialFolder.LocalApplicationData); 
        var companyName = Assembly.GetEntryAssembly().GetCustomAttributes<AssemblyCompanyAttribute>().FirstOrDefault();
        return Path.Combine(appData, companyName?.Company ?? Assembly.GetEntryAssembly().GetName().Name, fileName);
    }

    public static void Load()
    {
        if (File.Exists(filePath))
            Instance = System.Text.Json.JsonSerializer.Deserialize<T>(File.ReadAllText(filePath));
        else
            Instance = new T(); 
    }

    public static void Save()
    {
        string json = System.Text.Json.JsonSerializer.Serialize(Instance);
        Directory.CreateDirectory(Path.GetDirectoryName(filePath));
        File.WriteAllText(filePath, json);
    }
}

Некоторые улучшения могут быть сделаны при отключении конструктора подкласса настроек и создании SettingsManager<T>.Instance без его загрузки(); это зависит от ваших собственных вариантов использования.

Мои улучшения принятого ответа были отклонены, так что здесь как отдельный ответ.

Нет необходимости в каком-либо пакете nuget и нет необходимости создавать собственный JSON и т. д.
По умолчанию при создании новых проектов .NET Core или .NET5/6 раздел настроек отсутствует и его необходимо добавить вручную.

Просто вручную создайте папку Properties в решении. Когда вы назовете новую папку Properties, вы увидите, что значок папки немного изменится.

Properties folder

Щелкните правой кнопкой мыши эту новую папку свойств и добавьте новый элемент.

Добавьте файл настроек и, чтобы он был таким же, как в старых проектах, переименуйте предлагаемое имя с Settings1.settings на Settings.settings

Add Settings File

Вот ты где. Настройки уже вернулись.

Settings dialog

Вы можете добавить файл конфигурации приложения, чтобы получить файл .config в выходном каталоге.

Add Application Configuration File

Другие вопросы по теме