From b1636d5932e12ce71311189621bd28839cde4221 Mon Sep 17 00:00:00 2001
From: lrc <571244399@qq.com>
Date: Sat, 25 Jul 2026 21:38:19 +0800
Subject: [PATCH] =?UTF-8?q?=E9=87=8D=E6=9E=84=E5=8F=8D=E5=B0=84=E6=9E=84?=
=?UTF-8?q?=E5=BB=BA=E7=BB=84=E4=BB=B6=EF=BC=8C=E6=B7=BB=E5=8A=A0=E6=9D=90?=
=?UTF-8?q?=E8=B4=A8=E5=8F=8D=E5=B0=84=E7=BB=84=E4=BB=B6?=
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit
---
.../Accessors/MaterialPropertyAccessor.cs | 110 +++++++++
.../Accessors/ReflectionValueAccessor.cs | 77 +++++++
.../XuiavrgFieldDropdownValueAttribute.cs | 39 ++++
.../XuiavrgFieldToggleGroupValueAttribute.cs | 39 ++++
.../Attribute/XuiavrgSplitAttribute.cs | 7 +-
.../Scripts/AvrgMemberMetaData.cs | 129 ++++-------
.../Scripts/AvrgMemberMetaDataSet.cs | 89 ++++----
.../Scripts/AvrgMvvmBinding.cs | 7 +-
.../Scripts/Core/IAvrgValueAccessor.cs | 64 ++++++
.../Scripts/XuiFieldTempEntry.cs | 169 +++-----------
.../Scripts/XuiavrgFieldTag.cs | 39 +++-
.../Scripts/XuiavrgUtilities.cs | 209 ++++++++++++++----
.../AvrgValues/XUIAvrgMDColorValue.cs | 58 +++++
.../AvrgValues/XUIAvrgMDDropdownValue.cs | 100 +++++++++
.../AvrgValues/XUIAvrgMDInputFieldValue.cs | 67 ++++++
.../AvrgValues/XUIAvrgMDLabelValue.cs | 57 +++++
.../AvrgValues/XUIAvrgMDSliderValue.cs | 86 +++++++
.../AvrgValues/XUIAvrgMDTextureValue.cs | 63 ++++++
.../AvrgValues/XUIAvrgMDToggleGroupValue.cs | 114 ++++++++++
.../AvrgValues/XUIAvrgMDToggleValue.cs | 52 +++++
.../AvrgValues/XUIAvrgMDVectorValue.cs | 137 ++++++++++++
Runtime/OverrideUI/XUIAvrgMDValue.cs | 72 +++++-
22 files changed, 1444 insertions(+), 340 deletions(-)
create mode 100644 Runtime/InterfaceAttributeReflectionGenerator/Scripts/Accessors/MaterialPropertyAccessor.cs
create mode 100644 Runtime/InterfaceAttributeReflectionGenerator/Scripts/Accessors/ReflectionValueAccessor.cs
create mode 100644 Runtime/InterfaceAttributeReflectionGenerator/Scripts/Attribute/XuiavrgFieldDropdownValueAttribute.cs
create mode 100644 Runtime/InterfaceAttributeReflectionGenerator/Scripts/Attribute/XuiavrgFieldToggleGroupValueAttribute.cs
create mode 100644 Runtime/InterfaceAttributeReflectionGenerator/Scripts/Core/IAvrgValueAccessor.cs
create mode 100644 Runtime/OverrideUI/AvrgValues/XUIAvrgMDColorValue.cs
create mode 100644 Runtime/OverrideUI/AvrgValues/XUIAvrgMDDropdownValue.cs
create mode 100644 Runtime/OverrideUI/AvrgValues/XUIAvrgMDInputFieldValue.cs
create mode 100644 Runtime/OverrideUI/AvrgValues/XUIAvrgMDLabelValue.cs
create mode 100644 Runtime/OverrideUI/AvrgValues/XUIAvrgMDSliderValue.cs
create mode 100644 Runtime/OverrideUI/AvrgValues/XUIAvrgMDTextureValue.cs
create mode 100644 Runtime/OverrideUI/AvrgValues/XUIAvrgMDToggleGroupValue.cs
create mode 100644 Runtime/OverrideUI/AvrgValues/XUIAvrgMDToggleValue.cs
create mode 100644 Runtime/OverrideUI/AvrgValues/XUIAvrgMDVectorValue.cs
diff --git a/Runtime/InterfaceAttributeReflectionGenerator/Scripts/Accessors/MaterialPropertyAccessor.cs b/Runtime/InterfaceAttributeReflectionGenerator/Scripts/Accessors/MaterialPropertyAccessor.cs
new file mode 100644
index 0000000..3f9aaf4
--- /dev/null
+++ b/Runtime/InterfaceAttributeReflectionGenerator/Scripts/Accessors/MaterialPropertyAccessor.cs
@@ -0,0 +1,110 @@
+using System;
+using UnityEngine;
+using UnityEngine.Rendering;
+
+namespace XericUI.ReflectionUIGenerator
+{
+ ///
+ /// 材质属性访问器 — 通过 Material.Get/Set 系列方法读写 Shader 属性
+ ///
+ public class MaterialPropertyAccessor : IAvrgValueAccessor
+ {
+ private readonly Material _material;
+ private readonly string _propName;
+ private readonly ShaderPropertyType _shaderType;
+
+ public Type ValueType { get; }
+ public bool IsReadOnly { get; }
+
+ public MaterialPropertyAccessor(
+ Material material,
+ string propName,
+ ShaderPropertyType shaderType,
+ Type valueType,
+ bool isReadOnly = false)
+ {
+ _material = material;
+ _propName = propName;
+ _shaderType = shaderType;
+ ValueType = valueType;
+ IsReadOnly = isReadOnly;
+ }
+
+ public object GetValue()
+ {
+ if (_material == null) return null;
+
+ return _shaderType switch
+ {
+ ShaderPropertyType.Float or ShaderPropertyType.Range => _material.GetFloat(_propName),
+ ShaderPropertyType.Int => _material.GetInt(_propName),
+ ShaderPropertyType.Color => _material.GetColor(_propName),
+ ShaderPropertyType.Vector => _material.GetVector(_propName),
+ ShaderPropertyType.Texture => _material.GetTexture(_propName),
+ _ => null
+ };
+ }
+
+ public void SetValue(object value)
+ {
+ if (IsReadOnly || _material == null) return;
+
+ switch (_shaderType)
+ {
+ case ShaderPropertyType.Float:
+ case ShaderPropertyType.Range:
+ _material.SetFloat(_propName, Convert.ToSingle(value));
+ break;
+ case ShaderPropertyType.Int:
+ _material.SetInt(_propName, Convert.ToInt32(value));
+ break;
+ case ShaderPropertyType.Color:
+ _material.SetColor(_propName, (Color)value);
+ break;
+ case ShaderPropertyType.Vector:
+ _material.SetVector(_propName, (Vector4)value);
+ break;
+ case ShaderPropertyType.Texture:
+ _material.SetTexture(_propName, value as Texture);
+ break;
+ }
+ }
+
+ public string FormatDisplay(object value)
+ {
+ if (value == null) return "None";
+ if (value is Texture tex && tex != null) return tex.name;
+ return value.ToString();
+ }
+ }
+
+ ///
+ /// 材质属性访问器工厂 — 持有 Shader 属性名和类型,创建时绑定 Material 实例
+ ///
+ public class MaterialPropertyAccessorFactory : IAvrgValueAccessorFactory
+ {
+ private readonly string _propName;
+ private readonly ShaderPropertyType _shaderType;
+ private readonly Type _valueType;
+ private readonly bool _isReadOnly;
+
+ public MaterialPropertyAccessorFactory(
+ string propName,
+ ShaderPropertyType shaderType,
+ Type valueType,
+ bool isReadOnly = false)
+ {
+ _propName = propName;
+ _shaderType = shaderType;
+ _valueType = valueType;
+ _isReadOnly = isReadOnly;
+ }
+
+ public IAvrgValueAccessor CreateAccessor(object targetInstance)
+ {
+ var material = targetInstance as Material;
+ if (material == null) return null;
+ return new MaterialPropertyAccessor(material, _propName, _shaderType, _valueType, _isReadOnly);
+ }
+ }
+}
diff --git a/Runtime/InterfaceAttributeReflectionGenerator/Scripts/Accessors/ReflectionValueAccessor.cs b/Runtime/InterfaceAttributeReflectionGenerator/Scripts/Accessors/ReflectionValueAccessor.cs
new file mode 100644
index 0000000..f4bbc76
--- /dev/null
+++ b/Runtime/InterfaceAttributeReflectionGenerator/Scripts/Accessors/ReflectionValueAccessor.cs
@@ -0,0 +1,77 @@
+using System;
+using System.Reflection;
+
+namespace XericUI.ReflectionUIGenerator
+{
+ ///
+ /// 反射值访问器 — 通过 FieldInfo / PropertyInfo 读写 C# 对象成员
+ ///
+ public class ReflectionValueAccessor : IAvrgValueAccessor
+ {
+ private readonly MemberInfo _memberInfo;
+ private object _targetInstance;
+
+ public Type ValueType { get; }
+ public bool IsReadOnly { get; }
+
+ public ReflectionValueAccessor(MemberInfo memberInfo, object targetInstance)
+ {
+ _memberInfo = memberInfo;
+ _targetInstance = targetInstance;
+
+ ValueType = memberInfo switch
+ {
+ FieldInfo fi => fi.FieldType,
+ PropertyInfo pi => pi.PropertyType,
+ _ => typeof(object)
+ };
+
+ IsReadOnly = memberInfo is PropertyInfo pi && !pi.CanWrite;
+ }
+
+ public object GetValue()
+ {
+ if (_targetInstance == null) return null;
+ return _memberInfo switch
+ {
+ FieldInfo fi => fi.GetValue(_targetInstance),
+ PropertyInfo pi => pi.GetValue(_targetInstance),
+ _ => null
+ };
+ }
+
+ public void SetValue(object value)
+ {
+ if (IsReadOnly || _targetInstance == null) return;
+ switch (_memberInfo)
+ {
+ case FieldInfo fi:
+ fi.SetValue(_targetInstance, value);
+ break;
+ case PropertyInfo pi:
+ pi.SetValue(_targetInstance, value);
+ break;
+ }
+ }
+
+ public string FormatDisplay(object value) => value?.ToString() ?? "";
+ }
+
+ ///
+ /// 反射值访问器工厂 — 持有 MemberInfo,创建时绑定 TargetInstance
+ ///
+ public class ReflectionAccessorFactory : IAvrgValueAccessorFactory
+ {
+ private readonly MemberInfo _memberInfo;
+
+ public ReflectionAccessorFactory(MemberInfo memberInfo)
+ {
+ _memberInfo = memberInfo;
+ }
+
+ public IAvrgValueAccessor CreateAccessor(object targetInstance)
+ {
+ return new ReflectionValueAccessor(_memberInfo, targetInstance);
+ }
+ }
+}
diff --git a/Runtime/InterfaceAttributeReflectionGenerator/Scripts/Attribute/XuiavrgFieldDropdownValueAttribute.cs b/Runtime/InterfaceAttributeReflectionGenerator/Scripts/Attribute/XuiavrgFieldDropdownValueAttribute.cs
new file mode 100644
index 0000000..1df7fce
--- /dev/null
+++ b/Runtime/InterfaceAttributeReflectionGenerator/Scripts/Attribute/XuiavrgFieldDropdownValueAttribute.cs
@@ -0,0 +1,39 @@
+using System;
+
+namespace XericUI.ReflectionUIGenerator
+{
+ ///
+ /// 为 Dropdown UI 提供预设选项
+ ///
+ /// 使用示例:
+ /// [XuiavrgFieldDropdownValue("选项A", "选项B", "选项C", MergeMode = PresetOptionsMergeMode.Union)]
+ /// public string quality = "选项A";
+ ///
+ ///
+ [AttributeUsage(AttributeTargets.Field | AttributeTargets.Property, AllowMultiple = false)]
+ public class XuiavrgFieldDropdownValueAttribute : Attribute, IXuiavrgAttribute
+ {
+ /// 预设选项数组
+ public readonly string[] Options;
+
+ /// 与已有选项(如 enum 名)的合并模式
+ public PresetOptionsMergeMode MergeMode { get; set; } = PresetOptionsMergeMode.Replace;
+
+ public XuiavrgFieldDropdownValueAttribute(params string[] options)
+ {
+ Options = options ?? Array.Empty();
+ }
+
+ public void BindMetaData(AvrgMemberMetaData metaData)
+ {
+ if (metaData == null) return;
+
+ metaData.PresetOptions = Options;
+ metaData.OptionsMergeMode = MergeMode;
+
+ // 自动设置 Tag 为 Dropdown
+ if (metaData.Tag == XuiavrgFieldTag.None)
+ metaData.Tag = XuiavrgFieldTag.Dropdown;
+ }
+ }
+}
diff --git a/Runtime/InterfaceAttributeReflectionGenerator/Scripts/Attribute/XuiavrgFieldToggleGroupValueAttribute.cs b/Runtime/InterfaceAttributeReflectionGenerator/Scripts/Attribute/XuiavrgFieldToggleGroupValueAttribute.cs
new file mode 100644
index 0000000..2affa9e
--- /dev/null
+++ b/Runtime/InterfaceAttributeReflectionGenerator/Scripts/Attribute/XuiavrgFieldToggleGroupValueAttribute.cs
@@ -0,0 +1,39 @@
+using System;
+
+namespace XericUI.ReflectionUIGenerator
+{
+ ///
+ /// 为 ToggleGroup UI 提供预设选项
+ ///
+ /// 使用示例:
+ /// [XuiavrgFieldToggleGroupValue("低", "中", "高", MergeMode = PresetOptionsMergeMode.Replace)]
+ /// public string detailLevel = "中";
+ ///
+ ///
+ [AttributeUsage(AttributeTargets.Field | AttributeTargets.Property, AllowMultiple = false)]
+ public class XuiavrgFieldToggleGroupValueAttribute : Attribute, IXuiavrgAttribute
+ {
+ /// 预设选项数组
+ public readonly string[] Options;
+
+ /// 与已有选项(如 enum 名)的合并模式
+ public PresetOptionsMergeMode MergeMode { get; set; } = PresetOptionsMergeMode.Replace;
+
+ public XuiavrgFieldToggleGroupValueAttribute(params string[] options)
+ {
+ Options = options ?? Array.Empty();
+ }
+
+ public void BindMetaData(AvrgMemberMetaData metaData)
+ {
+ if (metaData == null) return;
+
+ metaData.PresetOptions = Options;
+ metaData.OptionsMergeMode = MergeMode;
+
+ // 自动设置 Tag 为 ToggleGroup
+ if (metaData.Tag == XuiavrgFieldTag.None)
+ metaData.Tag = XuiavrgFieldTag.ToggleGroup;
+ }
+ }
+}
diff --git a/Runtime/InterfaceAttributeReflectionGenerator/Scripts/Attribute/XuiavrgSplitAttribute.cs b/Runtime/InterfaceAttributeReflectionGenerator/Scripts/Attribute/XuiavrgSplitAttribute.cs
index 79e7eb6..9e5bcce 100644
--- a/Runtime/InterfaceAttributeReflectionGenerator/Scripts/Attribute/XuiavrgSplitAttribute.cs
+++ b/Runtime/InterfaceAttributeReflectionGenerator/Scripts/Attribute/XuiavrgSplitAttribute.cs
@@ -8,12 +8,11 @@ namespace XericUI.ReflectionUIGenerator
/// 将一个复合类型(如 Vector2)拆分为多个子属性,分别绑定到不同的 UI 元素
///
/// 使用示例:
- /// [XuiavrgFieldEntryGroup("speed")]
/// [XuiavrgSplit("x", "最小")]
/// [XuiavrgSplit("y", "最大")]
/// public Vector2 speedRange = new Vector2(0, 100);
///
- /// 上述代码创建一个 "speed" 编组的 Entry,内部包含两个子绑定:
+ /// 上述代码创建一个编组 Entry,内部包含两个子元数据:
/// speedRange.x 绑定到 UI 的"最小"输入,speedRange.y 绑定到"最大"输入
/// 子绑定通过 XUIAvrgMDValue.BindingSlots 与预制体上的 UI 组件匹配
///
@@ -39,13 +38,12 @@ namespace XericUI.ReflectionUIGenerator
public void BindMetaData(AvrgMemberMetaData metaData)
{
- // 创建子元数据
+ // 创建子元数据 — 纯元数据,不持有实例
var subMetaData = new AvrgMemberMetaData
{
MemberName = $"{metaData.MemberName}.{PropertyPath}",
PropertyPath = PropertyPath,
LabelName = DisplayLabel ?? PropertyPath,
- TargetInstance = null, // 由运行时闭包解析
Hide = metaData.Hide,
Required = metaData.Required,
FieldOrder = metaData.FieldOrder,
@@ -74,7 +72,6 @@ namespace XericUI.ReflectionUIGenerator
if (parentType == null || string.IsNullOrEmpty(propertyPath))
return null;
- // 仅支持单层路径
if (propertyPath.Contains('/'))
return null;
diff --git a/Runtime/InterfaceAttributeReflectionGenerator/Scripts/AvrgMemberMetaData.cs b/Runtime/InterfaceAttributeReflectionGenerator/Scripts/AvrgMemberMetaData.cs
index 1e667fd..6e10eaa 100644
--- a/Runtime/InterfaceAttributeReflectionGenerator/Scripts/AvrgMemberMetaData.cs
+++ b/Runtime/InterfaceAttributeReflectionGenerator/Scripts/AvrgMemberMetaData.cs
@@ -1,57 +1,36 @@
using System;
using System.Collections.Generic;
-using System.Reflection;
namespace XericUI.ReflectionUIGenerator
{
///
/// 单个成员的元数据数据结构
- /// 记录从反射 + Attribute 标记中提取的所有信息
+ /// 纯数据描述 — 不持有反射信息(MemberInfo)和实例引用(TargetInstance)
+ ///
+ /// 数据的实际读写通过 IAvrgValueAccessorFactory 在 BuildMDVM 阶段创建的 IAvrgValueAccessor 完成。
+ /// 这使得同一个元数据模板可复用于 C# 反射对象、Material Shader 属性等不同数据源。
///
public class AvrgMemberMetaData
{
- // ============ 反射信息 ============
+ // ============ 基本标识 ============
- /// 字段/属性名
+ /// 字段/属性名(或 Shader 属性名如 "_MainTex")
public string MemberName { get; set; }
- /// 反射信息 (FieldInfo 或 PropertyInfo)
- public MemberInfo MemberInfo { get; set; }
-
- /// 值类型
- public Type ValueType { get; set; }
-
- /// 是否为属性 (false 为字段)
- public bool IsProperty { get; set; }
-
- /// 绑定的目标实例 (可选,Step1 时可空)
- public object TargetInstance { get; set; }
-
- // ============ 拆分属性支持 ============
-
- ///
- /// 子属性路径(仅拆分属性有效)
- /// 用于从父值中访问子字段/属性,如 "x"、"y"、"width"
- ///
- public string PropertyPath { get; set; }
-
- ///
- /// 拆分子元数据列表
- /// 由 [XuiavrgSplit] 特性填充,每个子元数据对应一个拆分属性
- ///
- public List SubMetaDataList { get; set; }
-
- // ============ 从 IXuiavrgAttribute 融合的元数据 ============
-
- /// 显示标签
+ /// UI 显示标签
public string LabelName { get; set; }
- /// 单位文本
+ /// 单位文本(如 "m/s")
public string UnitLabelName { get; set; }
/// 格式化字符串(如 "{0:F2}")
public string FormatText { get; set; }
+ /// 值类型
+ public Type ValueType { get; set; }
+
+ // ============ UI 映射 ============
+
/// 功能标记
public XuiavrgFieldTag Tag { get; set; } = XuiavrgFieldTag.None;
@@ -67,21 +46,12 @@ namespace XericUI.ReflectionUIGenerator
/// 是否在 UI 中隐藏
public bool Hide { get; set; }
- /// 值变更时触发的反射调用方法名
- public string OnTriggerCall { get; set; }
+ // ============ 默认值与验证 ============
- ///
- /// 类的初始默认值(由类字段初始化器决定)
- /// 例如 public float speed = 10f; → DefaultValue = 10f
- ///
+ /// 默认值(用于 UI 初始化和回退参考)
public object DefaultValue { get; set; }
- // ============ 属性检查 (由 [PropertyCheck] 填充) ============
-
- ///
- /// 判断依据标识
- /// 默认使用 ValueType.FullName 与 AvrgValueRangeConfig 中的条目匹配
- ///
+ /// 属性检查依据标识(默认使用 ValueType.FullName)
public string PropertyCheckKey { get; set; }
/// 是否必填
@@ -99,47 +69,46 @@ namespace XericUI.ReflectionUIGenerator
/// 是否启用无效值状态检查
public bool ValidateInvalid { get; set; }
- // ============ 原始 Attribute 引用 ============
+ /// Shader Range 属性下限(null 表示非 Range 属性)
+ public float? RangeMin { get; set; }
+
+ /// Shader Range 属性上限(null 表示非 Range 属性)
+ public float? RangeMax { get; set; }
+
+ // ============ 拆分属性支持 ============
///
- /// 收集到的所有 IXuiavrgAttribute 列表
- /// 保留原始引用,供扩展和自定义处理使用
+ /// 子属性路径(仅拆分属性有效)
+ /// 用于从父值中访问子字段/属性,如 "x"、"y"、"width"
///
+ public string PropertyPath { get; set; }
+
+ ///
+ /// 拆分子元数据列表
+ /// 由 [XuiavrgSplit] 特性填充
+ ///
+ public List SubMetaDataList { get; set; }
+
+ // ============ 预设选项 (Dropdown/ToggleGroup) ============
+
+ /// 预设选项列表
+ public IReadOnlyList PresetOptions { get; set; }
+
+ /// 预设选项合并模式
+ public PresetOptionsMergeMode OptionsMergeMode { get; set; }
+
+ // ============ 扩展 ============
+
+ /// 收集到的所有 IXuiavrgAttribute 列表
public List Attributes { get; set; } = new List();
- ///
- /// 从绑定目标获取值
- ///
- public object GetValue()
- {
- if (TargetInstance == null) return null;
- switch (MemberInfo)
- {
- case PropertyInfo pi:
- return pi.GetValue(TargetInstance);
- case FieldInfo fi:
- return fi.GetValue(TargetInstance);
- default:
- return null;
- }
- }
+ // ============ ★ 核心变更 — 值访问器工厂 ============
///
- /// 设置值到绑定目标
+ /// 值访问器工厂
+ /// 在元数据提取阶段设置(持有 MemberInfo 或 Shader 属性信息)
+ /// 在 BuildMDVM 阶段调用 CreateAccessor(targetInstance) 创建真正的读写通道
///
- public void SetValue(object value)
- {
- if (TargetInstance == null) return;
- switch (MemberInfo)
- {
- case PropertyInfo pi:
- if (pi.CanWrite)
- pi.SetValue(TargetInstance, value);
- break;
- case FieldInfo fi:
- fi.SetValue(TargetInstance, value);
- break;
- }
- }
+ public IAvrgValueAccessorFactory AccessorFactory { get; set; }
}
}
diff --git a/Runtime/InterfaceAttributeReflectionGenerator/Scripts/AvrgMemberMetaDataSet.cs b/Runtime/InterfaceAttributeReflectionGenerator/Scripts/AvrgMemberMetaDataSet.cs
index 8e174ae..56be430 100644
--- a/Runtime/InterfaceAttributeReflectionGenerator/Scripts/AvrgMemberMetaDataSet.cs
+++ b/Runtime/InterfaceAttributeReflectionGenerator/Scripts/AvrgMemberMetaDataSet.cs
@@ -8,7 +8,7 @@ namespace XericUI.ReflectionUIGenerator
{
///
/// 元数据集
- /// 包含从对象反射提取的完整元数据,以及用于生成 MVVM 绑定的 BuildMDVM() 方法
+ /// 包含从对象/材质反射提取的完整元数据,以及用于生成 MVVM 绑定的 BuildMDVM() 方法
///
public class AvrgMemberMetaDataSet
{
@@ -33,7 +33,6 @@ namespace XericUI.ReflectionUIGenerator
///
internal void MergeGroupEntries()
{
- // 提取编组和非编组
var grouped = _metaDataList
.Where(m => !string.IsNullOrEmpty(m.GroupPath))
.GroupBy(m => m.GroupPath)
@@ -47,9 +46,7 @@ namespace XericUI.ReflectionUIGenerator
foreach (var gEntry in grouped)
{
- // 编组内第一个成员作为主元数据
var first = gEntry.First();
- // 收集组内所有成员的 SubMetaDataList(合并拆分属性)
foreach (var member in gEntry)
{
if (member.SubMetaDataList?.Count > 0)
@@ -62,9 +59,7 @@ namespace XericUI.ReflectionUIGenerator
merged.Add(first);
}
- // 非编组直接追加
merged.AddRange(nonGrouped);
-
_metaDataList = merged;
}
@@ -82,9 +77,10 @@ namespace XericUI.ReflectionUIGenerator
}
///
- /// 为元数据生成对应的 MVVM 值绑定模板
+ /// 为元数据生成对应的 MVVM 值绑定
+ /// 通过 AccessorFactory 创建 IAvrgValueAccessor 完成数据读写
///
- /// 可选的业务目标对象(如果元数据中没有绑定目标,则在此传入)
+ /// 目标实例(C# 对象、Material 等)
/// 可选的冲突解决配置
/// 绑定集合
public AvrgMvvmBindingCollection BuildMDVM(
@@ -92,30 +88,46 @@ namespace XericUI.ReflectionUIGenerator
AvrgMvvmResolverConfig resolverConfig = null)
{
var bindingCollection = new AvrgMvvmBindingCollection();
+ bindingCollection.SourceMetaDataSet = this;
var scheduler = new AvrgMvvmScheduler(resolverConfig);
foreach (var metaData in _metaDataList)
{
- // 确定目标实例
- var target = metaData.TargetInstance ?? businessTarget;
- if (target == null)
+ if (metaData.AccessorFactory == null) continue;
+ if (businessTarget == null)
+ {
+ Debug.LogWarning(
+ $"[Xuiavrg] 元数据 '{metaData.MemberName}' 缺少 targetInstance,跳过绑定");
continue;
+ }
+
+ var accessor = metaData.AccessorFactory.CreateAccessor(businessTarget);
+ if (accessor == null) continue;
// 创建主绑定
var binding = new AvrgMvvmBinding
{
MetaData = metaData,
- ReadFunc = () => metaData.GetValue(),
+ ReadFunc = () => accessor.GetValue(),
+ Accessor = accessor,
};
var capturedBinding = binding;
- binding.WriteAction = (val) =>
- {
- metaData.SetValue(val);
- scheduler.MarkDataDirty(capturedBinding);
- };
- // 如果元数据有拆分子属性,创建子绑定
+ if (accessor.IsReadOnly)
+ {
+ binding.WriteAction = null;
+ }
+ else
+ {
+ binding.WriteAction = (val) =>
+ {
+ accessor.SetValue(val);
+ scheduler.MarkDataDirty(capturedBinding);
+ };
+ }
+
+ // 拆分属性子绑定
if (metaData.SubMetaDataList?.Count > 0)
{
binding.SubBindings = new List();
@@ -128,14 +140,12 @@ namespace XericUI.ReflectionUIGenerator
var subBinding = new AvrgMvvmBinding
{
MetaData = subMetaData,
- // 从父值中读取子属性
- ReadFunc = () => ReadSubProperty(metaData.GetValue(), subPath),
- // 修改子属性并写回父值
+ ReadFunc = () => ReadSubProperty(accessor.GetValue(), subPath),
WriteAction = (val) =>
{
- var parentVal = metaData.GetValue();
+ var parentVal = accessor.GetValue();
var modifiedParent = WriteSubProperty(parentVal, subPath, val);
- metaData.SetValue(modifiedParent);
+ accessor.SetValue(modifiedParent);
scheduler.MarkDataDirty(capturedBinding);
},
};
@@ -155,16 +165,11 @@ namespace XericUI.ReflectionUIGenerator
// ============ 子属性访问辅助方法 ============
- ///
- /// 从父值中读取子属性(支持单层路径如 "x"、"y")
- /// 例如:ReadSubProperty(Vector2(1,2), "x") → 1f
- ///
private static object ReadSubProperty(object parentValue, string propertyPath)
{
if (parentValue == null || string.IsNullOrEmpty(propertyPath))
return null;
- // 仅支持单层路径
if (propertyPath.Contains('/'))
{
Debug.LogWarning($"[Xuiavrg] 暂不支持多层嵌套路径 '{propertyPath}'");
@@ -182,10 +187,6 @@ namespace XericUI.ReflectionUIGenerator
return null;
}
- ///
- /// 修改父值中的子属性并返回修改后的父值
- /// 适用于值类型(struct)和引用类型
- ///
private static object WriteSubProperty(object parentValue, string propertyPath, object value)
{
if (parentValue == null || string.IsNullOrEmpty(propertyPath))
@@ -221,8 +222,6 @@ namespace XericUI.ReflectionUIGenerator
///
/// 对当前元数据集执行属性检查验证
///
- /// 值范围配置(可选,不传入则不执行范围检查)
- /// 验证结果集合
public AvrgValidationResultSet Validate(AvrgValueRangeConfig valueRangeConfig = null)
{
var resultSet = new AvrgValidationResultSet();
@@ -232,7 +231,6 @@ namespace XericUI.ReflectionUIGenerator
if (metaData.Hide)
continue;
- // 只有标记了 PropertyCheck 的才验证
if (!metaData.Required && !metaData.ValidateReasonable
&& !metaData.ValidateWarning && !metaData.ValidateInvalid)
{
@@ -240,13 +238,13 @@ namespace XericUI.ReflectionUIGenerator
{
MetaData = metaData,
Result = AvrgMemberCheckResult.Skipped,
- CurrentValueString = metaData.GetValue()?.ToString(),
+ CurrentValueString = metaData.DefaultValue?.ToString(),
Message = "未标记属性检查",
});
continue;
}
- var value = metaData.GetValue();
+ var value = metaData.DefaultValue;
var checkResult = CheckSingleMember(metaData, value, valueRangeConfig);
resultSet.Results.Add(checkResult);
}
@@ -254,9 +252,6 @@ namespace XericUI.ReflectionUIGenerator
return resultSet;
}
- ///
- /// 验证单个成员
- ///
private AvrgMemberValidationResult CheckSingleMember(
AvrgMemberMetaData metaData,
object value,
@@ -268,7 +263,6 @@ namespace XericUI.ReflectionUIGenerator
CurrentValueString = value?.ToString() ?? "(null)",
};
- // 根据 CheckKey 查找值范围规则
AvrgValueRangeEntry rangeEntry = null;
string checkKey = !string.IsNullOrEmpty(metaData.PropertyCheckKey)
? metaData.PropertyCheckKey
@@ -279,22 +273,19 @@ namespace XericUI.ReflectionUIGenerator
rangeEntry = rangeConfig.FindEntry(checkKey);
}
- // ——— 优先级 1: 空值检查 (Required) ———
+ // 优先级 1: 空值检查 (Required)
if (metaData.Required)
{
bool isEmpty = false;
- // 引用类型 null
if (value == null)
{
isEmpty = true;
}
- // 字符串 null/空
else if (value is string str)
{
isEmpty = string.IsNullOrEmpty(str);
}
- // 使用 ValueRangeEntry 的 IsEmpty 判断
else if (rangeEntry != null && !rangeEntry.skipEmptyCheck)
{
isEmpty = rangeEntry.IsEmpty(value);
@@ -308,7 +299,6 @@ namespace XericUI.ReflectionUIGenerator
}
}
- // 如果没有范围配置,无法继续检查
if (rangeEntry == null)
{
result.Result = AvrgMemberCheckResult.Valid;
@@ -316,7 +306,7 @@ namespace XericUI.ReflectionUIGenerator
return result;
}
- // ——— 优先级 2: 合理值检查(最高优先级,命中即通过) ———
+ // 优先级 2: 合理值检查
if (metaData.ValidateReasonable && rangeEntry.IsReasonable(value))
{
result.Result = AvrgMemberCheckResult.Valid;
@@ -324,7 +314,7 @@ namespace XericUI.ReflectionUIGenerator
return result;
}
- // ——— 优先级 3: 无效值检查 ———
+ // 优先级 3: 无效值检查
if (metaData.ValidateInvalid && rangeEntry.IsInvalid(value))
{
result.Result = AvrgMemberCheckResult.Invalid;
@@ -332,7 +322,7 @@ namespace XericUI.ReflectionUIGenerator
return result;
}
- // ——— 优先级 4: 警告值检查 ———
+ // 优先级 4: 警告值检查
if (metaData.ValidateWarning && rangeEntry.IsWarning(value))
{
result.Result = AvrgMemberCheckResult.Warning;
@@ -340,7 +330,6 @@ namespace XericUI.ReflectionUIGenerator
return result;
}
- // ——— 最终: 通过 ———
result.Result = AvrgMemberCheckResult.Valid;
result.Message = "通过";
return result;
diff --git a/Runtime/InterfaceAttributeReflectionGenerator/Scripts/AvrgMvvmBinding.cs b/Runtime/InterfaceAttributeReflectionGenerator/Scripts/AvrgMvvmBinding.cs
index 970b894..c8faa0f 100644
--- a/Runtime/InterfaceAttributeReflectionGenerator/Scripts/AvrgMvvmBinding.cs
+++ b/Runtime/InterfaceAttributeReflectionGenerator/Scripts/AvrgMvvmBinding.cs
@@ -1,6 +1,5 @@
using System;
using System.Collections.Generic;
-using System.Reflection;
namespace XericUI.ReflectionUIGenerator
{
@@ -20,6 +19,12 @@ namespace XericUI.ReflectionUIGenerator
/// 写入值到数据源的委托
public Action