Equivalent to UserSettings / ApplicationSettings in WPF dotnet core

前端 未结 6 1315
没有蜡笔的小新
没有蜡笔的小新 2020-12-23 16:32

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 t

相关标签:
6条回答
  • 2020-12-23 16:51

    For Wpf Net.Core

    Project click Right Mouse Button -> Add New Item -> Settings File (General)

    Use

    Settings1.Default.Height = this.Height;
    Settings1.Default.Width = this.Width;
    
    this.Height = Settings1.Default.Height;
    this.Width = Settings1.Default.Width;
    
    Settings1.Default.Save();
    

    Where 'Settings1' created file name

    EXAMPLE

    Double click 'Settings1.settings' file and Edit

    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();
    }
    
    0 讨论(0)
  • 2020-12-23 16:54

    As pointed out in the posts you referenced, the Microsoft.Extensions.Configuration API is meant as a one time set up for your app, or at the very least to be read-only. If you're main goal is to persist user settings easy/fast/simple, you could roll something up yourself. Storing the settings in the ApplicationData folder, in resemblance to the old 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);
        }
    }
    

    A demo using the most basic of UserSettings

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

    I'm not going to provide a full MVVM example, still we'd have an instance in memory, ref _userSettings. Once you load settings, the demo will have its default properties overridden. In production, of course, you wouldn't provide default values on start up. It's just for the purpose of illustration.

    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>
    
    0 讨论(0)
  • 2020-12-23 16:55

    Just double click the Settings.settings file in your project. It will still open up in the designer just like before. You just do not have it listed in Properties windows anymore.

    0 讨论(0)
  • 2020-12-23 16:57

    Based on Funk's answer here's an abstract generic singleton-style variation that removes some of the administration around SettingsManager and makes creating additional settings classes and using them as simple as possible:

    Typed Settings 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; }
    }
    

    Usage:

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

    As you can see the number of lines to create and use your settings are just as minimal, and a bit more rigid as there are no parameters.

    The base class defines where settings are stored and allows only for one settings file per MySettings subclass - assembly and class names determine its location. For the purpose of replacing a properties file that is enough.

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

    Some improvements might be made in disabling the constructor of the settings subclass and creation of SettingsManager<T>.Instance without Load()ing it; that depends on your own use cases.

    0 讨论(0)
  • 2020-12-23 17:01

    You can use a Nuget package System.Configuration.ConfigurationManager. It is compatible with .Net Standard 2.0, so it should be usable in .Net Core application.

    There is no designer for this, but otherwise it works the same as .Net version, and you should be able to just copy the code from your Settings.Designer.cs. Also, you can override OnPropertyChanged, so there's no need to call Save.

    Here's an example, from the working .Net Standard project:

    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; }
        }
    }
    
    0 讨论(0)
  • 2020-12-23 17:03

    You can add the same old good settings file e.g. via the right click on the Properties -> Add -> New Item and search for the "Settings". The file can be edited in the settings designer and used as in the .net framework projects before (ConfigurationManager, Settings.Default.Upgrade(), Settings.Default.Save, etc. works).

    Add also the app.config file to the project root folder (the same way via the Add -> New Item), save the settings once again, compile the project and you will find a .dll.config file in the output folder. You can change now default app values as before.

    Tested with Visual Studio 1.16.3.5 and a .net core 3.0 WPF project.

    0 讨论(0)
提交回复
热议问题