Files
serein-flow/Workbench/Temp/XamlScript.cs
2026-01-27 17:35:09 +08:00

614 lines
26 KiB
C#
Raw Permalink Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
using Serein.Script;
using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Diagnostics;
using System.Linq;
using System.Reflection;
using System.Text;
using System.Threading.Tasks;
using System.Windows.Data;
using System.Windows.Media;
using System.Windows;
using System.Configuration;
using System.ComponentModel.DataAnnotations;
using System.Windows.Markup;
using System.Reactive;
namespace Serein.Workbench.Temp
{
[ContentProperty(nameof(XamlScript.Content))]
public class XamlScript : FrameworkElement
{
/// <summary>
/// 脚本节点内容
/// </summary>
public string? Content { get; set; }
public override string ToString() => Content ?? string.Empty;
}
public enum XamlScriptTriggerType
{
/// <summary>
/// 初始化时触发
/// </summary>
Loaded,
/// <summary>
/// 属性变更触发
/// </summary>
Property,
/// <summary>
/// 指定事件触发
/// </summary>
Event,
/// <summary>
/// 获得/失去焦点触发
/// </summary>
Focus
}
/*public enum FindPriorityType
{
/// <summary>
/// 不查找
/// </summary>
None,
/// <summary>
/// 视觉优先
/// </summary>
Visual,
/// <summary>
/// 逻辑优先
/// </summary>
Logical,
/// <summary>
/// 仅视觉
/// </summary>
VisualOnly,
/// <summary>
/// 仅逻辑
/// </summary>
LogicalOnly,
}*/
public static class ScriptMethod
{
public static void debug(object value)
{
Debug.WriteLine(value);
}
}
public static class XScript
{
static XScript()
{
LoadType(typeof(Serein.Library.ScriptBaseFunc));
LoadType(typeof(ScriptMethod));
}
private static void LoadType(Type type)
{
// 获取方法
var tempMethods = type.GetMethods().Where(method =>
method.IsStatic &&
!(method.Name.Equals("GetHashCode")
|| method.Name.Equals("Equals")
|| method.Name.Equals("ToString")
|| method.Name.Equals("GetType")
)).Select(method => (method.Name, method)).ToArray();
// 挂在方法
foreach ((string name, MethodInfo method) item in tempMethods)
{
SereinScript.AddStaticFunction(item.name, item.method);
}
}
#region
public static readonly DependencyProperty XamlScriptProperty =
DependencyProperty.RegisterAttached(
"XamlScript",
typeof(XamlScript),
typeof(XScript),
new PropertyMetadata(null, OnXamlScriptChanged));
/// <summary>触发类型</summary>
public static readonly DependencyProperty TriggerTypeProperty =
DependencyProperty.RegisterAttached(
"TriggerType",
typeof(XamlScriptTriggerType),
typeof(XScript),
new PropertyMetadata(XamlScriptTriggerType.Loaded));
/// <summary>触发属性名称</summary>
public static readonly DependencyProperty TriggerNameProperty =
DependencyProperty.RegisterAttached(
"TriggerName",
typeof(string),
typeof(XScript),
new PropertyMetadata(""));
/// <summary>脚本返回结果,可用于外部绑定</summary>
public static readonly DependencyProperty ReturnProperty =
DependencyProperty.RegisterAttached(
"Return",
typeof(object),
typeof(XScript),
new FrameworkPropertyMetadata(null, FrameworkPropertyMetadataOptions.BindsTwoWayByDefault));
/// <summary>查找DataContent来源类型</summary>
public static readonly DependencyProperty FindTypeProperty =
DependencyProperty.RegisterAttached("FindType",
typeof(Type),
typeof(XScript),
new PropertyMetadata(null));
/*/// <summary>查找优先级</summary>
public static readonly DependencyProperty FindPriorityProperty =
DependencyProperty.RegisterAttached(
"FindPriority",
typeof(FindPriorityType),
typeof(XScript),
new PropertyMetadata(FindPriorityType.Visual));*/
/* /// <summary>是否使用</summary>
public static readonly DependencyProperty UseDataContextProperty =
DependencyProperty.RegisterAttached(
"UseDataContext",
typeof(bool),
typeof(XScript),
new PropertyMetadata(true));*/
public static void SetFindType(DependencyObject element, Type value) => element.SetValue(FindTypeProperty, value);
public static Type GetFindType(DependencyObject element) => (Type)element.GetValue(FindTypeProperty);
/* public static void SetFindPriority(DependencyObject element, FindPriorityType value) => element.SetValue(FindPriorityProperty, value);
public static FindPriorityType GetFindPriority(DependencyObject element) => (FindPriorityType)element.GetValue(FindPriorityProperty);*/
public static void SetXamlScript(DependencyObject element, XamlScript value) => element.SetValue(XamlScriptProperty, value);
public static XamlScript GetXamlScript(DependencyObject element) => (XamlScript)element.GetValue(XamlScriptProperty);
public static void SetTriggerType(DependencyObject element, XamlScriptTriggerType value) => element.SetValue(TriggerTypeProperty, value);
public static XamlScriptTriggerType GetTriggerType(DependencyObject element) => (XamlScriptTriggerType)element.GetValue(TriggerTypeProperty);
public static void SetTriggerName(DependencyObject element, string value) => element.SetValue(TriggerNameProperty, value);
public static string GetTriggerName(DependencyObject element) => (string)element.GetValue(TriggerNameProperty);
public static void SetReturn(DependencyObject element, object value) => element.SetValue(ReturnProperty, value);
public static object GetReturn(DependencyObject element) => element.GetValue(ReturnProperty);
/* public static void SetUseDataContext(DependencyObject element, bool value) => element.SetValue(UseDataContextProperty, value);
public static bool GetUseDataContext(DependencyObject element) => (bool)element.GetValue(UseDataContextProperty);*/
public static readonly DependencyProperty ParamData1Property = DependencyProperty.RegisterAttached("ParamData1", typeof(object), typeof(XScript), new PropertyMetadata(Unit.Default));
public static readonly DependencyProperty ParamData2Property = DependencyProperty.RegisterAttached("ParamData2", typeof(object), typeof(XScript), new PropertyMetadata(Unit.Default));
public static readonly DependencyProperty ParamData3Property = DependencyProperty.RegisterAttached("ParamData3", typeof(object), typeof(XScript), new PropertyMetadata(Unit.Default));
public static readonly DependencyProperty ParamData4Property = DependencyProperty.RegisterAttached("ParamData4", typeof(object), typeof(XScript), new PropertyMetadata(Unit.Default));
public static readonly DependencyProperty ParamData5Property = DependencyProperty.RegisterAttached("ParamData5", typeof(object), typeof(XScript), new PropertyMetadata(Unit.Default));
public static readonly DependencyProperty ParamData6Property = DependencyProperty.RegisterAttached("ParamData6", typeof(object), typeof(XScript), new PropertyMetadata(Unit.Default));
public static readonly DependencyProperty ParamData7Property = DependencyProperty.RegisterAttached("ParamData7", typeof(object), typeof(XScript), new PropertyMetadata(Unit.Default));
public static readonly DependencyProperty ParamData8Property = DependencyProperty.RegisterAttached("ParamData8", typeof(object), typeof(XScript), new PropertyMetadata(Unit.Default));
public static void SetParamData1(DependencyObject element, object value) => element.SetValue(ParamData1Property, value); public static object GetParamData1(DependencyObject element) => element.GetValue(ParamData1Property);
public static void SetParamData2(DependencyObject element, object value) => element.SetValue(ParamData2Property, value); public static object GetParamData2(DependencyObject element) => element.GetValue(ParamData2Property);
public static void SetParamData3(DependencyObject element, object value) => element.SetValue(ParamData3Property, value); public static object GetParamData3(DependencyObject element) => element.GetValue(ParamData3Property);
public static void SetParamData4(DependencyObject element, object value) => element.SetValue(ParamData4Property, value); public static object GetParamData4(DependencyObject element) => element.GetValue(ParamData4Property);
public static void SetParamData5(DependencyObject element, object value) => element.SetValue(ParamData5Property, value); public static object GetParamData5(DependencyObject element) => element.GetValue(ParamData5Property);
public static void SetParamData6(DependencyObject element, object value) => element.SetValue(ParamData6Property, value); public static object GetParamData6(DependencyObject element) => element.GetValue(ParamData6Property);
public static void SetParamData7(DependencyObject element, object value) => element.SetValue(ParamData7Property, value); public static object GetParamData7(DependencyObject element) => element.GetValue(ParamData7Property);
public static void SetParamData8(DependencyObject element, object value) => element.SetValue(ParamData8Property, value); public static object GetParamData8(DependencyObject element) => element.GetValue(ParamData8Property);
public static readonly DependencyProperty ParamName1Property = DependencyProperty.RegisterAttached("ParamName1", typeof(string), typeof(XScript), new PropertyMetadata(""));
public static readonly DependencyProperty ParamName2Property = DependencyProperty.RegisterAttached("ParamName2", typeof(string), typeof(XScript), new PropertyMetadata(""));
public static readonly DependencyProperty ParamName3Property = DependencyProperty.RegisterAttached("ParamName3", typeof(string), typeof(XScript), new PropertyMetadata(""));
public static readonly DependencyProperty ParamName4Property = DependencyProperty.RegisterAttached("ParamName4", typeof(string), typeof(XScript), new PropertyMetadata(""));
public static readonly DependencyProperty ParamName5Property = DependencyProperty.RegisterAttached("ParamName5", typeof(string), typeof(XScript), new PropertyMetadata(""));
public static readonly DependencyProperty ParamName6Property = DependencyProperty.RegisterAttached("ParamName6", typeof(string), typeof(XScript), new PropertyMetadata(""));
public static readonly DependencyProperty ParamName7Property = DependencyProperty.RegisterAttached("ParamName7", typeof(string), typeof(XScript), new PropertyMetadata(""));
public static readonly DependencyProperty ParamName8Property = DependencyProperty.RegisterAttached("ParamName8", typeof(string), typeof(XScript), new PropertyMetadata(""));
public static void SetParamName1(DependencyObject element, string value) => element.SetValue(ParamName1Property, value); public static string GetParamName1(DependencyObject element) => (string)element.GetValue(ParamName1Property);
public static void SetParamName2(DependencyObject element, string value) => element.SetValue(ParamName2Property, value); public static string GetParamName2(DependencyObject element) => (string)element.GetValue(ParamName2Property);
public static void SetParamName3(DependencyObject element, string value) => element.SetValue(ParamName3Property, value); public static string GetParamName3(DependencyObject element) => (string)element.GetValue(ParamName3Property);
public static void SetParamName4(DependencyObject element, string value) => element.SetValue(ParamName4Property, value); public static string GetParamName4(DependencyObject element) => (string)element.GetValue(ParamName4Property);
public static void SetParamName5(DependencyObject element, string value) => element.SetValue(ParamName5Property, value); public static string GetParamName5(DependencyObject element) => (string)element.GetValue(ParamName5Property);
public static void SetParamName6(DependencyObject element, string value) => element.SetValue(ParamName6Property, value); public static string GetParamName6(DependencyObject element) => (string)element.GetValue(ParamName6Property);
public static void SetParamName7(DependencyObject element, string value) => element.SetValue(ParamName7Property, value); public static string GetParamName7(DependencyObject element) => (string)element.GetValue(ParamName7Property);
public static void SetParamName8(DependencyObject element, string value) => element.SetValue(ParamName8Property, value); public static string GetParamName8(DependencyObject element) => (string)element.GetValue(ParamName8Property);
#endregion
//private static void OnDataContentChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
//{
// if (d is not FrameworkElement fe)
// return;
// if (e.NewValue is not Xaml xaml)
// return;
// BindLetsToDataContent(fe, xaml);
//}
private static void OnXamlScriptChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
{
if (d is not FrameworkElement fe)
return;
//if (e.NewValue is not Xaml xaml)
// return;
// 绑定上下文
//BindLetsToContext(fe, xaml);
var trigger = GetTriggerType(fe);
fe.Loaded += (_, _) =>
{
switch (trigger)
{
case XamlScriptTriggerType.Focus:
fe.GotFocus += (_, _) => RunScript(fe);
fe.LostFocus += (_, _) => RunScript(fe);
break;
case XamlScriptTriggerType.Property:
AttachPropertyTrigger(fe);
break;
case XamlScriptTriggerType.Event:
HookEvent(fe);
break;
}
// 动态绑定 Let 到 DataContent
//BindLetsToDataContent(fe, xaml);
if (GetTriggerType(fe) == XamlScriptTriggerType.Loaded)
RunScript(fe);
};
}
/*private static void BindLetsToDataContent(FrameworkElement fe, Xaml xaml)
{
if (fe == null || xaml?.Lets == null) return;
// 获取 DataContent
var dataContext = GetDataContent(fe);
if (dataContext is null)
{
dataContext = fe.DataContext;
}
if (dataContext is null)
{
return;
}
foreach (var let in xaml.Lets)
{
if (let == null) continue;
var bindingExpr = BindingOperations.GetBindingExpression(let, XamlLet.ValueProperty);
if (bindingExpr != null)
{
// 取原始绑定
var oldBinding = bindingExpr.ParentBinding;
// 创建新绑定,复制原绑定设置
var newBinding = new Binding
{
Path = oldBinding.Path,
Mode = oldBinding.Mode,
UpdateSourceTrigger = oldBinding.UpdateSourceTrigger,
Converter = oldBinding.Converter,
ConverterParameter = oldBinding.ConverterParameter,
ConverterCulture = oldBinding.ConverterCulture,
Source = dataContext // ? 新数据源
};
// 重新应用
BindingOperations.SetBinding(fe, XamlLet.ValueProperty, newBinding);
var data = fe.GetValue(XamlLet.ValueProperty);
}
}
}
*/
#region
private static void AttachPropertyTrigger(FrameworkElement fe)
{
var propName = GetTriggerName(fe);
if (string.IsNullOrWhiteSpace(propName))
return;
DependencyPropertyDescriptor dpd =
DependencyPropertyDescriptor.FromName(propName, fe.GetType(), fe.GetType());
if (dpd != null)
{
dpd.AddValueChanged(fe, (_, _) => RunScript(fe));
}
else
{
// 非 DependencyProperty监听普通属性
var prop = fe.GetType().GetProperty(propName);
if (prop != null)
{
if (fe is INotifyPropertyChanged notifier)
{
notifier.PropertyChanged += (s, ev) =>
{
if (ev.PropertyName == propName)
RunScript(fe);
};
}
}
}
}
private static void HookEvent(FrameworkElement fe)
{
var eventName = GetTriggerName(fe);
if (string.IsNullOrEmpty(eventName))
{
return;
}
var evt = fe.GetType().GetEvent(eventName, BindingFlags.Instance | BindingFlags.Public);
if (evt == null)
{
Debug.WriteLine($"找不到事件 {eventName} 于 {fe.GetType().Name}");
return;
}
// 动态创建一个与事件签名匹配的委托
var handlerMethod = typeof(XScript).GetMethod(nameof(OnEventRaised),
BindingFlags.NonPublic | BindingFlags.Static);
if (handlerMethod == null)
return;
var handler = Delegate.CreateDelegate(evt.EventHandlerType, handlerMethod);
evt.AddEventHandler(fe, handler);
// 将 fe 和 xaml 映射保存,方便在回调时取出上下文
//_eventContext[fe] = xaml;
}
//private static readonly Dictionary<object, object> _eventContext = new();
private static void OnEventRaised(object sender, EventArgs e)
{
if (sender is FrameworkElement fe)
{
//&& _eventContext.TryGetValue(fe, out var xaml)
RunScript(fe);
}
}
private static async void RunScript(FrameworkElement fe)
{
try
{
var result = await ExecuteScriptAsync(fe);
fe.SetValue(ReturnProperty, result);
}
catch (Exception ex)
{
Debug.WriteLine(ex.ToString());
}
}
private static (string?, object?) GetGetParamFunc(FrameworkElement fe, int index)
{
Func<DependencyObject, string> getNamefunc = index switch
{
1 => GetParamName1,
2 => GetParamName2,
3 => GetParamName3,
4 => GetParamName4,
5 => GetParamName5,
6 => GetParamName6,
7 => GetParamName7,
8 => GetParamName8,
_ => throw new NotImplementedException()
};
Func<DependencyObject, object> getDatafunc = index switch
{
1 => GetParamData1,
2 => GetParamData2,
3 => GetParamData3,
4 => GetParamData4,
5 => GetParamData5,
6 => GetParamData6,
7 => GetParamData7,
8 => GetParamData8,
_ => throw new NotImplementedException()
};
var name = getNamefunc?.Invoke(fe);
var data = getDatafunc?.Invoke(fe);
return (name, data);
}
private static async Task<object?> ExecuteScriptAsync(FrameworkElement fe)
{
var script = GetXamlScript(fe);
if (script is null || string.IsNullOrWhiteSpace(script.Content))
{
Debug.WriteLine($"[{fe.Name}] 脚本为空跳过执行 ({DateTime.Now:T})");
return null;
}
Debug.WriteLine($"[{fe.Name}] 执行脚本 ({DateTime.Now:T})");
IScriptInvokeContext invokeContext = new ScriptInvokeContext();
Dictionary<string, Type> letTypes = new Dictionary<string, Type>();
for(var i = 1; i <= 8; i++)
{
(var name,var data) = GetGetParamFunc(fe,i);
if(name is null || data is null
|| string.IsNullOrEmpty(name))
{
continue;
}
var type = data.GetType();
letTypes[name] = type;
invokeContext.SetVarValue(name, data);
}
SereinScript sereinScript = new SereinScript();
sereinScript.ParserScript(script.Content, letTypes);
var result = await sereinScript.InterpreterAsync(invokeContext);
Debug.WriteLine("脚本执行结束\n");
return result;
}
/// <summary>
/// 获取绑定值
/// </summary>
/// <param name="fe"></param>
/// <param name="let"></param>
/// <param name="dataContext"></param>
/// <returns></returns>
/* private static object? GetBindingValue(FrameworkElement fe, XamlLet let, object dataContext)
{
var bindingExpr = BindingOperations.GetBindingExpression(let, XamlLet.ValueProperty);
if (bindingExpr != null)
{
// 取原始绑定
Binding oldBinding = bindingExpr.ParentBinding;
//var rs = oldBinding.RelativeSource;
// 创建新绑定,复制原绑定设置
var newBinding = new Binding
{
Path = oldBinding.Path,
Mode = oldBinding.Mode,
UpdateSourceTrigger = oldBinding.UpdateSourceTrigger,
Converter = oldBinding.Converter,
ConverterParameter = oldBinding.ConverterParameter,
ConverterCulture = oldBinding.ConverterCulture,
Source = dataContext
};
// 重新应用
BindingOperations.SetBinding(fe, XamlLet.ValueProperty, newBinding);
var data = fe.GetValue(XamlLet.ValueProperty);
return data;
}
return null;
}*/
/*private static FrameworkElement GetParent(FrameworkElement fe)
{
var type = GetFindType(fe);
if (type is null)
{
return fe;
}
var priority = GetFindPriority(fe);
if (priority == FindPriorityType.VisualOnly)
{
if (FindVisualParent(fe, type) is FrameworkElement element)
{
return element;
}
}
else if (priority == FindPriorityType.LogicalOnly)
{
if (FindLogicalParent(fe, type) is FrameworkElement element)
{
return element;
}
}
else if (priority == FindPriorityType.Visual)
{
if (FindVisualParent(fe, type) is FrameworkElement element)
{
return element;
}
if (FindVisualParent(fe, type) is FrameworkElement element2)
{
return element2;
}
}
else if (priority == FindPriorityType.Logical)
{
if (FindVisualParent(fe, type) is FrameworkElement element)
{
return element;
}
if (FindVisualParent(fe, type) is FrameworkElement element2)
{
return element2;
}
}
return fe;
}*/
/// <summary>
/// 视觉树查找控件
/// </summary>
/// <param name="child"></param>
/// <param name="targetType"></param>
/// <returns></returns>
private static DependencyObject? FindVisualParent(DependencyObject child, Type targetType)
{
DependencyObject? current = child;
while (current != null)
{
current = VisualTreeHelper.GetParent(current);
if (current != null && targetType.IsAssignableFrom(current.GetType()))
return current;
}
return null;
}
/// <summary>
/// 逻辑树查找控件
/// </summary>
/// <param name="child"></param>
/// <param name="targetType"></param>
/// <returns></returns>
private static DependencyObject? FindLogicalParent(DependencyObject child, Type targetType)
{
DependencyObject? current = child;
while (current != null)
{
current = LogicalTreeHelper.GetParent(current);
if (current != null && targetType.IsAssignableFrom(current.GetType()))
return current;
}
return null;
}
#endregion
}
}