mirror of
https://github.com/LorisYounger/VPet.git
synced 2024-08-30 18:42:36 +00:00
VPet.Solution
实装反射工具
This commit is contained in:
parent
2993c5d5eb
commit
6aff72bf91
@ -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;
|
||||
|
@ -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);
|
||||
}
|
||||
}
|
||||
|
@ -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>
|
||||
|
180
VPet.Solution/Utils/ReflectionUtils.cs
Normal file
180
VPet.Solution/Utils/ReflectionUtils.cs
Normal 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);
|
||||
}
|
||||
}
|
@ -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" />
|
||||
|
@ -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);
|
||||
}
|
||||
|
||||
|
@ -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>
|
||||
|
@ -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 />
|
||||
|
Loading…
Reference in New Issue
Block a user