308 lines
12 KiB
C#
308 lines
12 KiB
C#
using System;
|
||
using System.Collections.Generic;
|
||
using System.Diagnostics.CodeAnalysis;
|
||
using System.Linq;
|
||
using System.Reflection;
|
||
using System.Runtime.CompilerServices;
|
||
using System.Text;
|
||
using System.Threading.Tasks;
|
||
using System.Windows.Markup;
|
||
|
||
namespace Deedy.Activity
|
||
{
|
||
public static partial class Helper
|
||
{
|
||
/// <summary>
|
||
/// 查找指定类型的祖辈元素
|
||
/// </summary>
|
||
/// <typeparam name="T">要查找的祖非类型</typeparam>
|
||
/// <param name="element">查找的起点</param>
|
||
/// <param name="level">查找的层级,默认1级</param>
|
||
/// <returns>如果找到根元素也未找到则返回「null」</returns>
|
||
public static T? FindAncestorElement<T>(this IElement element, int level = 1) where T : IElement
|
||
{
|
||
if (element == null || level < 1) return default;
|
||
|
||
var parent = element.ParentElement;
|
||
int currentLevel = 0;
|
||
|
||
while (parent != null && currentLevel < level)
|
||
{
|
||
if (parent is T foundParent)
|
||
{
|
||
// 每次找到一个约定类型的袓辈节点计数需要加1
|
||
currentLevel++;
|
||
if (currentLevel >= level)
|
||
return foundParent;
|
||
}
|
||
else parent = parent.ParentElement;
|
||
}
|
||
|
||
return default;
|
||
}
|
||
/// <summary>
|
||
/// 设置「IElement」对象的「DepthLevel」属性值
|
||
/// </summary>
|
||
/// <param name="element">要设置的对象</param>
|
||
/// <param name="depthLevel">层级深度</param>
|
||
/// <returns>如果设置失败则返回「False」</returns>
|
||
public static bool Help_Setter_DepthLevel(this IElement element, int depthLevel)
|
||
{
|
||
if (element is BasalAction action)
|
||
{
|
||
ActionSetter_DepthLevel(action, depthLevel);
|
||
return true;
|
||
}
|
||
else
|
||
{
|
||
try
|
||
{
|
||
element.SetNamedPropertyValue(nameof(IElement.DepthLevel), depthLevel);
|
||
return true;
|
||
}
|
||
catch { return false; }
|
||
}
|
||
}
|
||
[UnsafeAccessor(UnsafeAccessorKind.Method, Name = "set_DepthLevel")]
|
||
private static extern void ActionSetter_DepthLevel(BasalAction action, int value);
|
||
|
||
/// <summary>
|
||
/// 设置「IElement」对象的「ParentElement」属性值
|
||
/// </summary>
|
||
/// <param name="element">要设置的对象</param>
|
||
/// <param name="parent">属性值</param>
|
||
/// <returns>如果设置失败则返回「False」</returns>
|
||
public static bool Help_Setter_ParentElement(this IElement element, IElement? parent)
|
||
{
|
||
if (element is BasalAction action)
|
||
{
|
||
ActionSetter_ParentElement(action, parent);
|
||
return true;
|
||
}
|
||
else
|
||
{
|
||
try
|
||
{
|
||
element.SetNamedPropertyValue(nameof(IElement.ParentElement), parent);
|
||
return true;
|
||
}
|
||
catch
|
||
{
|
||
return false;
|
||
}
|
||
}
|
||
}
|
||
[UnsafeAccessor(UnsafeAccessorKind.Method, Name = "set_ParentElement")]
|
||
private static extern void ActionSetter_ParentElement(BasalAction action, IElement? value);
|
||
/// <summary>
|
||
/// 帮助一个IElement节点构建参数映射表
|
||
/// </summary>
|
||
/// <param name="element">需要构建参数映射表的Element节点</param>
|
||
/// <param name="output">消息输出器</param>
|
||
public static void Help_BuildParamMapping(this IElement element, Output? output = null)
|
||
{
|
||
if (element == null) return;
|
||
if (element.ParamsMapping == null) element.ParamsMapping = new();
|
||
var props = element.GetType().GetProperties(BindingFlags.Instance | BindingFlags.Public | BindingFlags.GetProperty | BindingFlags.SetProperty);
|
||
foreach (var prop in props)
|
||
{
|
||
if (prop.CanWrite && prop.CanRead)
|
||
{
|
||
var pda = prop.GetCustomAttribute<ParamDefineAttribute>();
|
||
if (pda != null)
|
||
{
|
||
pda.Name = prop.Name;
|
||
if (element.ParamsMapping.TryGetParamDefine(prop.Name, out var value)) pda = value;
|
||
else element.ParamsMapping.Add(pda);
|
||
value.Target = element;
|
||
value.Property = prop;
|
||
}
|
||
}
|
||
}
|
||
}
|
||
/// <summary>
|
||
/// 构建一个元素的参数分组,为设计器准备的辅助方法
|
||
/// </summary>
|
||
/// <param name="element"></param>
|
||
/// <param name="output"></param>
|
||
/// <returns></returns>
|
||
public static ParamGroupCollection Help_BuildParamGroup(this IElement element, Output? output = null)
|
||
{
|
||
ParamGroupCollection result = new();
|
||
if (element != null)
|
||
{
|
||
result.AddFromType(element.GetType());
|
||
result.AppendParamRange(element.ParamsMapping);
|
||
}
|
||
return result;
|
||
}
|
||
/// <summary>
|
||
/// 构建结构树,设置层级:并且完成参数映射的刷新
|
||
/// </summary>
|
||
/// <param name="parent">父级元素</param>
|
||
public static void Help_Element_ReadyToWorking(this IElement @this, IElement? parent = null, Output? output = null)
|
||
{
|
||
@this.LinkTo(parent);
|
||
if (@this is ICombinedElement combined)
|
||
{
|
||
combined.Help_CombineElements();
|
||
foreach (IElement subElement in combined.Elements)
|
||
subElement.ReadyToWorking(@this);
|
||
}
|
||
}
|
||
public static void Help_Element_ReadyToEditing(this IElement @this, Runtime runtime)
|
||
{
|
||
if (@this is null) return;
|
||
@this.SetNamedPropertyValue<Runtime?>(nameof(IActivity.Runtime), runtime);
|
||
if (@this is ICombinedElement combined)
|
||
{
|
||
combined.Help_BuildParamMapping();
|
||
foreach (var subElement in combined.Elements)
|
||
subElement.ReadyToEditing(runtime);
|
||
}
|
||
}
|
||
/// <summary>
|
||
/// 将当前节点链接到一个节点上
|
||
/// </summary>
|
||
/// <param name="element">要链接到的节点</param>
|
||
public static void Help_LinkTo(this IElement @this, [AllowNull] IElement element)
|
||
{
|
||
if (@this == null) return;
|
||
|
||
if (element == null)
|
||
{
|
||
@this.Unlink();
|
||
return;
|
||
}
|
||
if (@this.ParentElement != null)
|
||
{
|
||
if (@this.ParentElement == element) return;
|
||
else
|
||
{
|
||
if (@this.ParentElement is IContainerElement container)
|
||
{
|
||
//this.SetNamedPropertyValue(nameof(ParentElement), element);
|
||
@this.Help_Setter_ParentElement(element);
|
||
}
|
||
}
|
||
}
|
||
else
|
||
{
|
||
//this.SetNamedPropertyValue(nameof(ParentElement), element);
|
||
@this.Help_Setter_ParentElement(element);
|
||
}
|
||
//this.SetNamedPropertyValue(nameof(DepthLevel), (this.ParentElement?.DepthLevel ?? 0) + 1);
|
||
@this.Help_Setter_DepthLevel((@this.ParentElement?.DepthLevel ?? 0) + 1);
|
||
}
|
||
/// <summary>
|
||
/// 断开自身与父级节点的链接关系
|
||
/// </summary>
|
||
public static void Help_Unlink(this IElement @this)
|
||
{
|
||
if (@this == null) return;
|
||
|
||
//this.SetNamedPropertyValue<IElement?>(nameof(ParentElement), null);
|
||
@this.Help_Setter_ParentElement(null);
|
||
//this.SetNamedPropertyValue(nameof(DepthLevel), 0);
|
||
@this.Help_Setter_DepthLevel(0);
|
||
}
|
||
/// <summary>
|
||
/// 克隆一个「IElement」节点
|
||
/// </summary>
|
||
/// <param name="this">要复制的节点</param>
|
||
/// <returns>如果复制失败则返回「null」</returns>
|
||
public static IElement? Clone(this IElement @this)
|
||
{
|
||
if (@this == null) return null;
|
||
try
|
||
{
|
||
return XamlReader.Parse(XamlWriter.Save(@this)) as IElement;
|
||
}
|
||
catch { return null; }
|
||
}
|
||
/// <summary>
|
||
/// 尝试克隆一个「IElement」节点
|
||
/// </summary>
|
||
/// <param name="this">要克隆的节点</param>
|
||
/// <param name="element">克隆后的结果</param>
|
||
/// <returns>如果克隆失败则返回「False」</returns>
|
||
public static bool TryClone(this IElement @this, out IElement element)
|
||
{
|
||
#pragma warning disable CS8601 // 引用类型赋值可能为 null。
|
||
element = @this.Clone();
|
||
#pragma warning restore CS8601 // 引用类型赋值可能为 null。
|
||
return element is not null;
|
||
}
|
||
/// <summary>
|
||
/// 尝试将一个「IElement」元素序列化为一个字符串形式档案
|
||
/// </summary>
|
||
/// <param name="this">要序列化的元素</param>
|
||
/// <param name="document">序列化后的文本文档</param>
|
||
/// <returns>如果出错则返回「Flase」</returns>
|
||
public static bool TryEncode(this IElement @this, out string document)
|
||
{
|
||
bool result = false;
|
||
try
|
||
{
|
||
document = XamlWriter.Save(@this);
|
||
result = true;
|
||
}
|
||
catch { document = string.Empty; }
|
||
return result;
|
||
}
|
||
/// <summary>
|
||
/// 尝试将一个字符串「档案」解析为「IElement」对象
|
||
/// </summary>
|
||
/// <param name="document">要解析的「档案」</param>
|
||
/// <param name="element">解析后的对象</param>
|
||
/// <returns>如果解析失败则返回「False」</returns>
|
||
public static bool TryDecode<TReturn>(this string document, out TReturn element) where TReturn : IElement
|
||
{
|
||
#pragma warning disable CS8600 // 将 null 字面量或可能为 null 的值转换为非 null 类型。
|
||
TReturn instance = default;
|
||
#pragma warning restore CS8600 // 将 null 字面量或可能为 null 的值转换为非 null 类型。
|
||
bool result = false;
|
||
try
|
||
{
|
||
if (XamlReader.Parse(document) is TReturn @return)
|
||
{
|
||
instance = @return;
|
||
result = true;
|
||
}
|
||
}
|
||
catch { }
|
||
#pragma warning disable CS8601 // 引用类型赋值可能为 null。
|
||
element = instance;
|
||
#pragma warning restore CS8601 // 引用类型赋值可能为 null。
|
||
return result;
|
||
}
|
||
/// <summary>
|
||
/// 尝试将一个字符串「档案」解析为「IElement」对象
|
||
/// </summary>
|
||
/// <param name="document">要解析的「档案」</param>
|
||
/// <param name="element">解析后的对象</param>
|
||
/// <returns>如果解析失败则返回「False」</returns>
|
||
public static bool TryDecode(this string document, out IElement element)
|
||
{
|
||
|
||
#pragma warning disable CS8600 // 将 null 字面量或可能为 null 的值转换为非 null 类型。
|
||
IElement instance = null;
|
||
#pragma warning restore CS8600 // 将 null 字面量或可能为 null 的值转换为非 null 类型。
|
||
bool result = false;
|
||
try
|
||
{
|
||
if (XamlReader.Parse(document) is IElement @return)
|
||
{
|
||
instance = @return;
|
||
result = true;
|
||
}
|
||
}
|
||
catch { }
|
||
#pragma warning disable CS8601 // 引用类型赋值可能为 null。
|
||
element = instance;
|
||
#pragma warning restore CS8601 // 引用类型赋值可能为 null。
|
||
return result;
|
||
}
|
||
}
|
||
}
|