From ca93b3f9c8226540826a5cf04289568a07af32ea Mon Sep 17 00:00:00 2001 From: rmbgame Date: Sat, 6 Mar 2021 10:57:02 +0800 Subject: [PATCH] =?UTF-8?q?=E9=85=8D=E7=BD=AE=E6=96=87=E4=BB=B6=E5=8A=9F?= =?UTF-8?q?=E8=83=BD=E5=AE=8C=E5=96=84?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../App.axaml.cs | 10 +- .../Converters/BitmapAssetValueConverter.cs | 5 +- .../UI/Pages/Settings/Settings_UI.axaml | 5 +- .../UI/Pages/SteamAccountPage.axaml | 4 +- .../Extensions/TaskExtensions.cs | 79 +++++++ .../Models/{ => Enum}/TextReaderProvider.cs | 0 .../Models/{ => Settings}/AppUserSettings.cs | 14 -- .../Models/Settings/AuthSettings.cs | 46 +++++ .../Models/Settings/GeneralSettings.cs | 76 +++++++ .../Models/Settings/SettingsHost.cs | 90 ++++++++ .../Models/Settings/SteamSettings.cs | 41 ++++ .../Models/Settings/UISettings.cs | 31 +++ .../ApplicationSettingsProvider.cs | 94 +++++++++ .../Serialization/FileSettingsProvider.cs | 115 +++++++++++ .../Serialization/ISerializationProvider.cs | 26 +++ .../Serialization/SerializableProperty.cs | 18 ++ .../Serialization/SerializablePropertyBase.cs | 194 ++++++++++++++++++ .../Serialization/ValueChangedEventArgs.cs | 18 ++ .../Services/IConfigFileService.cs | 17 -- ...plication.SteamTools.Client.Desktop.csproj | 1 + .../UI/Resx/AppResources.Designer.cs | 9 + .../UI/Resx/AppResources.resx | 5 +- .../UI/Resx/R.cs | 28 ++- .../UI/ViewModels/MainWindowViewModel.cs | 14 +- .../Pages/{ => About}/AboutPageViewModel.cs | 0 .../Pages/Settings/SettingsPageViewModel.cs | 27 ++- System.Common.CoreLib/Serializable.cs | 11 + .../Models/Settings/SettingsHost.cs | 4 +- .../SteamTools/Views/Pages/SettingsPage.xaml | 1 - 29 files changed, 906 insertions(+), 77 deletions(-) create mode 100644 System.Application.SteamTools.Client.Desktop/Extensions/TaskExtensions.cs rename System.Application.SteamTools.Client.Desktop/Models/{ => Enum}/TextReaderProvider.cs (100%) rename System.Application.SteamTools.Client.Desktop/Models/{ => Settings}/AppUserSettings.cs (69%) create mode 100644 System.Application.SteamTools.Client.Desktop/Models/Settings/AuthSettings.cs create mode 100644 System.Application.SteamTools.Client.Desktop/Models/Settings/GeneralSettings.cs create mode 100644 System.Application.SteamTools.Client.Desktop/Models/Settings/SettingsHost.cs create mode 100644 System.Application.SteamTools.Client.Desktop/Models/Settings/SteamSettings.cs create mode 100644 System.Application.SteamTools.Client.Desktop/Models/Settings/UISettings.cs create mode 100644 System.Application.SteamTools.Client.Desktop/Serialization/ApplicationSettingsProvider.cs create mode 100644 System.Application.SteamTools.Client.Desktop/Serialization/FileSettingsProvider.cs create mode 100644 System.Application.SteamTools.Client.Desktop/Serialization/ISerializationProvider.cs create mode 100644 System.Application.SteamTools.Client.Desktop/Serialization/SerializableProperty.cs create mode 100644 System.Application.SteamTools.Client.Desktop/Serialization/SerializablePropertyBase.cs create mode 100644 System.Application.SteamTools.Client.Desktop/Serialization/ValueChangedEventArgs.cs rename System.Application.SteamTools.Client.Desktop/UI/ViewModels/Pages/{ => About}/AboutPageViewModel.cs (100%) diff --git a/System.Application.SteamTools.Client.Desktop.Avalonia.App/App.axaml.cs b/System.Application.SteamTools.Client.Desktop.Avalonia.App/App.axaml.cs index a55b039b8..3e11772ff 100644 --- a/System.Application.SteamTools.Client.Desktop.Avalonia.App/App.axaml.cs +++ b/System.Application.SteamTools.Client.Desktop.Avalonia.App/App.axaml.cs @@ -19,6 +19,8 @@ using Avalonia.Markup.Xaml.Styling; using System.Application.Services; using System.Windows.Input; using System.Linq; +using System.Application.Models.Settings; +using System.Application.UI.Resx; #if WINDOWS using System.Windows.Shell; using WpfApplication = System.Windows.Application; @@ -77,6 +79,13 @@ namespace System.Application.UI Name = ThisAssembly.AssemblyTrademark; + #region å�¯åŠ¨æ—¶åŠ è½½çš„èµ„æº� + SettingsHost.Load(); + compositeDisposable.Add(SettingsHost.Save); + UISettings.Language.Subscribe(x => R.ChangeLanguage(x)); + + #endregion + MainWindow = new MainWindow { DataContext = new MainWindowViewModel(), @@ -151,7 +160,6 @@ namespace System.Application.UI { notifyIcon.IconPath = string.Empty; }); - #endregion #if WINDOWS diff --git a/System.Application.SteamTools.Client.Desktop.Avalonia/Application/Converters/BitmapAssetValueConverter.cs b/System.Application.SteamTools.Client.Desktop.Avalonia/Application/Converters/BitmapAssetValueConverter.cs index f2c9aa74b..95dc9310c 100644 --- a/System.Application.SteamTools.Client.Desktop.Avalonia/Application/Converters/BitmapAssetValueConverter.cs +++ b/System.Application.SteamTools.Client.Desktop.Avalonia/Application/Converters/BitmapAssetValueConverter.cs @@ -42,7 +42,10 @@ namespace System.Application.Converters } else if (value is Stream s) { - s.Position = 0; + if (s.Position > 0) + { + s.Position = 0; + } return new Bitmap(s); } throw new NotSupportedException(); diff --git a/System.Application.SteamTools.Client.Desktop.Avalonia/Application/UI/Pages/Settings/Settings_UI.axaml b/System.Application.SteamTools.Client.Desktop.Avalonia/Application/UI/Pages/Settings/Settings_UI.axaml index 7916d038c..a2363f99a 100644 --- a/System.Application.SteamTools.Client.Desktop.Avalonia/Application/UI/Pages/Settings/Settings_UI.axaml +++ b/System.Application.SteamTools.Client.Desktop.Avalonia/Application/UI/Pages/Settings/Settings_UI.axaml @@ -2,6 +2,7 @@ xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" xmlns:vm="clr-namespace:System.Application.UI.ViewModels;assembly=System.Application.SteamTools.Client.Desktop" xmlns:resx="clr-namespace:System.Application.UI.Resx;assembly=System.Application.SteamTools.Client.Desktop" + xmlns:ms="clr-namespace:System.Application.Models.Settings;assembly=System.Application.SteamTools.Client.Desktop" xmlns:d="http://schemas.microsoft.com/expression/blend/2008" xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006" mc:Ignorable="d" d:DesignWidth="800" d:DesignHeight="450" @@ -14,7 +15,7 @@ @@ -32,7 +33,7 @@ - + \ No newline at end of file diff --git a/System.Application.SteamTools.Client.Desktop.Avalonia/Application/UI/Pages/SteamAccountPage.axaml b/System.Application.SteamTools.Client.Desktop.Avalonia/Application/UI/Pages/SteamAccountPage.axaml index 830bb72c9..b20b0da0d 100644 --- a/System.Application.SteamTools.Client.Desktop.Avalonia/Application/UI/Pages/SteamAccountPage.axaml +++ b/System.Application.SteamTools.Client.Desktop.Avalonia/Application/UI/Pages/SteamAccountPage.axaml @@ -38,8 +38,8 @@ IsVisible="{Binding AvatarStream,Converter={StaticResource IsNullConverter},ConverterParameter=invert}"/> - - + + TaskLog.Raise(new TaskLog(callerMemberName, callerFilePath, callerLineNumber, x.Exception)), + TaskContinuationOptions.OnlyOnFaulted); + } + + public static void ForgetAndDispose( + this Task task, + [CallerMemberName] string callerMemberName = "", + [CallerFilePath] string callerFilePath = "", + [CallerLineNumber] int callerLineNumber = 0) + { + task.ContinueWith( + x => TaskLog.Raise(new TaskLog(callerMemberName, callerFilePath, callerLineNumber, x.Exception)), + TaskContinuationOptions.OnlyOnFaulted).ContinueWith(s=>s.Dispose()); + } + + public static Task WhenAll(this IEnumerable tasks) + { + return Task.WhenAll(tasks); + } + + public static Task WhenAll(this IEnumerable> tasks) + { + return Task.WhenAll(tasks); + } + } + + public class TaskLog + { + public string CallerMemberName { get; } + + public string CallerFilePath { get; } + + public int CallerLineNumber { get; } + + public Exception Exception { get; } + + public TaskLog(string callerMemberName, string callerFilePath, int callerLineNumber, Exception exception) + { + this.CallerMemberName = callerMemberName; + this.CallerFilePath = callerFilePath; + this.CallerLineNumber = callerLineNumber; + this.Exception = exception; + } + + + public static EventHandler Occured = (sender, e) => + { + const string format = @"Unhandled Exception occured from Task.Forget() +----------- +Caller file : {1} + : line {2} +Caller member: {0} +Exception: {3} + +"; + System.Diagnostics.Debug.WriteLine(format, e.CallerMemberName, e.CallerFilePath, e.CallerLineNumber, e.Exception); + }; + + internal static void Raise(TaskLog log) + { + Occured?.Invoke(typeof(TaskLog), log); + } + } +} diff --git a/System.Application.SteamTools.Client.Desktop/Models/TextReaderProvider.cs b/System.Application.SteamTools.Client.Desktop/Models/Enum/TextReaderProvider.cs similarity index 100% rename from System.Application.SteamTools.Client.Desktop/Models/TextReaderProvider.cs rename to System.Application.SteamTools.Client.Desktop/Models/Enum/TextReaderProvider.cs diff --git a/System.Application.SteamTools.Client.Desktop/Models/AppUserSettings.cs b/System.Application.SteamTools.Client.Desktop/Models/Settings/AppUserSettings.cs similarity index 69% rename from System.Application.SteamTools.Client.Desktop/Models/AppUserSettings.cs rename to System.Application.SteamTools.Client.Desktop/Models/Settings/AppUserSettings.cs index ef83e6c6c..a04658d83 100644 --- a/System.Application.SteamTools.Client.Desktop/Models/AppUserSettings.cs +++ b/System.Application.SteamTools.Client.Desktop/Models/Settings/AppUserSettings.cs @@ -24,18 +24,4 @@ namespace System.Application.Models /// public Dictionary? TextReaderProvider { get; set; } } - -#if DEBUG - - [Obsolete("use AppUserSettings", true)] - public class SettingsModel - { - [Obsolete("use AppUserSettings.StartupArgs", true)] - public string? SteamRunParam { get; set; } - - [Obsolete("use AppUserSettings.EnableTextLog", true)] - public bool EnableTextLog { get; set; } - } - -#endif } \ No newline at end of file diff --git a/System.Application.SteamTools.Client.Desktop/Models/Settings/AuthSettings.cs b/System.Application.SteamTools.Client.Desktop/Models/Settings/AuthSettings.cs new file mode 100644 index 000000000..025104c58 --- /dev/null +++ b/System.Application.SteamTools.Client.Desktop/Models/Settings/AuthSettings.cs @@ -0,0 +1,46 @@ +using System; +using System.Application.Serialization; +using System.Collections.Generic; +using System.IO; +using System.Linq; +using System.Runtime.CompilerServices; +using System.Threading.Tasks; + +namespace System.Application.Models.Settings +{ + public static class AuthSettings + { + static AuthSettings() + { + Authenticators.ValueChanged += Authenticators_ValueChanged; + } + + private static void Authenticators_ValueChanged(object sender, ValueChangedEventArgs e) + { + if (e.NewValue != e.OldValue) + { + if (AuthSettings.IsCurrentDirectorySaveAuthData.Value) + { + File.WriteAllText(Path.Combine(AppContext.BaseDirectory, Constants.AUTHDATA_FILE), e.NewValue); + } + } + } + + /// + /// ÁîÅÆÊý¾Ý(ѹËõ´æ´¢£© + /// + public static SerializableProperty Authenticators { get; } + = new SerializableProperty(GetKey(), Providers.Local) { AutoSave = true }; + + /// + /// ÊÇ·ñ½«ÁîÅÆÊý¾Ý´æ´¢ÔÚ³ÌÐò·¾¶Ï + /// + public static SerializableProperty IsCurrentDirectorySaveAuthData { get; } + = new SerializableProperty(GetKey(), Providers.Local, false) { AutoSave = true }; + + private static string GetKey([CallerMemberName] string propertyName = "") + { + return nameof(AuthSettings) + "." + propertyName; + } + } +} diff --git a/System.Application.SteamTools.Client.Desktop/Models/Settings/GeneralSettings.cs b/System.Application.SteamTools.Client.Desktop/Models/Settings/GeneralSettings.cs new file mode 100644 index 000000000..6c74ef0de --- /dev/null +++ b/System.Application.SteamTools.Client.Desktop/Models/Settings/GeneralSettings.cs @@ -0,0 +1,76 @@ +using System; +using System.Application.Serialization; +using System.Collections.Generic; +using System.Linq; +using System.Runtime.CompilerServices; +using System.Threading.Tasks; + +namespace System.Application.Models.Settings +{ + public static class GeneralSettings + { + static GeneralSettings() + { + WindowsStartupAutoRun.ValueChanged += WindowsStartupAutoRun_ValueChanged; + IsEnableLogRecord.ValueChanged += IsEnableLogRecord_ValueChanged; + //CreateDesktopShortcut.ValueChanged += CreateDesktopShortcut_ValueChanged; + } + + private static void IsEnableLogRecord_ValueChanged(object sender, ValueChangedEventArgs e) + { + + } + + private static void WindowsStartupAutoRun_ValueChanged(object sender, ValueChangedEventArgs e) + { + + } + + /// + /// ³ÌÐòÊÇ·ñ¿ª»ú×ÔÆô¶¯ + /// + public static SerializableProperty WindowsStartupAutoRun { get; } + = new SerializableProperty(GetKey(), Providers.Local, false) { AutoSave = true }; + + ///// + ///// ´´½¨×ÀÃæ¿ì½Ý·½Ê½ + ///// + //public static SerializableProperty CreateDesktopShortcut { get; } + // = new SerializableProperty(GetKey(), Providers.Roaming, false) { AutoSave = true }; + + /// + /// ³ÌÐòÆô¶¯Ê±×îС»¯ + /// + public static SerializableProperty IsStartupAppMinimized { get; } + = new SerializableProperty(GetKey(), Providers.Local, false) { AutoSave = true }; + + /// + /// ÊÇ·ñÏÔʾÆðʼҳ + /// + public static SerializableProperty IsShowStartPage { get; } + = new SerializableProperty(GetKey(), Providers.Local, true) { AutoSave = true }; + + /// + /// ÆôÓÃÓÎÏ·ÁÐ±í±¾µØ»º´æ + /// + public static SerializableProperty IsSteamAppListLocalCache { get; } + = new SerializableProperty(GetKey(), Providers.Local, true) { AutoSave = true }; + + /// + /// ×Ô¶¯¼ì²é¸üР+ /// + public static SerializableProperty IsAutoCheckUpdate { get; } + = new SerializableProperty(GetKey(), Providers.Local, true) { AutoSave = true }; + + /// + /// ÆôÓôíÎóÈÕÖ¾¼Ç¼ + /// + public static SerializableProperty IsEnableLogRecord { get; } + = new SerializableProperty(GetKey(), Providers.Local, false) { AutoSave = true }; + + private static string GetKey([CallerMemberName] string propertyName = "") + { + return nameof(GeneralSettings) + "." + propertyName; + } + } +} diff --git a/System.Application.SteamTools.Client.Desktop/Models/Settings/SettingsHost.cs b/System.Application.SteamTools.Client.Desktop/Models/Settings/SettingsHost.cs new file mode 100644 index 000000000..461deb669 --- /dev/null +++ b/System.Application.SteamTools.Client.Desktop/Models/Settings/SettingsHost.cs @@ -0,0 +1,90 @@ +using System; +using System.Application.Serialization; +using System.Application.Services; +using System.Collections.Generic; +using System.IO; +using System.Runtime.CompilerServices; +using System.Text; + +namespace System.Application.Models.Settings +{ + public abstract class SettingsHost + { + private static readonly Dictionary instances = new(); + private readonly Dictionary cachedProperties = new(); + + protected virtual string CategoryName => this.GetType().Name; + + protected SettingsHost() + { + instances[this.GetType()] = this; + } + + /// + /// 请å�‚阅 缓存在当å‰�实例中获å�– + /// 如果没有缓存,根æ�®åˆ›å»ºå®ƒ + /// + /// + protected SerializableProperty Cache(Func> create, [CallerMemberName] string propertyName = "") + { + var key = this.CategoryName + "." + propertyName; + + if (this.cachedProperties.TryGetValue(key, out object obj) && obj is SerializableProperty property1) + return property1; + + var property = create(key); + this.cachedProperties[key] = property; + + return property; + } + + #region Load / Save + + public static void Load() + { + try + { + Providers.Local.Load(); + } + catch (Exception ex) + { + Log.Error(nameof(SettingsHost), "Config Load", ex); + + File.Delete(Providers.LocalFilePath); + Providers.Local.Load(); + } + + } + + public static void Save() + { + try + { + Providers.Local.Save(); + } + catch (Exception ex) + { + Log.Error(nameof(SettingsHost), "Config Save", ex); + throw; + } + + } + + #endregion + + /// + /// 获å�–类型的é…�置对象的唯一实例。 + /// + public static T Instance() where T : SettingsHost, new() + { + return instances.TryGetValue(typeof(T), out SettingsHost host) ? (T)host : new T(); + } + } + + public class Providers + { + public static string LocalFilePath => Path.Combine(IOPath.AppDataDirectory, IConfigFileService.ConfigFileName); + + public static ISerializationProvider Local { get; } = new FileSettingsProvider(LocalFilePath); + } +} diff --git a/System.Application.SteamTools.Client.Desktop/Models/Settings/SteamSettings.cs b/System.Application.SteamTools.Client.Desktop/Models/Settings/SteamSettings.cs new file mode 100644 index 000000000..17cafd8eb --- /dev/null +++ b/System.Application.SteamTools.Client.Desktop/Models/Settings/SteamSettings.cs @@ -0,0 +1,41 @@ +using System; +using System.Application.Serialization; +using System.Collections.Generic; +using System.Linq; +using System.Runtime.CompilerServices; +using System.Threading.Tasks; + +namespace System.Application.Models.Settings +{ + public static class SteamSettings + { + /// + /// SteamÆô¶¯²ÎÊý + /// + public static SerializableProperty SteamStratParameter { get; } + = new SerializableProperty(GetKey(), Providers.Local, string.Empty) { AutoSave = true }; + + /// + /// SteamƤ·ô + /// + public static SerializableProperty SteamSkin { get; } + = new SerializableProperty(GetKey(), Providers.Local, string.Empty) { AutoSave = true }; + + /// + /// ×Ô¶¯ÔËÐÐSteam + /// + public static SerializableProperty IsAutoRunSteam { get; } + = new SerializableProperty(GetKey(), Providers.Local, false) { AutoSave = true }; + + /// + /// ¼ì²âµ½SteamÆô¶¯Ê±µ¯³öÏûϢ֪ͨ + /// + public static SerializableProperty IsEnableSteamLaunchNotification { get; } + = new SerializableProperty(GetKey(), Providers.Local, false) { AutoSave = true }; + + private static string GetKey([CallerMemberName] string propertyName = "") + { + return nameof(SteamSettings) + "." + propertyName; + } + } +} diff --git a/System.Application.SteamTools.Client.Desktop/Models/Settings/UISettings.cs b/System.Application.SteamTools.Client.Desktop/Models/Settings/UISettings.cs new file mode 100644 index 000000000..5fc9e0d37 --- /dev/null +++ b/System.Application.SteamTools.Client.Desktop/Models/Settings/UISettings.cs @@ -0,0 +1,31 @@ +using System; +using System.Application.Serialization; +using System.Collections.Generic; +using System.Linq; +using System.Runtime.CompilerServices; +using System.Threading.Tasks; + +namespace System.Application.Models.Settings +{ + public static class UISettings + { + /// + /// Ö÷Ìâ + /// + public static SerializableProperty Theme { get; } + = new SerializableProperty(GetKey(), Providers.Local) { AutoSave = true }; + + /// + /// ÓïÑÔ + /// + public static SerializableProperty Language { get; } + = new SerializableProperty(GetKey(), Providers.Local) { AutoSave = true }; + + + + private static string GetKey([CallerMemberName] string propertyName = "") + { + return nameof(UISettings) + "." + propertyName; + } + } +} diff --git a/System.Application.SteamTools.Client.Desktop/Serialization/ApplicationSettingsProvider.cs b/System.Application.SteamTools.Client.Desktop/Serialization/ApplicationSettingsProvider.cs new file mode 100644 index 000000000..1844d90b3 --- /dev/null +++ b/System.Application.SteamTools.Client.Desktop/Serialization/ApplicationSettingsProvider.cs @@ -0,0 +1,94 @@ +using System; +using System.Collections.Generic; +using System.ComponentModel; +using System.Configuration; +using System.Linq; + +namespace System.Application.Serialization +{ + public class ApplicationSettingsProvider : ApplicationSettingsBase, ISerializationProvider + { + public static ISerializationProvider Default { get; } = new ApplicationSettingsProvider(typeof(ApplicationSettingsProvider).FullName + "." + nameof(Default)); + + public bool IsLoaded { get; private set; } + + // UserScopedSettings 除é�žè‡³å°‘有一个,å�¦åˆ™ä¼¼ä¹Žä¸�会读å�–LocalFileSettingsProvider + // ReSharper disable once InconsistentNaming + // ReSharper disable once UnassignedGetOnlyAutoProperty + [UserScopedSetting, EditorBrowsable(EditorBrowsableState.Never)] + public object __Infrastructure { get; } + + public ApplicationSettingsProvider() { } + + public ApplicationSettingsProvider(string settingsKey) : base(settingsKey) { } + + public void SetValue(string key, T value) + { + this.AddProperty(key, typeof(T)); + this[key] = value; + } + + public bool TryGetValue(string key, out T value) + { + this.AddProperty(key, typeof(T)); + + try + { + value = (T)this[key]; + return true; + } + catch + { + value = default(T); + return false; + } + } + + public bool RemoveValue(string key) + { + return this.RemoveProperry(key); + } + + private void AddProperty(string key, Type type) + { + if (this.Properties.OfType().All(x => x.Name != key)) + { + var property = new SettingsProperty(key) + { + PropertyType = type, + Provider = this.Providers.OfType().FirstOrDefault(), + SerializeAs = SettingsSerializeAs.Xml, + }; + property.Attributes.Add(typeof(UserScopedSettingAttribute), new UserScopedSettingAttribute()); + + this.Properties.Add(property); + this.Reload(); + } + } + + private bool RemoveProperry(string key) + { + if (this.Properties.OfType().All(x => x.Name != key)) + { + return false; + } + + this.Properties.Remove(key); + this.Reload(); + + return true; + } + + void ISerializationProvider.Load() + { + this.Reload(); + this.IsLoaded = true; + } + + event EventHandler ISerializationProvider.Reloaded + { + add { } + remove { } + } + } +} diff --git a/System.Application.SteamTools.Client.Desktop/Serialization/FileSettingsProvider.cs b/System.Application.SteamTools.Client.Desktop/Serialization/FileSettingsProvider.cs new file mode 100644 index 000000000..aeec29e7d --- /dev/null +++ b/System.Application.SteamTools.Client.Desktop/Serialization/FileSettingsProvider.cs @@ -0,0 +1,115 @@ +using System; +using System.Collections.Generic; +using System.IO; +using System.Linq; + +namespace System.Application.Serialization +{ + public class FileSettingsProvider : ISerializationProvider + { + private readonly string _path; + private readonly object _sync = new object(); + private SortedDictionary _settings = new SortedDictionary(); + + public bool IsLoaded { get; private set; } + + + public FileSettingsProvider(string path) + { + this._path = path; + } + + public void SetValue(string key, T value) + { + lock (this._sync) + { + this._settings[key] = value; + } + } + + public bool TryGetValue(string key, out T value) + { + lock (this._sync) + { + object obj; + if (this._settings.TryGetValue(key, out obj) && obj is T) + { + value = (T)obj; + return true; + } + } + + value = default(T); + return false; + } + + public bool RemoveValue(string key) + { + lock (this._sync) + { + return this._settings.Remove(key); + } + } + + public void Save() + { + if (this._settings.Count == 0) return; + + var dir = Path.GetDirectoryName(this._path); + if (dir == null) throw new DirectoryNotFoundException(); + + if (!Directory.Exists(dir)) + { + Directory.CreateDirectory(dir); + } + + lock (this._sync) + { + using var stream = new FileStream(this._path, FileMode.Create, FileAccess.ReadWrite); + if (stream.Position > 0) + { + stream.Position = 0; + } + var data = Serializable.SMP(this._settings); + stream.Write(data, 0, data.Length); + //XamlServices.Save(stream, this._settings); + } + } + + public void Load() + { + if (File.Exists(this._path)) + { + using var stream = new FileStream(this._path, FileMode.Open, FileAccess.Read); + lock (this._sync) + { + if (stream.Position > 0) + { + stream.Position = 0; + } + + var source = Serializable.DMP>(stream); + //var source = XamlServices.Load(stream) as IDictionary; + this._settings = source == null + ? new SortedDictionary() + : new SortedDictionary(source); + } + } + else + { + lock (this._sync) + { + this._settings = new SortedDictionary(); + } + } + + this.IsLoaded = true; + } + + event EventHandler ISerializationProvider.Reloaded + { + add { } + remove { } + } + } +} diff --git a/System.Application.SteamTools.Client.Desktop/Serialization/ISerializationProvider.cs b/System.Application.SteamTools.Client.Desktop/Serialization/ISerializationProvider.cs new file mode 100644 index 000000000..ba619283d --- /dev/null +++ b/System.Application.SteamTools.Client.Desktop/Serialization/ISerializationProvider.cs @@ -0,0 +1,26 @@ +using System; +using System.Collections.Generic; +using System.Linq; + +namespace System.Application.Serialization +{ + public interface ISerializationProvider + { + bool IsLoaded { get; } + + void Save(); + + void Load(); + + /// + /// ÔÚproviderÒÑÖØÐ¼ÓÔØµ½ÆÚʱ·¢Éú + /// + event EventHandler Reloaded; + + void SetValue(string key, T value); + + bool TryGetValue(string key, out T value); + + bool RemoveValue(string key); + } +} diff --git a/System.Application.SteamTools.Client.Desktop/Serialization/SerializableProperty.cs b/System.Application.SteamTools.Client.Desktop/Serialization/SerializableProperty.cs new file mode 100644 index 000000000..a48062222 --- /dev/null +++ b/System.Application.SteamTools.Client.Desktop/Serialization/SerializableProperty.cs @@ -0,0 +1,18 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Threading.Tasks; + +namespace System.Application.Serialization +{ + public sealed class SerializableProperty : SerializablePropertyBase + { + public SerializableProperty(string key) : this(key, default(T)) { } + + public SerializableProperty(string key, T defaultValue) : base(key, ApplicationSettingsProvider.Default, defaultValue) { } + + public SerializableProperty(string key, ISerializationProvider provider) : base(key, provider) { } + + public SerializableProperty(string key, ISerializationProvider provider, T defaultValue) : base(key, provider, defaultValue) { } + } +} diff --git a/System.Application.SteamTools.Client.Desktop/Serialization/SerializablePropertyBase.cs b/System.Application.SteamTools.Client.Desktop/Serialization/SerializablePropertyBase.cs new file mode 100644 index 000000000..cfa70ac1a --- /dev/null +++ b/System.Application.SteamTools.Client.Desktop/Serialization/SerializablePropertyBase.cs @@ -0,0 +1,194 @@ +using System.Collections.Generic; +using System.ComponentModel; +using System.Diagnostics; + +namespace System.Application.Serialization +{ + [DebuggerDisplay("Value={Value}, Key={Key}, Default={Default}")] + public abstract class SerializablePropertyBase : INotifyPropertyChanged + { + private T _value; + private bool _cached; + + public string Key { get; } + + public ISerializationProvider Provider { get; } + + public bool AutoSave { get; set; } + + public T Default { get; } + + public virtual T Value + { + get + { + if (this._cached) return this._value; + + if (!this.Provider.IsLoaded) + { + this.Provider.Load(); + } + + object obj; + if (this.Provider.TryGetValue(this.Key, out obj)) + { + this._value = this.DeserializeCore(obj); + this._cached = true; + } + else + { + this._value = this.Default; + } + + return this._cached ? this._value : this.Default; + } + set + { + if (this._cached && Equals(this._value, value)) return; + + if (!this.Provider.IsLoaded) + { + this.Provider.Load(); + } + + var old = this._value; + this._value = value; + this._cached = true; + this.Provider.SetValue(this.Key, this.SerializeCore(value)); + this.OnValueChanged(old, value); + + if (this.AutoSave) this.Provider.Save(); + } + } + + protected SerializablePropertyBase(string key, ISerializationProvider provider) : this(key, provider, default(T)) { } + + protected SerializablePropertyBase(string key, ISerializationProvider provider, T defaultValue) + { + if (key == null) throw new ArgumentNullException(nameof(key)); + if (provider == null) throw new ArgumentNullException(nameof(provider)); + + this.Key = key; + this.Provider = provider; + this.Default = defaultValue; + + this.Provider.Reloaded += (sender, args) => + { + if (this._cached) + { + this._cached = false; + + var oldValue = this._value; + var newValue = this.Value; + if (!Equals(oldValue, newValue)) + { + this.OnValueChanged(oldValue, newValue); + } + } + else + { + var oldValue = default(T); + var newValue = this.Value; + this.OnValueChanged(oldValue, newValue); + } + }; + } + + + protected virtual object SerializeCore(T value) + { + return value; + } + + protected virtual T DeserializeCore(object value) + { + return (T)value; + } + + + public virtual IDisposable Subscribe(Action listener) + { + listener(this.Value); + return new ValueChangedEventListener(this, listener); + } + + public virtual void Reset() + { + if (!this.Provider.IsLoaded) + { + this.Provider.Load(); + } + + object old; + if (this.Provider.TryGetValue(this.Key, out old)) + { + if (this.Provider.RemoveValue(this.Key)) + { + this._value = default(T); + this._cached = false; + this.OnValueChanged(this.DeserializeCore(old), this.Default); + + if (this.AutoSave) this.Provider.Save(); + } + } + } + + private class ValueChangedEventListener : IDisposable + { + private readonly Action _listener; + private readonly SerializablePropertyBase _source; + + public ValueChangedEventListener(SerializablePropertyBase property, Action listener) + { + this._listener = listener; + this._source = property; + this._source.ValueChanged += this.HandleValueChanged; + } + + private void HandleValueChanged(object sender, ValueChangedEventArgs args) + { + this._listener(args.NewValue); + } + + public void Dispose() + { + this._source.ValueChanged -= this.HandleValueChanged; + } + } + + + public static implicit operator T(SerializablePropertyBase property) + { + return property.Value; + } + + + #region events + + public event EventHandler> ValueChanged; + + protected virtual void OnValueChanged(T oldValue, T newValue) + { + this.ValueChanged?.Invoke(this, new ValueChangedEventArgs(oldValue, newValue)); + } + + private readonly Dictionary>> _handlers + = new Dictionary>>(); + + event PropertyChangedEventHandler INotifyPropertyChanged.PropertyChanged + { + add { this.ValueChanged += (this._handlers[value] = (sender, args) => value(sender, new PropertyChangedEventArgs(nameof(this.Value)))); } + remove + { + EventHandler> handler; + if (this._handlers.TryGetValue(value, out handler)) + { + this.ValueChanged -= handler; + this._handlers.Remove(value); + } + } + } + + #endregion + } +} diff --git a/System.Application.SteamTools.Client.Desktop/Serialization/ValueChangedEventArgs.cs b/System.Application.SteamTools.Client.Desktop/Serialization/ValueChangedEventArgs.cs new file mode 100644 index 000000000..7a3adfc1b --- /dev/null +++ b/System.Application.SteamTools.Client.Desktop/Serialization/ValueChangedEventArgs.cs @@ -0,0 +1,18 @@ +using System; +using System.Collections.Generic; +using System.Linq; + +namespace System.Application.Serialization +{ + public class ValueChangedEventArgs : EventArgs + { + public T OldValue { get; } + public T NewValue { get; } + + public ValueChangedEventArgs(T oldValue, T newValue) + { + this.OldValue = oldValue; + this.NewValue = newValue; + } + } +} \ No newline at end of file diff --git a/System.Application.SteamTools.Client.Desktop/Services/IConfigFileService.cs b/System.Application.SteamTools.Client.Desktop/Services/IConfigFileService.cs index bf8955e41..eb74b3919 100644 --- a/System.Application.SteamTools.Client.Desktop/Services/IConfigFileService.cs +++ b/System.Application.SteamTools.Client.Desktop/Services/IConfigFileService.cs @@ -23,21 +23,4 @@ namespace System.Application.Services public static IConfigFileService Instance => DI.Get(); } - -#if DEBUG - - [Obsolete("use IConfigFileService.Instance", true)] - public class ConfigService - { - [Obsolete("use IConfigFileService.Instance.UserSettings", true)] - public SettingsModel? SteamToolModel { get; set; } - - [Obsolete("use IConfigFileService.Instance.SaveChanges", true)] - public void SaveConfig() => throw new NotImplementedException(); - - [Obsolete("changedAutoRead", true)] - public SettingsModel ReadConfig() => throw new NotImplementedException(); - } - -#endif } \ No newline at end of file diff --git a/System.Application.SteamTools.Client.Desktop/System.Application.SteamTools.Client.Desktop.csproj b/System.Application.SteamTools.Client.Desktop/System.Application.SteamTools.Client.Desktop.csproj index 0a5c822d0..1bece56d8 100644 --- a/System.Application.SteamTools.Client.Desktop/System.Application.SteamTools.Client.Desktop.csproj +++ b/System.Application.SteamTools.Client.Desktop/System.Application.SteamTools.Client.Desktop.csproj @@ -22,6 +22,7 @@ + diff --git a/System.Application.SteamTools.Client.Desktop/UI/Resx/AppResources.Designer.cs b/System.Application.SteamTools.Client.Desktop/UI/Resx/AppResources.Designer.cs index 03fd76a36..ee1e54026 100644 --- a/System.Application.SteamTools.Client.Desktop/UI/Resx/AppResources.Designer.cs +++ b/System.Application.SteamTools.Client.Desktop/UI/Resx/AppResources.Designer.cs @@ -288,6 +288,15 @@ namespace System.Application.UI.Resx { /// /// 查找类似 以该账å�·ç™»å½•Steam 的本地化字符串。 /// + public static string UserChange_BtnText { + get { + return ResourceManager.GetString("UserChange_BtnText", resourceCulture); + } + } + + /// + /// 查找类似 按下此按钮将é‡�å�¯Steam并以该账å�·ç™»å½• 的本地化字符串。 + /// public static string UserChange_BtnTootlip { get { return ResourceManager.GetString("UserChange_BtnTootlip", resourceCulture); diff --git a/System.Application.SteamTools.Client.Desktop/UI/Resx/AppResources.resx b/System.Application.SteamTools.Client.Desktop/UI/Resx/AppResources.resx index c9761f6fa..e8b69cd08 100644 --- a/System.Application.SteamTools.Client.Desktop/UI/Resx/AppResources.resx +++ b/System.Application.SteamTools.Client.Desktop/UI/Resx/AppResources.resx @@ -192,9 +192,12 @@ 本地令牌 - + 以该账å�·ç™»å½•Steam + + 按下此按钮将é‡�å�¯Steam并以该账å�·ç™»å½• + è´¦å�·åˆ‡æ�¢ diff --git a/System.Application.SteamTools.Client.Desktop/UI/Resx/R.cs b/System.Application.SteamTools.Client.Desktop/UI/Resx/R.cs index f7dc86d83..9ec45234c 100644 --- a/System.Application.SteamTools.Client.Desktop/UI/Resx/R.cs +++ b/System.Application.SteamTools.Client.Desktop/UI/Resx/R.cs @@ -18,6 +18,8 @@ namespace System.Application.UI.Resx public static readonly IList> Languages; public static readonly Dictionary SteamLanguages; + public AppResources Res { get; set; } = new AppResources(); + public static CultureInfo DefaultCurrentUICulture { get; } static R() @@ -43,6 +45,8 @@ namespace System.Application.UI.Resx { "ja", "japanese" }, { "ru", "russian" }, }; + + AppResources.Culture = DefaultCurrentUICulture; } static bool IsMatch(CultureInfo cultureInfo, string cultureName) @@ -67,15 +71,19 @@ namespace System.Application.UI.Resx /// public static void ChangeLanguage(string cultureName) { - void ChangeLanguage() - { - if (IsMatch(CultureInfo.CurrentUICulture, cultureName)) return; - CultureInfo.CurrentUICulture = CultureInfo.GetCultureInfo(cultureName); - AppResources.Culture = CultureInfo.GetCultureInfo(cultureName); - Current.Res = new AppResources(); - Current.RaisePropertyChanged(nameof(Res)); - } - MainThreadDesktop.BeginInvokeOnMainThread(ChangeLanguage); + //void ChangeLanguage() + //{ + if (IsMatch(CultureInfo.CurrentUICulture, cultureName)) return; + // CultureInfo.CurrentUICulture = CultureInfo.GetCultureInfo(cultureName); + if (string.IsNullOrWhiteSpace(cultureName)) + AppResources.Culture = null; + else + AppResources.Culture = CultureInfo.GetCultureInfo(Languages.SingleOrDefault(x => x.Key == cultureName).Key); + //AppResources.Culture = CultureInfo.GetCultureInfo(cultureName); + Current.Res = new AppResources(); + Current.RaisePropertyChanged(nameof(Res)); + //} + //MainThreadDesktop.BeginInvokeOnMainThread(ChangeLanguage); } public static string GetCurrentCultureSteamLanguageName() @@ -83,7 +91,5 @@ namespace System.Application.UI.Resx return AppResources.Culture == null ? Languages.First().Value : SteamLanguages[AppResources.Culture.Name]; } - //public AppResources Res => new AppResources(); - public AppResources Res { get; set; } = new AppResources(); } } \ No newline at end of file diff --git a/System.Application.SteamTools.Client.Desktop/UI/ViewModels/MainWindowViewModel.cs b/System.Application.SteamTools.Client.Desktop/UI/ViewModels/MainWindowViewModel.cs index 81ec30657..ad94f02b8 100644 --- a/System.Application.SteamTools.Client.Desktop/UI/ViewModels/MainWindowViewModel.cs +++ b/System.Application.SteamTools.Client.Desktop/UI/ViewModels/MainWindowViewModel.cs @@ -1,4 +1,6 @@ using ReactiveUI; +using System.Application.Models.Settings; +using System.Application.UI.Resx; using System.Collections.Generic; using System.Linq; using System.Properties; @@ -58,8 +60,6 @@ namespace System.Application.UI.ViewModels (this.ASFPage = new ArchiSteamFarmPlusPageViewModel().AddTo(this)), (this.GameRelatedPage = new GameRelatedPageViewModel().AddTo(this)), (this.OtherPlatformPage = new OtherPlatformPageViewModel().AddTo(this)), - - #region SystemTab SettingsPageViewModel.Instance, @@ -72,15 +72,7 @@ namespace System.Application.UI.ViewModels this.SelectedItem = this.TabItems.First(); - if (ViewModelBase.IsInDesignMode) - { - return; - } - Task.Run(Initialize).ContinueWith(s => - { - Log.Error(nameof(MainWindowViewModel), s.Exception, nameof(Initialize) + "Action Error"); - }, TaskContinuationOptions.OnlyOnFaulted) - .ContinueWith(s => s.Dispose()); + Task.Run(Initialize).ForgetAndDispose(); } diff --git a/System.Application.SteamTools.Client.Desktop/UI/ViewModels/Pages/AboutPageViewModel.cs b/System.Application.SteamTools.Client.Desktop/UI/ViewModels/Pages/About/AboutPageViewModel.cs similarity index 100% rename from System.Application.SteamTools.Client.Desktop/UI/ViewModels/Pages/AboutPageViewModel.cs rename to System.Application.SteamTools.Client.Desktop/UI/ViewModels/Pages/About/AboutPageViewModel.cs diff --git a/System.Application.SteamTools.Client.Desktop/UI/ViewModels/Pages/Settings/SettingsPageViewModel.cs b/System.Application.SteamTools.Client.Desktop/UI/ViewModels/Pages/Settings/SettingsPageViewModel.cs index a47793258..cdf1dbbd0 100644 --- a/System.Application.SteamTools.Client.Desktop/UI/ViewModels/Pages/Settings/SettingsPageViewModel.cs +++ b/System.Application.SteamTools.Client.Desktop/UI/ViewModels/Pages/Settings/SettingsPageViewModel.cs @@ -1,4 +1,5 @@ using ReactiveUI; +using System.Application.Models.Settings; using System.Application.UI.Resx; using System.Collections.Generic; using System.Linq; @@ -15,22 +16,20 @@ namespace System.Application.UI.ViewModels protected set { throw new NotImplementedException(); } } + KeyValuePair _SelectLanguage; + public KeyValuePair SelectLanguage + { + get => _SelectLanguage; + protected set => this.RaiseAndSetIfChanged(ref _SelectLanguage, value); + } + public SettingsPageViewModel() { - + SelectLanguage = R.Languages.SingleOrDefault(x => x.Key == UISettings.Language.Value); + if (SelectLanguage.Key == null) + SelectLanguage = R.Languages.First(); + this.WhenAnyValue(x => x.SelectLanguage) + .Subscribe(x => UISettings.Language.Value = x.Key); } - - KeyValuePair _CurrentLanguage = R.Languages.First(); - public KeyValuePair CurrentLanguage - { - get => _CurrentLanguage; - set - { - this.RaiseAndSetIfChanged(ref _CurrentLanguage, value); - R.ChangeLanguage(_CurrentLanguage.Key); - } - } - - } } \ No newline at end of file diff --git a/System.Common.CoreLib/Serializable.cs b/System.Common.CoreLib/Serializable.cs index c2c978c57..aae984114 100644 --- a/System.Common.CoreLib/Serializable.cs +++ b/System.Common.CoreLib/Serializable.cs @@ -1,6 +1,7 @@ using MessagePack; using Newtonsoft.Json; using System.Diagnostics.CodeAnalysis; +using System.IO; using System.Text; using System.Text.Encodings.Web; using System.Threading; @@ -181,6 +182,16 @@ namespace System public static T DMP(byte[] buffer, CancellationToken cancellationToken = default) => MessagePackSerializer.Deserialize(buffer, cancellationToken: cancellationToken); + /// + /// (Deserialize)MessagePack å��åº�列化 + /// + /// + /// + /// + [return: MaybeNull] + public static T DMP(Stream buffer, CancellationToken cancellationToken = default) + => MessagePackSerializer.Deserialize(buffer, cancellationToken: cancellationToken); + /// /// (Deserialize)MessagePack å��åº�列化 + Base64Url Decode /// diff --git a/source/SteamTools/Models/Settings/SettingsHost.cs b/source/SteamTools/Models/Settings/SettingsHost.cs index b45de1b9e..5a4580dfb 100644 --- a/source/SteamTools/Models/Settings/SettingsHost.cs +++ b/source/SteamTools/Models/Settings/SettingsHost.cs @@ -26,8 +26,8 @@ namespace SteamTools.Models.Settings } /// - /// <请å�‚阅cref ="SerializableProperty {T}" />缓存在当å‰�实例中获å�– - /// 如果没有缓存,请根æ�®åˆ›å»ºå®ƒ + /// 请å�‚阅 缓存在当å‰�实例中获å�– + /// 如果没有缓存,请根æ�®åˆ›å»ºå®ƒ /// /// protected SerializableProperty Cache(Func> create, [CallerMemberName] string propertyName = "") diff --git a/source/SteamTools/Views/Pages/SettingsPage.xaml b/source/SteamTools/Views/Pages/SettingsPage.xaml index 9cece7cc9..e1e3178ea 100644 --- a/source/SteamTools/Views/Pages/SettingsPage.xaml +++ b/source/SteamTools/Views/Pages/SettingsPage.xaml @@ -166,7 +166,6 @@ SelectedValue="{Binding Source={x:Static ms:GeneralSettings.Culture}, Path=Value, Mode=TwoWay}" IsReadOnly="True" MinWidth="250" /> -