VPet.Solution 实装反射工具

This commit is contained in:
Hakoyu 2024-01-10 21:37:17 +08:00
parent 2993c5d5eb
commit 6aff72bf91
8 changed files with 258 additions and 61 deletions

View File

@ -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<GraphicsSettingModel>
{
#region
private double _zoomLevel = 1;
/// <summary>
@ -17,6 +19,24 @@ public class GraphicsSettingModel : ObservableClass<GraphicsSettingModel>
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;
/// <summary>
@ -37,7 +57,14 @@ public class GraphicsSettingModel : ObservableClass<GraphicsSettingModel>
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<GraphicsSettingModel>
/// <summary>
/// 设置中桌宠启动的位置
/// </summary>
[ReflectionPropertyInfo(typeof(ObservablePointToPointConverter))]
public ObservablePoint StartRecordPoint
{
get => _startRecordPoint;

View File

@ -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<SettingModel>
private Setting _setting;
private ReflectionOptions _saveReflectionOptions = new() { CheckValueEquals = true };
public SettingModel(Setting setting)
{
_setting = setting;
@ -68,21 +71,9 @@ public class SettingModel : ObservableClass<SettingModel>
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<SettingModel>
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);
}
}

View File

@ -31,12 +31,6 @@ public class ObservablePoint : ObservableClass<ObservablePoint>, IEquatable<Obse
Y = y;
}
public ObservablePoint(Point point)
{
X = point.X;
Y = point.Y;
}
/// <summary>
/// 复制一个新的对象
/// </summary>
@ -88,6 +82,19 @@ public class ObservablePoint : ObservableClass<ObservablePoint>, IEquatable<Obse
#endregion
}
public class ObservablePointToPointConverter : ReflectionConverterBase<ObservablePoint, Point>
{
public override Point Convert(ObservablePoint sourceValue)
{
return new(sourceValue.X, sourceValue.Y);
}
public override ObservablePoint ConvertBack(Point targetValue)
{
return new(targetValue.X, targetValue.Y);
}
}
///// <summary>
///// 可观察地点
///// </summary>

View File

@ -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
{
/// <summary>
/// 目标名称
/// <para>
/// (TargetType, (PropertyName, TargetPropertyName))
/// </para>
/// </summary>
private static readonly Dictionary<Type, ReflectionObjectInfo> _typePropertyReflectionInfos =
new();
public static void SetValue<TSource, TTarget>(
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<ReflectionPropertyInfoAttribute>();
if (string.IsNullOrWhiteSpace(reflectionInfo.PropertyName))
reflectionInfo.PropertyName = property.Name;
objectInfo.PropertyInfos[property.Name] = reflectionInfo;
}
}
return objectInfo;
}
}
/// <summary>
/// 反射对象信息
/// </summary>
public class ReflectionObjectInfo
{
public HashSet<string> PropertyNames { get; }
public Dictionary<string, ReflectionPropertyInfoAttribute> PropertyInfos { get; } = new();
public ReflectionObjectInfo(Type type)
{
PropertyNames = new(type.GetProperties().Select(p => p.Name));
}
}
/// <summary>
/// 反射属性信息
/// </summary>
public class ReflectionPropertyInfoAttribute : Attribute
{
/// <summary>
/// 目标属性名称
/// </summary>
public string PropertyName { get; set; }
/// <summary>
/// 反射转换器
/// </summary>
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();
}
}
/// <summary>
/// 反射设置
/// </summary>
public class ReflectionOptions
{
/// <summary>
/// 检查值是否相等, 若相等则跳过赋值
/// </summary>
[DefaultValue(false)]
public bool CheckValueEquals { get; set; } = false;
}
/// <summary>
/// 反射转换器
/// </summary>
public interface IReflectionConverter
{
public object Convert(object sourceValue);
public object ConvertBack(object targetValue);
}
/// <summary>
/// 反射转换器
/// </summary>
/// <typeparam name="TSource">源值类型</typeparam>
/// <typeparam name="TTarget">目标值类型</typeparam>
public abstract class ReflectionConverterBase<TSource, TTarget> : 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);
}
}

View File

@ -101,6 +101,7 @@
<Compile Include="Utils\ClearFocus.cs" />
<Compile Include="Utils\ElementHelper.cs" />
<Compile Include="Utils\FindTopParent.cs" />
<Compile Include="Utils\ReflectionUtils.cs" />
<Compile Include="ViewModels\MainWindowVM.cs" />
<Compile Include="ViewModels\SettingEditor\CustomizedSettingPageVM.cs" />
<Compile Include="ViewModels\SettingEditor\DiagnosticSettingPageVM.cs" />

View File

@ -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<SettingWindowVM>
#endregion
#region Command
public ObservableCommand ResetSettingCommand { get; } = new();
public ObservableCommand<SettingModel> ResetSettingCommand { get; } = new();
public ObservableCommand<SettingModel> SaveSettingCommand { get; } = new();
public ObservableCommand SaveAllSettingCommand { get; } = new();
#endregion
@ -62,6 +63,13 @@ public class SettingWindowVM : ObservableClass<SettingWindowVM>
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<SettingWindowVM>
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);
}

View File

@ -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}" />
</Grid>

View File

@ -49,20 +49,16 @@
Command="{Binding PlacementTarget.Tag.SaveSettingCommand, RelativeSource={RelativeSource AncestorType=ContextMenu}}"
CommandParameter="{Binding PlacementTarget.DataContext, RelativeSource={RelativeSource AncestorType=ContextMenu}}"
Header="{ll:Str 保存}" />
<MenuItem
Command="{Binding PlacementTarget.Tag.ResetSettingCommand, RelativeSource={RelativeSource AncestorType=ContextMenu}}"
CommandParameter="{Binding PlacementTarget.DataContext, RelativeSource={RelativeSource AncestorType=ContextMenu}}"
Header="{ll:Str 重置}" />
</ContextMenu>
</Setter.Value>
</Setter>
</Style>
</ListBox.ItemContainerStyle>
</ListBox>
<Button
Grid.Row="2"
Margin="0"
HorizontalAlignment="Stretch"
Command="{Binding ResetSettingCommand}"
Content="{ll:Str 重置选中的设置}"
IsEnabled="{Binding SelectedItem, ElementName=ListBox_Saves, Converter={StaticResource NullToFalse}}"
Style="{DynamicResource Button_BaseStyle}" />
<Button
Grid.Row="3"
Margin="0"
@ -71,7 +67,7 @@
Content="{ll:Str 全部保存}"
Style="{DynamicResource Button_BaseStyle}" />
</Grid>
<Grid Grid.Column="1">
<Grid Grid.Column="1" IsEnabled="{Binding SelectedItem, ElementName=ListBox_Saves, Converter={StaticResource NullToFalse}}">
<Grid.RowDefinitions>
<RowDefinition Height="Auto" />
<RowDefinition />