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 { /// /// 脚本节点内容 /// public string? Content { get; set; } public override string ToString() => Content ?? string.Empty; } public enum XamlScriptTriggerType { /// /// 初始化时触发 /// Loaded, /// /// 属性变更触发 /// Property, /// /// 指定事件触发 /// Event, /// /// 获得/失去焦点触发 /// Focus } /*public enum FindPriorityType { /// /// 不查找 /// None, /// /// 视觉优先 /// Visual, /// /// 逻辑优先 /// Logical, /// /// 仅视觉 /// VisualOnly, /// /// 仅逻辑 /// 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)); /// 触发类型 public static readonly DependencyProperty TriggerTypeProperty = DependencyProperty.RegisterAttached( "TriggerType", typeof(XamlScriptTriggerType), typeof(XScript), new PropertyMetadata(XamlScriptTriggerType.Loaded)); /// 触发属性名称 public static readonly DependencyProperty TriggerNameProperty = DependencyProperty.RegisterAttached( "TriggerName", typeof(string), typeof(XScript), new PropertyMetadata("")); /// 脚本返回结果,可用于外部绑定 public static readonly DependencyProperty ReturnProperty = DependencyProperty.RegisterAttached( "Return", typeof(object), typeof(XScript), new FrameworkPropertyMetadata(null, FrameworkPropertyMetadataOptions.BindsTwoWayByDefault)); /// 查找DataContent来源类型 public static readonly DependencyProperty FindTypeProperty = DependencyProperty.RegisterAttached("FindType", typeof(Type), typeof(XScript), new PropertyMetadata(null)); /*/// 查找优先级 public static readonly DependencyProperty FindPriorityProperty = DependencyProperty.RegisterAttached( "FindPriority", typeof(FindPriorityType), typeof(XScript), new PropertyMetadata(FindPriorityType.Visual));*/ /* /// 是否使用 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 _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 getNamefunc = index switch { 1 => GetParamName1, 2 => GetParamName2, 3 => GetParamName3, 4 => GetParamName4, 5 => GetParamName5, 6 => GetParamName6, 7 => GetParamName7, 8 => GetParamName8, _ => throw new NotImplementedException() }; Func 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 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 letTypes = new Dictionary(); 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; } /// /// 获取绑定值 /// /// /// /// /// /* 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; }*/ /// /// 视觉树查找控件 /// /// /// /// 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; } /// /// 逻辑树查找控件 /// /// /// /// 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 } }