diff --git a/VPet.Solution/Models/SettingEditor/GraphicsSettingModel.cs b/VPet.Solution/Models/SettingEditor/GraphicsSettingModel.cs index 9e67d3b..983c2ab 100644 --- a/VPet.Solution/Models/SettingEditor/GraphicsSettingModel.cs +++ b/VPet.Solution/Models/SettingEditor/GraphicsSettingModel.cs @@ -1,10 +1,12 @@ -using System.ComponentModel; +using HKW.HKWUtils.Observable; +using System.ComponentModel; using System.Windows; namespace VPet.Solution.Models.SettingEditor; public class GraphicsSettingModel : ObservableClass { + #region private double _zoomLevel = 1; /// @@ -17,6 +19,24 @@ public class GraphicsSettingModel : ObservableClass set => SetProperty(ref _zoomLevel, value); } + private double _zoomLevelMinimum = 0.5; + + [DefaultValue(0.5)] + public double ZoomLevelMinimum + { + get => _zoomLevelMinimum; + set => SetProperty(ref _zoomLevelMinimum, value); + } + + private double _zoomLevelMaximum = 3; + + [DefaultValue(3)] + public double ZoomLevelMaximum + { + get => _zoomLevelMaximum; + set => SetProperty(ref _zoomLevelMaximum, value); + } + #endregion private int _resolution = 1000; /// @@ -37,7 +57,14 @@ public class GraphicsSettingModel : ObservableClass public bool IsBiggerScreen { get => _isBiggerScreen; - set => SetProperty(ref _isBiggerScreen, value); + set + { + SetProperty(ref _isBiggerScreen, value); + if (value is true) + ZoomLevelMaximum = 8; + else + ZoomLevelMaximum = 3; + } } private bool _topMost; @@ -143,6 +170,7 @@ public class GraphicsSettingModel : ObservableClass /// /// 设置中桌宠启动的位置 /// + [ReflectionPropertyInfo(typeof(ObservablePointToPointConverter))] public ObservablePoint StartRecordPoint { get => _startRecordPoint; diff --git a/VPet.Solution/Models/SettingEditor/SettingModel.cs b/VPet.Solution/Models/SettingEditor/SettingModel.cs index 77d85e5..1dcf46f 100644 --- a/VPet.Solution/Models/SettingEditor/SettingModel.cs +++ b/VPet.Solution/Models/SettingEditor/SettingModel.cs @@ -2,6 +2,7 @@ using HKW.HKWUtils.Observable; using LinePutScript; using System.ComponentModel; +using System.Reflection; using System.Runtime.CompilerServices; using System.Windows; using VPet.Solution.Properties; @@ -60,6 +61,8 @@ public class SettingModel : ObservableClass private Setting _setting; + private ReflectionOptions _saveReflectionOptions = new() { CheckValueEquals = true }; + public SettingModel(Setting setting) { _setting = setting; @@ -68,21 +71,9 @@ public class SettingModel : ObservableClass private GraphicsSettingModel LoadGraphicsSettings() { - var graphicsSettings = new GraphicsSettingModel(); - var sourceAccessor = ObjectAccessor.Create(_setting); - var targetAccessor = ObjectAccessor.Create(graphicsSettings); - foreach (var property in typeof(GraphicsSettingModel).GetProperties()) - { - if (sourceAccessor[property.Name] is Point point) - { - targetAccessor[property.Name] = new ObservablePoint(point); - } - else - { - targetAccessor[property.Name] = sourceAccessor[property.Name]; - } - } - return graphicsSettings; + var graphicsSetting = new GraphicsSettingModel(); + ReflectionUtils.SetValue(_setting, graphicsSetting); + return graphicsSetting; } public void Save() @@ -93,24 +84,6 @@ public class SettingModel : ObservableClass private void SaveGraphicsSettings() { - var sourceAccessor = ObjectAccessor.Create(GraphicsSetting); - var targetAccessor = ObjectAccessor.Create(_setting); - foreach (var property in typeof(GraphicsSettingModel).GetProperties()) - { - //if (_settingProperties.Contains(property.Name) is false) - // continue; - var sourceValue = sourceAccessor[property.Name]; - var targetValue = targetAccessor[property.Name]; - if (sourceValue.Equals(targetValue)) - continue; - if (sourceValue is ObservablePoint point) - { - targetAccessor[property.Name] = point.ToPoint(); - } - else - { - targetAccessor[property.Name] = sourceValue; - } - } + ReflectionUtils.SetValue(GraphicsSetting, _setting, _saveReflectionOptions); } } diff --git a/VPet.Solution/Utils/ObservablePoint.cs b/VPet.Solution/Utils/ObservablePoint.cs index f1ed5d2..4b886bc 100644 --- a/VPet.Solution/Utils/ObservablePoint.cs +++ b/VPet.Solution/Utils/ObservablePoint.cs @@ -31,12 +31,6 @@ public class ObservablePoint : ObservableClass, IEquatable /// 复制一个新的对象 /// @@ -88,6 +82,19 @@ public class ObservablePoint : ObservableClass, IEquatable +{ + public override Point Convert(ObservablePoint sourceValue) + { + return new(sourceValue.X, sourceValue.Y); + } + + public override ObservablePoint ConvertBack(Point targetValue) + { + return new(targetValue.X, targetValue.Y); + } +} + ///// ///// 可观察地点 ///// diff --git a/VPet.Solution/Utils/ReflectionUtils.cs b/VPet.Solution/Utils/ReflectionUtils.cs new file mode 100644 index 0000000..5358bb4 --- /dev/null +++ b/VPet.Solution/Utils/ReflectionUtils.cs @@ -0,0 +1,180 @@ +using FastMember; +using System; +using System.Collections.Generic; +using System.ComponentModel; +using System.Linq; +using System.Reflection; +using System.Text; +using System.Threading.Tasks; + +namespace HKW.HKWUtils; + +public static class ReflectionUtils +{ + /// + /// 目标名称 + /// + /// (TargetType, (PropertyName, TargetPropertyName)) + /// + /// + private static readonly Dictionary _typePropertyReflectionInfos = + new(); + + public static void SetValue( + TSource source, + TTarget target, + ReflectionOptions options = null! + ) + { + options ??= new(); + var sourceType = typeof(TSource); + var targetType = typeof(TTarget); + if (_typePropertyReflectionInfos.TryGetValue(sourceType, out var sourceInfo) is false) + sourceInfo = _typePropertyReflectionInfos[sourceType] = GetReflectionObjectInfo( + sourceType + ); + if (_typePropertyReflectionInfos.TryGetValue(targetType, out var targetInfo) is false) + targetInfo = _typePropertyReflectionInfos[targetType] = GetReflectionObjectInfo( + targetType + ); + + var sourceAccessor = ObjectAccessor.Create(source); + var targetAccessor = ObjectAccessor.Create(target); + + foreach (var property in sourceType.GetProperties()) + { + // 获取源属性名 + var sourcePropertyName = sourceInfo.PropertyInfos.TryGetValue( + property.Name, + out var sourceReflectionInfo + ) + ? sourceReflectionInfo.PropertyName + : property.Name; + if (targetInfo.PropertyNames.Contains(sourcePropertyName) is false) + continue; + // 获取目标属性名 + var targetPropertyName = targetInfo.PropertyInfos.TryGetValue( + sourcePropertyName, + out var targetReflectionInfo + ) + ? targetReflectionInfo.PropertyName + : property.Name; + // 获取源值 + var sourceValue = sourceAccessor[sourcePropertyName]; + // 转换源值 + if (sourceReflectionInfo?.Converter is IReflectionConverter sourceConverter) + sourceValue = sourceConverter.Convert(sourceValue); + else if (targetReflectionInfo?.Converter is IReflectionConverter targetConverter) + sourceValue = targetConverter.ConvertBack(sourceValue); + // 比较源值和目标值 + if (options.CheckValueEquals) + { + var targetValue = targetAccessor[targetPropertyName]; + if (sourceValue.Equals(targetValue)) + continue; + } + targetAccessor[targetPropertyName] = sourceValue; + } + } + + private static ReflectionObjectInfo GetReflectionObjectInfo(Type type) + { + var objectInfo = new ReflectionObjectInfo(type); + foreach (var property in type.GetProperties()) + { + if (property.IsDefined(typeof(ReflectionPropertyInfoAttribute))) + { + var reflectionInfo = property.GetCustomAttribute(); + if (string.IsNullOrWhiteSpace(reflectionInfo.PropertyName)) + reflectionInfo.PropertyName = property.Name; + objectInfo.PropertyInfos[property.Name] = reflectionInfo; + } + } + return objectInfo; + } +} + +/// +/// 反射对象信息 +/// +public class ReflectionObjectInfo +{ + public HashSet PropertyNames { get; } + + public Dictionary PropertyInfos { get; } = new(); + + public ReflectionObjectInfo(Type type) + { + PropertyNames = new(type.GetProperties().Select(p => p.Name)); + } +} + +/// +/// 反射属性信息 +/// +public class ReflectionPropertyInfoAttribute : Attribute +{ + /// + /// 目标属性名称 + /// + public string PropertyName { get; set; } + + /// + /// 反射转换器 + /// + public IReflectionConverter? Converter { get; } = null; + + public ReflectionPropertyInfoAttribute(Type converterType) + : this(string.Empty, converterType) { } + + public ReflectionPropertyInfoAttribute(string name, Type? converterType = null) + { + PropertyName = name; + if (converterType is null) + return; + Converter = (IReflectionConverter)TypeAccessor.Create(converterType).CreateNew(); + } +} + +/// +/// 反射设置 +/// +public class ReflectionOptions +{ + /// + /// 检查值是否相等, 若相等则跳过赋值 + /// + [DefaultValue(false)] + public bool CheckValueEquals { get; set; } = false; +} + +/// +/// 反射转换器 +/// +public interface IReflectionConverter +{ + public object Convert(object sourceValue); + public object ConvertBack(object targetValue); +} + +/// +/// 反射转换器 +/// +/// 源值类型 +/// 目标值类型 +public abstract class ReflectionConverterBase : IReflectionConverter +{ + public abstract TTarget Convert(TSource sourceValue); + + public abstract TSource ConvertBack(TTarget targetValue); + + object IReflectionConverter.Convert(object sourceValue) + { + return Convert((TSource)sourceValue); + } + + object IReflectionConverter.ConvertBack(object targetValue) + { + return ConvertBack((TTarget)targetValue); + } +} diff --git a/VPet.Solution/VPet.Solution.csproj b/VPet.Solution/VPet.Solution.csproj index 425c9ce..3ecc9ff 100644 --- a/VPet.Solution/VPet.Solution.csproj +++ b/VPet.Solution/VPet.Solution.csproj @@ -101,6 +101,7 @@ + diff --git a/VPet.Solution/ViewModels/SettingEditor/SettingWindowVM.cs b/VPet.Solution/ViewModels/SettingEditor/SettingWindowVM.cs index 3b35d92..5e81faf 100644 --- a/VPet.Solution/ViewModels/SettingEditor/SettingWindowVM.cs +++ b/VPet.Solution/ViewModels/SettingEditor/SettingWindowVM.cs @@ -1,5 +1,6 @@ using HKW.HKWUtils.Observable; using LinePutScript; +using LinePutScript.Localization.WPF; using Panuon.WPF.UI; using System; using System.Collections.Generic; @@ -47,7 +48,7 @@ public class SettingWindowVM : ObservableClass #endregion #region Command - public ObservableCommand ResetSettingCommand { get; } = new(); + public ObservableCommand ResetSettingCommand { get; } = new(); public ObservableCommand SaveSettingCommand { get; } = new(); public ObservableCommand SaveAllSettingCommand { get; } = new(); #endregion @@ -62,6 +63,13 @@ public class SettingWindowVM : ObservableClass PropertyChanged += MainWindowVM_PropertyChanged; ResetSettingCommand.ExecuteCommand += ResetSettingCommand_ExecuteCommand; SaveSettingCommand.ExecuteCommand += SaveSettingCommand_ExecuteCommand; + SaveAllSettingCommand.ExecuteCommand += SaveAllSettingCommand_ExecuteCommand; + } + + private void SaveAllSettingCommand_ExecuteCommand() + { + foreach (var setting in _settings) + setting.Save(); } private void SaveSettingCommand_ExecuteCommand(SettingModel parameter) @@ -69,22 +77,26 @@ public class SettingWindowVM : ObservableClass parameter.Save(); } - private void ResetSettingCommand_ExecuteCommand() + private void ResetSettingCommand_ExecuteCommand(SettingModel parameter) { if ( - MessageBoxX.Show( + MessageBox.Show( SettingWindow.Instance, - "确定重置吗", + "确定重置设置吗\n名称: {0}\n路径: {1}".Translate(parameter.Name, parameter.FilePath), "", MessageBoxButton.YesNo, - MessageBoxIcon.Warning + MessageBoxImage.Warning ) is not MessageBoxResult.Yes ) return; CurrentSetting = _settings[_settings.IndexOf(CurrentSetting)] = new SettingModel( new Setting("") - ); + ) + { + Name = CurrentSetting.Name, + FilePath = CurrentSetting.FilePath + }; RefreshShowSettings(SearchSetting); } diff --git a/VPet.Solution/Views/SettingEditor/GraphicsSettingPage.xaml b/VPet.Solution/Views/SettingEditor/GraphicsSettingPage.xaml index 250a6fb..f58d0de 100644 --- a/VPet.Solution/Views/SettingEditor/GraphicsSettingPage.xaml +++ b/VPet.Solution/Views/SettingEditor/GraphicsSettingPage.xaml @@ -107,8 +107,8 @@ x:Name="Slider_Zoom" Grid.Column="1" LargeChange="0.1" - Maximum="3" - Minimum="0.5" + Maximum="{Binding GraphicsSetting.ZoomLevelMaximum}" + Minimum="{Binding GraphicsSetting.ZoomLevelMinimum}" SmallChange="0.05" Style="{DynamicResource Slider_BaseStyle}" TickFrequency="0.05" @@ -117,8 +117,8 @@ Grid.Column="2" Foreground="{DynamicResource DARKPrimaryDarker}" Interval="0.1" - Maximum="3" - Minimum="0.5" + Maximum="{Binding GraphicsSetting.ZoomLevelMaximum}" + Minimum="{Binding GraphicsSetting.ZoomLevelMinimum}" Style="{DynamicResource NumberInput_BaseStyle}" Value="{Binding Value, ElementName=Slider_Zoom}" /> diff --git a/VPet.Solution/Views/SettingEditor/SettingWindow.xaml b/VPet.Solution/Views/SettingEditor/SettingWindow.xaml index 989afa4..3af0e6a 100644 --- a/VPet.Solution/Views/SettingEditor/SettingWindow.xaml +++ b/VPet.Solution/Views/SettingEditor/SettingWindow.xaml @@ -49,20 +49,16 @@ Command="{Binding PlacementTarget.Tag.SaveSettingCommand, RelativeSource={RelativeSource AncestorType=ContextMenu}}" CommandParameter="{Binding PlacementTarget.DataContext, RelativeSource={RelativeSource AncestorType=ContextMenu}}" Header="{ll:Str 保存}" /> + -