What is the prefered way for persisting user settings for WPF applications with .Net Core >=3.0?
Created WPF .Net Core 3.0 Project (VS2019 V16.3.1) Now I have seen there is no Properties.Settings section anymore.
After online search, started to dive into Microsoft.Extensions.Configuration.
Beside the bloated code to access the settings, now even worse -> No save?
User Configuration Settings in .NET Core
幸运的是,Microsoft.Extensions.Configuration不支持按设计保存。阅读此Github问题中的更多内容为什么ConfigurationProvider中没有保存?
对于.Net Core> = 3.0的WPF应用程序,要保留用户设置的首选(便捷/快速/简单)方式是什么?
之前<= .Net 4.8一样简单:
Ale*_*aum 61
您可以添加相同的旧设置文件,例如通过右键单击“属性”->“添加”->“新项目”并搜索“设置”。该文件可以在设置设计器中编辑,并在之前的 .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 项目进行测试。
Mar*_*gle 13
我对已接受答案的改进被拒绝,因此这里作为单独的答案。
不需要任何 nuget 包,也不需要滚动自己的 JSON 等。
默认情况下,创建新的 .NET Core 或 .NET5/6/7 项目时,设置部分会丢失,您必须手动添加它。
<2023 年更新>
正如 Viacheslav 所描述的,有一种解决方案几乎只需单击一个按钮即可。打开项目属性,转到“设置”,然后单击“创建或打开应用程序设置”链接。
使用 VS 2022 和.net7进行测试
本次更新保留了所描述的旧手动方法。
</更新2023>
只需在解决方案中手动创建 Properties 文件夹即可。当您命名新文件夹时,Properties您会看到文件夹图标会略有变化。
右键单击这个新的属性文件夹并添加新项目
添加设置文件并与旧项目中的名称相同,将建议的名称从Settings1.settings重命名为Settings.settings
给你。设置已经回来了。
您可以添加应用程序配置文件以获取输出目录中的 .config 文件
Fun*_*unk 10
正如您所引用的帖子中所指出的那样,配置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);
}
}
Run Code Online (Sandbox Code Playgroud)
使用最基础的演示 UserSettings
public class UserSettings
{
public string Name { get; set; }
}
Run Code Online (Sandbox Code Playgroud)
我将不提供完整的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";
}
}
Run Code Online (Sandbox Code Playgroud)
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>
Run Code Online (Sandbox Code Playgroud)
对于 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();
Run Code Online (Sandbox Code Playgroud)
其中“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();
}
Run Code Online (Sandbox Code Playgroud)
小智 7
根据 Funk 的回答,这是一个抽象的通用单例样式变体,它删除了 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; }
}
Run Code Online (Sandbox Code Playgroud)
用法:
//Loading and reading values
MySettings.Load();
var theText = MySettings.Instance.MyText;
var theBool = MySettings.Instance.SomeBoolean;
//Updating values
MySettings.Instance.MyText = "SomeNewText"
MySettings.Save();
Run Code Online (Sandbox Code Playgroud)
正如您所看到的,创建和使用您的设置的行数非常少,并且由于没有参数而更加严格。
基类定义设置的存储位置,并且每个 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);
}
}
Run Code Online (Sandbox Code Playgroud)
SettingsManager<T>.Instance可以在禁用设置子类的构造函数以及在不使用 Load()的情况下创建它方面进行一些改进;这取决于您自己的用例。
您可以使用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; }
}
}
Run Code Online (Sandbox Code Playgroud)
| 归档时间: |
|
| 查看次数: |
2152 次 |
| 最近记录: |