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

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

SolutionExplorer

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

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

К счастью или к сожалению, Microsoft.Extensions.Configuration не поддерживает сохранение по дизайну. Узнайте больше в этом выпуске Github Почему в ConfigurationProvider нет сохранения?


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


До <= .Net 4.8 это было так просто, как:

  • добавить переменные в свойствах. Настройки пользователя

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

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

34
MarkusEgle 2 Июл 2019 в 10:39

4 ответа

Лучший ответ

enter image description here

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

Добавьте также файл app.config в корневую папку проекта (аналогично через «Добавить» -> «Новый элемент»), сохраните настройки еще раз, скомпилируйте проект, и вы найдете файл .dll.config в выходной папке. Вы можете изменить значения приложения по умолчанию, как и раньше.

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

25
Alexander Zwitbaum 17 Окт 2019 в 01:40

Вы можете использовать пакет 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; }
    }
}
6
Tom 11 Июл 2019 в 00:24

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

0
kreld 10 Окт 2019 в 14:34

Как указывалось в публикациях, на которые вы ссылались, API-интерфейс конфигурации предназначен для однократной настройки вашего приложения или, по крайней мере, только для чтения. Если ваша главная цель - сохранить пользовательские настройки легко / быстро / просто, вы можете свернуть что-нибудь самостоятельно. Сохранение настроек в папке 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, но у нас будет экземпляр в памяти, ref _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>
18
Funk 9 Июл 2019 в 22:21