Каков предпочтительный способ сохранения пользовательских настроек для приложений WPF с .NET 5, .NET 6 или .Net Core >=3.0?
Создан проект WPF .Net Core 3.0 (VS2019 V16.3.1) Теперь я вижу, что раздела Properties.Settings больше нет.
После поиска в Интернете начал погружаться в 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?
Читать переменные при запускеvar culture = new CultureInfo(Properties.Settings.Default.LanguageSettings);
когда переменная изменяется -> немедленно сохранить ееProperties.Settings.Default.LanguageSettings = selected.TwoLetterISOLanguageName; Properties.Settings.Default.Save();
Я говорю о настройках для повышения удобства пользователя. В моем примере выше это похоже на настройки приложения, но мне нравится позволять пользователям выбирать свой язык. И почему я должен заставлять своих пользователей редактировать файл конфигурации? Почему бы не предоставить административную область для настройки? Почему бы не сделать это как Практическое руководство. Запись пользовательских настроек во время выполнения с помощью C# Я успешно использую/сохраняю эти пользовательские настройки около 15 лет. Должны ли мы вернуться к реестру? Вернуться к истокам?
У меня сейчас нет "ответа". Но я ожидаю, что будет какая-то абстракция конфигурации, которая будет писать либо в LocalApplicationData, либо в ApplicationData, либо в CommonApplicationData, в зависимости от области пользовательских настроек. Я ожидаю, что эта абстракция запретит вам изменять конфигурацию вашего приложения на уровне приложения (по многим причинам, в том числе из-за того, что ваши уровни разрешений, вероятно, будут неадекватными). Это то, что я ожидал от кода конфигурации Microsoft для клиентских приложений. Документация, на которую вы ссылаетесь, - это документация asp.net, которая совершенно отличается.
Я предположил, что Microsoft.Extensions.Configuration подходит даже для WPF/WinForms <-> .Net Core, а не только для ASP.Net. Другой информации пока не нашел.
Один ссылка, где я прочитал, что следует использовать Microsoft.Extensions.Configuration: stackoverflow.com/a/48866609/3090544
Аааа, возможно, я ошибся, когда сказал, что это был asp.net только тогда. Надеюсь ответит знающий. Тем временем я бы решил, что это может быть просто последовательный файл в LocalApplicationData и просто игнорирование материала конфигурации Microsoft.
У меня были подобные расстройства. В конце концов я просто пошел с JSON.net. Супер просто
Вы всегда можете использовать простой текстовый файл (JSON, XML, обычный ключ=значение;ключ=значение или форматирование ini-файла, если вам нужны разделы) или облегченную базу данных, такую как SQLite. Вы также можете создавать простые объекты данных и сериализовать их. Настройка этого (может быть кроме SQLite и сериализации) делается очень быстро. XML и JSON (через API) могут изменять и искать данные без явного разбора. XML обеспечивает простой и легкий обход (например, LINQ to XML).
Итак, мы вернулись к тому, чтобы «просто создать собственное (дерьмовое) решение» для самых простых вещей. Я думал, что мы оставили это позади около 20 лет назад.
Если я сверну свой собственный файл JSON, где я должен его сохранить?





Как указано в сообщениях, на которые вы ссылались, 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
Это лучшее решение! Использование «старого» подхода к параметрам в сочетании с MSIX приводит к перезаписыванию параметров приложения при каждом автоматическом обновлении.
API Property.Settings имел метод сохранения. это было не только для чтения. Было здорово и очень просто хранить и загружать пользовательские настройки в локальные данные приложения, где такие настройки начинаются. Это было ТИПИЧНО БЕЗОПАСНО, не полагаясь на дрянные строковые вещи, такие как настройки ["LeftHotkey"].
@Blechdose Вы хоть читали OP, мы говорили о Microsoft.Extensions.Configuration, у которого нет метода сохранения. Предлагаемое мной решение является безопасным по типу и сохраняет настройки в локальных данных приложения или в любом другом месте.
Сообщение @Funk OP посвящено Property.Settings, которых ему не хватает в .NET Core, и вы сказали «API конфигурации», который затем ссылается на Property.Settings. Хотя вы правы, я пропустил часть «сообщения, на которые вы ссылаетесь» (вы имеете в виду ссылки?). Но это все еще сбивает с толку. Не могли бы вы изменить его с «API конфигурации» на «Microsoft.Extensions.Configuration»?
Как, черт возьми, этот прогресс? Переход от хорошо работающей и зарекомендовавшей себя системы хранения пользовательских настроек, простой в использовании и интегрированной в IDE, к «да ладно, просто приготовьте что-то свое, сохраните его в txt-файле где-нибудь или что-то в этом роде». Весь этот проект ядра .net представляет собой такой огромный шаг назад по сравнению с .NET Framework, что это действительно ошеломляет.
Вы можете использовать пакет 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
Просто добавьте этот «класс пользовательских настроек» в свой проект, где вы должны добавить переменные настроек в качестве свойств с некоторыми аннотациями, где вы можете посмотреть, как это должно выглядеть в других старых Settings.Designer.cs.
Просто дважды щелкните файл Settings.settings в вашем проекте. Он по-прежнему будет открываться в дизайнере, как и раньше. Просто его больше нет в списке свойств.
И где мне найти этот файл настроек? В решении для приложения WPF .Net Core такого файла нет, даже не на уровне папки. Только что снова проверил с недавно созданным проектом с VS2019 V16.3.3
«Файл настроек» находится в списке типов файлов, когда вы щелкаете проект правой кнопкой мыши и выбираете Добавить новый элемент. Однако этот файл настроек доступен только для чтения, поскольку в нем нет метода Сохранять(). (Вы также не можете вызвать Обновление() или Перезагрузить(), поэтому вы не можете переместить настройки из старой версии в новую.)
Вы можете добавить тот же старый добрый файл настроек, например. щелкните правой кнопкой мыши Свойства -> Добавить -> Новый элемент и найдите «Настройки». Файл можно редактировать в дизайнере настроек и использовать, как и в проектах .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
И где править <exe-файл>.config при развертывании? Или они переименованы/перемещены? До настройки .Net Core наш конфиг редактировался в той же директории, что и exe-файл.
Теперь он заменен файлом <executable..dll.config>. Могу я получить награду, пожалуйста?
Насколько мне известно, изменение назначения вознаграждения невозможно, но я изменил принятый ответ на ваш. Включите не менее 15 пунктов... :-)
В моем проекте WPF Core 3.1 обозреватель решений выглядит как в вопросе, а не как в ответе. Свойства отсутствуют, то есть я также не могу щелкнуть их правой кнопкой мыши.
Вам также необходимо установить пакет NuGet System.Configuration.ConfigurationManager
@PeterHuber, если вы не можете найти папку «Свойства», просто создайте их, а затем добавьте в нее файл .settings.
Работает идеально. Но я получаю много сообщений IntelliSense в списке ошибок. «Не удалось найти информацию о схеме для элемента «userSetting»», «Не удалось найти информацию о схеме для элемента «setting»» и т. д.
Последующие добавления в файл настроек в пользовательском интерфейсе с последующим сохранением приводят к диалоговому окну с ошибкой: Произошла ошибка при сохранении значений в файле app.config. Файл может быть поврежден или содержать недопустимый XML. Я использую .net core 3.0 в консольном приложении.
После добавления app.config, как мне сохранить ..? Кроме того, app.settings больше не поддерживает тег usersettings. Как данные из настроек попадают в файл app.config.
Спасибо. Автоматически создает <myproject>.dll.config из App.Config, который можно изменять извне. К сожалению, это не удается при создании одного исполняемого файла. Какие-либо предложения?
@PeterHuber мой проект изначально не включал папку «Свойства». мне пришлось создать профиль публикации, который создал папку свойств с профилем публикации под ней. затем были добавлены настройки, как было предложено в ответе.
Для 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()
На основе отвечать 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, вы увидите, что значок папки немного изменится.
Щелкните правой кнопкой мыши эту новую папку свойств и добавьте новый элемент.
Добавьте файл настроек и, чтобы он был таким же, как в старых проектах, переименуйте предлагаемое имя с Settings1.settings на Settings.settings
Вот ты где. Настройки уже вернулись.
Вы можете добавить файл конфигурации приложения, чтобы получить файл .config в выходном каталоге.
Мне любопытно, что заставило вас изменить файлы конфигурации во время выполнения? Чем вызваны эти изменения? Вы пытаетесь использовать свою конфигурацию как некий профиль пользователя?