等效于 WPF .NET 5、.NET 6 或 .Net Core 中的 UserSettings / ApplicationSettings

Equivalent to UserSettings / ApplicationSettings in WPF .NET 5, .NET 6 or .Net Core

为使用 .NET 5、.NET 6 或 .Net Core >=3.0 的 WPF 应用程序保留用户设置的首选方法是什么?

创建WPF .Net Core 3.0 项目(VS2019 V16.3.1) 现在我看到没有 Properties.Settings 部分了。

经过网上搜索,开始深入Microsoft.Extensions.Configuration。

除了臃肿的代码访问设置,现在更糟 -> 没有保存?
User Configuration Settings in .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?


使用 .Net Core >=3.0 的 WPF 应用程序的首选(和 easy/fast/simple)持久用户设置的方式是什么?
在`之前

正如您在引用的帖子中指出的那样,Microsoft.Extensions.Configuration API 是为您的应用设置的一次性设置,或者至少是 read-only。如果您的主要目标是保留用户设置 easy/fast/simple,您可以自己收集一些东西。将设置存储在 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>

您可以使用 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; }
    }
}

只需双击项目中的 Settings.settings 文件。它仍然会像以前一样在设计器中打开。您只是不再将其列在属性 windows 中。

您可以添加相同的旧设置文件,例如通过右键单击属性 -> 添加 -> 新项目并搜索 "Settings"。该文件可以在设置设计器中编辑,并像以前在 .net 框架项目中一样使用(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 项目进行测试。

对于 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'创建的文件名

示例

双击“Settings1.settings”文件并编辑

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();
}

基于 Funk 的 ,这里有一个抽象的通用单例样式变体,它删除了一些围绕 SettingsManager 的管理,并使得创建额外的设置 classes 并尽可能简单地使用它们:

键入的设置 class:

//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();

如您所见,创建和使用您的设置的行数同样最少,而且由于没有参数而更加严格。

基础 class 定义存储设置的位置,并且每个 MySettings 子 class 只允许一个设置文件 - 程序集和 class 名称决定其位置。为了替换一个属性文件就足够了。

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);
    }
}

在禁用设置 subclass 的构造函数和在没有 Load() 的情况下创建 SettingsManager<T>.Instance 方面可能会做出一些改进;这取决于您自己的用例。

我对已接受答案的改进被拒绝了,所以这里作为单独的答案。

不需要任何 nuget 包,也不需要自己动手 JSON 等等
默认情况下,在创建新的 .NET Core 或 .NET5/6 项目时,设置部分缺失,您必须手动添加它。

只需在解决方案中手动创建 Properties 文件夹即可。 当您将新文件夹命名为 Properties 时,您会看到文件夹图标会略有变化。

右键单击这个新的 Properties 文件夹并添加新项目

添加一个设置文件并与旧项目中的相同,将建议的名称从 Settings1.settings 重命名为 Settings.settings

给你。设置已经恢复。

您可以添加应用程序配置文件以获取输出目录中的 .config 文件