From c439a46e386853886d5205ab5176710da40a764b Mon Sep 17 00:00:00 2001 From: luxiaoqi Date: Thu, 23 Nov 2023 15:20:43 +0800 Subject: [PATCH] Button.AsyncClick --- AndroidTest/Resources/Resource.designer.cs | 2 +- CPF.Android/Resources/Resource.designer.cs | 2 +- CPF.Toolkit.Demo/CPF.Toolkit.Demo.csproj | 4 +- CPF.Toolkit.Demo/MainView.cs | 15 ++-- CPF.Toolkit.Demo/MainViewModel.cs | 7 +- CPF.Toolkit/CPF.Toolkit.csproj | 4 +- CPF.Toolkit/Controls/AsyncButton.cs | 31 ------- CPF.Toolkit/Input/AsyncCommand.cs | 51 ----------- CPF.Toolkit/Input/IAsyncCommand.cs | 14 --- CPF/Controls/Button.cs | 2 + CPF/Controls/ButtonBase.cs | 43 +++++++--- CPF/CpfObject.cs | 99 ++++++++++++++++++---- CPF/Input/AsyncEventHandler.cs | 9 ++ CPF/Reflection/FastReflectionExtensions.cs | 84 +++++++++++++++++- CPF/WeakEventHandler.cs | 19 +++++ 15 files changed, 249 insertions(+), 137 deletions(-) delete mode 100644 CPF.Toolkit/Controls/AsyncButton.cs delete mode 100644 CPF.Toolkit/Input/AsyncCommand.cs delete mode 100644 CPF.Toolkit/Input/IAsyncCommand.cs create mode 100644 CPF/Input/AsyncEventHandler.cs diff --git a/AndroidTest/Resources/Resource.designer.cs b/AndroidTest/Resources/Resource.designer.cs index cfe2ba6..65e50fe 100644 --- a/AndroidTest/Resources/Resource.designer.cs +++ b/AndroidTest/Resources/Resource.designer.cs @@ -14,7 +14,7 @@ namespace AndroidTest { - [global::System.CodeDom.Compiler.GeneratedCodeAttribute("Xamarin.Android.Build.Tasks", "1.0.0.0")] + [global::System.CodeDom.Compiler.GeneratedCodeAttribute("Xamarin.Android.Build.Tasks", "13.2.2.120")] public partial class Resource { diff --git a/CPF.Android/Resources/Resource.designer.cs b/CPF.Android/Resources/Resource.designer.cs index fe4dd0e..f35f37f 100644 --- a/CPF.Android/Resources/Resource.designer.cs +++ b/CPF.Android/Resources/Resource.designer.cs @@ -14,7 +14,7 @@ namespace CPF.Android { - [global::System.CodeDom.Compiler.GeneratedCodeAttribute("Xamarin.Android.Build.Tasks", "1.0.0.0")] + [global::System.CodeDom.Compiler.GeneratedCodeAttribute("Xamarin.Android.Build.Tasks", "13.2.2.120")] public partial class Resource { diff --git a/CPF.Toolkit.Demo/CPF.Toolkit.Demo.csproj b/CPF.Toolkit.Demo/CPF.Toolkit.Demo.csproj index 61bfbf6..53c52d2 100644 --- a/CPF.Toolkit.Demo/CPF.Toolkit.Demo.csproj +++ b/CPF.Toolkit.Demo/CPF.Toolkit.Demo.csproj @@ -15,5 +15,7 @@ - + + + diff --git a/CPF.Toolkit.Demo/MainView.cs b/CPF.Toolkit.Demo/MainView.cs index 52887b2..ac83c1b 100644 --- a/CPF.Toolkit.Demo/MainView.cs +++ b/CPF.Toolkit.Demo/MainView.cs @@ -6,7 +6,6 @@ using CPF.Drawing; using CPF.Shapes; using CPF.Styling; using CPF.Svg; -using CPF.Toolkit.Controls; using CPF.Toolkit.Dialogs; using System; using System.Collections.Generic; @@ -21,13 +20,13 @@ namespace CPF.Toolkit.Demo { } + MainViewModel vm = new MainViewModel(); protected override void InitializeComponent() { Title = "标题"; Width = 500; Height = 400; Background = null; - var vm = new MainViewModel(); this.DataContext = this.CommandContext = vm; Children.Add(new WindowFrame(this, new WrapPanel @@ -71,13 +70,19 @@ namespace CPF.Toolkit.Demo Content = "loading", Commands = { { nameof(Button.Click),(s,e) => vm.LoadingTest() } } }, - new AsyncButton + new Button { Content = "AsyncButton", - Command = vm.AsyncClick, - }, + }.Assign(out var asyncButton), } })); + + asyncButton.AsyncClick += AsyncButton_AsyncClick; + } + + private async Task AsyncButton_AsyncClick(object sender, RoutedEventArgs e) + { + await this.vm.AsyncClick(); } } } diff --git a/CPF.Toolkit.Demo/MainViewModel.cs b/CPF.Toolkit.Demo/MainViewModel.cs index c3c4dd9..3470b28 100644 --- a/CPF.Toolkit.Demo/MainViewModel.cs +++ b/CPF.Toolkit.Demo/MainViewModel.cs @@ -1,5 +1,4 @@ using CPF.Controls; -using CPF.Toolkit.Input; using System; using System.Collections.Generic; using System.Diagnostics; @@ -49,10 +48,10 @@ namespace CPF.Toolkit.Demo //this.Dialog.Sucess(result); } - public IAsyncCommand AsyncClick => new AsyncCommand(async () => + public async Task AsyncClick() { - await Task.Delay(5000); + await Task.Delay(3000); this.Dialog.Alert("test"); - }); + } } } diff --git a/CPF.Toolkit/CPF.Toolkit.csproj b/CPF.Toolkit/CPF.Toolkit.csproj index a49aac9..4e089e0 100644 --- a/CPF.Toolkit/CPF.Toolkit.csproj +++ b/CPF.Toolkit/CPF.Toolkit.csproj @@ -21,5 +21,7 @@ - + + + diff --git a/CPF.Toolkit/Controls/AsyncButton.cs b/CPF.Toolkit/Controls/AsyncButton.cs deleted file mode 100644 index f7aff72..0000000 --- a/CPF.Toolkit/Controls/AsyncButton.cs +++ /dev/null @@ -1,31 +0,0 @@ -using CPF.Controls; -using CPF.Toolkit.Input; -using System; -using System.Collections.Generic; -using System.Text; - -namespace CPF.Toolkit.Controls -{ - public class AsyncButton : Button - { - - protected override void InitializeComponent() - { - base.InitializeComponent(); - this.Triggers.Add(nameof(IsEnabled), Relation.Me, null, (nameof(Background), "224,224,224")); - } - - public IAsyncCommand Command { get; set; } - - protected override async void OnClick(RoutedEventArgs e) - { - this.IsEnabled = false; - base.OnClick(e); - if (this.Command != null) - { - await this.Command.ExecuteAsync(); - } - this.IsEnabled = true; - } - } -} diff --git a/CPF.Toolkit/Input/AsyncCommand.cs b/CPF.Toolkit/Input/AsyncCommand.cs deleted file mode 100644 index c70a06a..0000000 --- a/CPF.Toolkit/Input/AsyncCommand.cs +++ /dev/null @@ -1,51 +0,0 @@ -using System; -using System.Collections.Generic; -using System.Text; -using System.Threading; -using System.Threading.Tasks; - -namespace CPF.Toolkit.Input -{ - public class AsyncCommand : IAsyncCommand - { - public AsyncCommand(Func execute) - { - this.execute = execute; - } - - private readonly Func execute; - private Task executionTask; - - public Task ExecutionTask - { - get => this.executionTask; - private set - { - if (ReferenceEquals(this.executionTask, value)) - { - return; - } - - this.executionTask = value; - bool isAlreadyCompletedOrNull = value?.IsCompleted ?? true; - if (isAlreadyCompletedOrNull) - { - return; - } - } - } - - public bool IsRunning => ExecutionTask is { IsCompleted: false }; - - - public Task ExecuteAsync() - { - Task executionTask = ExecutionTask; - if (this.execute is not null) - { - executionTask = ExecutionTask = this.execute(); - } - return executionTask; - } - } -} diff --git a/CPF.Toolkit/Input/IAsyncCommand.cs b/CPF.Toolkit/Input/IAsyncCommand.cs deleted file mode 100644 index e2c2f45..0000000 --- a/CPF.Toolkit/Input/IAsyncCommand.cs +++ /dev/null @@ -1,14 +0,0 @@ -using System; -using System.Collections.Generic; -using System.Text; -using System.Threading.Tasks; - -namespace CPF.Toolkit.Input -{ - public interface IAsyncCommand - { - Task ExecutionTask { get; } - bool IsRunning { get; } - Task ExecuteAsync(); - } -} diff --git a/CPF/Controls/Button.cs b/CPF/Controls/Button.cs index efbceda..cb1dc66 100644 --- a/CPF/Controls/Button.cs +++ b/CPF/Controls/Button.cs @@ -38,6 +38,8 @@ namespace CPF.Controls overridePropertys.Override(nameof(Background), new UIPropertyMetadataAttribute((ViewFill)"221,221,221", UIPropertyOptions.AffectsRender)); } + + //protected override void OnPropertyChanged(string propertyName, object oldValue, object newValue, PropertyMetadataAttribute propertyMetadata) //{ // base.OnPropertyChanged(propertyName, oldValue, newValue, propertyMetadata); diff --git a/CPF/Controls/ButtonBase.cs b/CPF/Controls/ButtonBase.cs index 142f299..e5fb568 100644 --- a/CPF/Controls/ButtonBase.cs +++ b/CPF/Controls/ButtonBase.cs @@ -4,6 +4,7 @@ using System.Text; using CPF.Input; using CPF.Drawing; using System.ComponentModel; +using System.Threading.Tasks; namespace CPF.Controls { @@ -20,7 +21,7 @@ namespace CPF.Controls } bool isMouseLeftButtonPressed; - protected override void OnMouseDown(MouseButtonEventArgs e) + protected override async void OnMouseDown(MouseButtonEventArgs e) { base.OnMouseDown(e); if (!e.Handled) @@ -72,6 +73,7 @@ namespace CPF.Controls try { OnClick(); + await OnAsyncClick(); exceptionThrown = false; } finally @@ -100,7 +102,7 @@ namespace CPF.Controls // e.Handled = true; // } //} - protected override void OnMouseUp(MouseButtonEventArgs e) + protected override async void OnMouseUp(MouseButtonEventArgs e) { base.OnMouseUp(e); if (e.MouseButton == MouseButton.Left) @@ -128,6 +130,7 @@ namespace CPF.Controls if (l.X >= 0 && l.Y >= 0 && l.X <= r.Width && l.Y <= r.Height) { OnClick(); + await OnAsyncClick(); } } } @@ -136,7 +139,7 @@ namespace CPF.Controls } bool IsSpaceKeyDown = false; - protected override void OnKeyDown(KeyEventArgs e) + protected override async void OnKeyDown(KeyEventArgs e) { base.OnKeyDown(e); if (ClickMode == ClickMode.Hover || e.Handled) @@ -159,6 +162,7 @@ namespace CPF.Controls if (ClickMode == ClickMode.Press) { OnClick(); + await OnAsyncClick(); } e.Handled = true; @@ -177,6 +181,7 @@ namespace CPF.Controls } OnClick(); + await OnAsyncClick(); e.Handled = true; } } @@ -195,7 +200,7 @@ namespace CPF.Controls } } - protected override void OnKeyUp(KeyEventArgs e) + protected override async void OnKeyUp(KeyEventArgs e) { base.OnKeyUp(e); if (ClickMode == ClickMode.Hover || e.Handled) @@ -222,7 +227,10 @@ namespace CPF.Controls } if (shouldClick) + { OnClick(); + await OnAsyncClick(); + } } else { @@ -250,22 +258,29 @@ namespace CPF.Controls IsPressed = false; } } + protected void OnClick() { - //var p1 = PointToScreen(new Point()); - //var p4 = PointToScreen(new Point(ActualSize.Width, ActualSize.Height)); - //var rect = new Rect(p1, p4); - //if (rect.Contains(Root.InputManager.MouseDevice.Location)) - //{ OnClick(new RoutedEventArgs(this)); - //} } - + protected virtual void OnClick(RoutedEventArgs e) { RaiseEvent(e, nameof(Click)); } + protected async Task OnAsyncClick() + { + await OnAsyncClick(new RoutedEventArgs(this)); + } + + protected virtual async Task OnAsyncClick(RoutedEventArgs e) + { + this.IsEnabled = false; + await AsyncRaiseEvent(e, nameof(AsyncClick)); + this.IsEnabled = true; + } + /// /// 获取或设置 Click 事件何时发生。 /// @@ -296,6 +311,12 @@ namespace CPF.Controls add { AddHandler(value); } remove { RemoveHandler(value); } } + + public event AsyncEventHandler AsyncClick + { + add { AddHandler(value); } + remove { RemoveHandler(value); } + } } /// diff --git a/CPF/CpfObject.cs b/CPF/CpfObject.cs index 3c0f928..94729ef 100644 --- a/CPF/CpfObject.cs +++ b/CPF/CpfObject.cs @@ -13,6 +13,7 @@ using CPF.Animation; using System.Collections.Concurrent; using System.Runtime.InteropServices; using System.Collections; +using System.Threading.Tasks; namespace CPF { @@ -1708,10 +1709,6 @@ namespace CPF /// public void RaiseEvent(in TEventArgs eventArgs, string eventName) { - //if (eventArgs is RoutedEventArgs routed) - //{ - // routed.Sender = this; - //} OnRaiseEvent(eventArgs, eventName); if (observers != null && eventArgs is EventArgs args) @@ -1739,10 +1736,6 @@ namespace CPF v = item.Target.Target; objs.Add(v); } - //else if (item.Relation != null && this is UIElement) - //{ - // objs.AddRange(item.Relation.Query(this as UIElement)); - //} else { v = CommandContext; @@ -1778,14 +1771,6 @@ namespace CPF } } } - //v.GetType().GetMethod(item.MethodName, BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.Instance).FastInvoke(v, ps); - - //var m = obj.GetType().GetMethod(item.MethodName, BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.Instance); - //if (m == null) - //{ - // throw new Exception("未找到该方法 " + item.MethodName); - //} - //m.Invoke(obj, ps); obj.Invoke(item.MethodName, ps); } } @@ -1804,6 +1789,88 @@ namespace CPF } } + public async Task AsyncRaiseEvent(TEventArgs eventArgs, string eventName) + { + OnRaiseEvent(eventArgs, eventName); + + if (observers != null && eventArgs is EventArgs args) + { + EventObserver eventObserver = new EventObserver(eventName, args, this); + foreach (var observer in observers) + { + observer.OnNext(eventObserver); + } + } + + if (commands != null) + { + List list; + if (commands.commands.TryGetValue(eventName, out list)) + { + foreach (var item in list) + { + if (item.Action == null) + { + var objs = new List(); + object v = null; + if (item.Target != null) + { + v = item.Target.Target; + objs.Add(v); + } + else + { + v = CommandContext; + if (v != null) + { + objs.Add(v); + } + } + foreach (var obj in objs) + { + if (obj == null) + { + continue; + } + object[] ps = new object[item.Params == null ? 0 : item.Params.Length]; + if (item.Params != null && item.Params.Length > 0) + { + //ps = item.Params; + item.Params.CopyTo(ps, 0); + for (int i = 0; i < ps.Length; i++) + { + var p = ps[i]; + if (p is CommandParameter) + { + if ((CommandParameter)p == CommandParameter.EventArgs) + { + ps[i] = eventArgs; + } + else if ((CommandParameter)p == CommandParameter.EventSender) + { + ps[i] = this; + } + } + } + } + obj.Invoke(item.MethodName, ps); + } + } + else + { + item.Action(this, eventArgs); + } + } + } + } + + var handler = Events[eventName]; + if (handler != null) + { + await handler.AsyncInvoke(this, eventArgs); + } + } + protected virtual void OnRaiseEvent(in TEventArgs eventArgs, string eventName) { diff --git a/CPF/Input/AsyncEventHandler.cs b/CPF/Input/AsyncEventHandler.cs new file mode 100644 index 0000000..3e41833 --- /dev/null +++ b/CPF/Input/AsyncEventHandler.cs @@ -0,0 +1,9 @@ +using System; +using System.Collections.Generic; +using System.Text; +using System.Threading.Tasks; + +namespace CPF.Input +{ + public delegate Task AsyncEventHandler(object sender, TEventArgs e); +} diff --git a/CPF/Reflection/FastReflectionExtensions.cs b/CPF/Reflection/FastReflectionExtensions.cs index ec507f7..d32a54a 100644 --- a/CPF/Reflection/FastReflectionExtensions.cs +++ b/CPF/Reflection/FastReflectionExtensions.cs @@ -6,6 +6,7 @@ using System.Reflection; using System.Linq.Expressions; using System.Collections.Concurrent; using System.Reflection.Emit; +using System.Threading.Tasks; namespace CPF.Reflection { @@ -69,6 +70,7 @@ namespace CPF.Reflection } } static ConcurrentDictionary> methods = new ConcurrentDictionary>(); + static ConcurrentDictionary> asyncMethods = new ConcurrentDictionary>(); static KeyValuePair>[] saveMethods; static ConcurrentDictionary>> setValues = new ConcurrentDictionary>>(); @@ -106,6 +108,28 @@ namespace CPF.Reflection return fun(instance, parameters); } + public static async Task AsyncInvoke(this object instance, string methodName, params object[] parameters) + { + var type = instance.GetType(); + + if (!asyncMethods.TryGetValue(type, out var list)) + { + list = new ConcurrentDictionary(); + asyncMethods.TryAdd(type, list); + } + if (!list.TryGetValue(methodName, out var fun)) + { + var minfo = type.GetMethod(methodName, BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.Instance); + if (minfo == null) + { + throw new Exception(type + "未找到方法:" + methodName); + } + fun = AsyncMethodCache.CreateInvokeDelegate(minfo); + list.TryAdd(methodName, fun); + } + await fun(instance, parameters); + } + /// /// 反射快速调用 /// @@ -115,10 +139,15 @@ namespace CPF.Reflection /// public static object FastInvoke(this MethodInfo methodInfo, object instance, params object[] parameters) { - //return FastReflectionCaches.MethodInvokerCache.Get(methodInfo).Invoke(instance, parameters); return methodCache.Get(methodInfo).Invoke(instance, parameters); } + public static async Task FastAsyncInvoke(this MethodInfo methodInfo, object instance, params object[] parameters) + { + await asyncMethodCache.Get(methodInfo).Invoke(instance, parameters); + } + static MethodCache methodCache = new MethodCache(); + static AsyncMethodCache asyncMethodCache = new AsyncMethodCache(); /// /// 快速动态设置对象的属性值 @@ -369,7 +398,57 @@ namespace CPF.Reflection } } + class AsyncMethodCache : FastReflectionCache + { + protected override AsyncInvokeHandler Create(MethodInfo key) + { + return CreateInvokeDelegate(key); + } + public static AsyncInvokeHandler CreateInvokeDelegate(MethodInfo methodInfo) + { + var instanceParameter = Expression.Parameter(typeof(object), "instance"); + var parametersParameter = Expression.Parameter(typeof(object[]), "parameters"); + + var parameterExpressions = new List(); + var paramInfos = methodInfo.GetParameters(); + for (int i = 0; i < paramInfos.Length; i++) + { + BinaryExpression valueObj = Expression.ArrayIndex( + parametersParameter, Expression.Constant(i)); + UnaryExpression valueCast = Expression.Convert( + valueObj, paramInfos[i].ParameterType); + + parameterExpressions.Add(valueCast); + } + + var instanceCast = methodInfo.IsStatic ? null : + Expression.Convert(instanceParameter, methodInfo.ReflectedType); + + var methodCall = Expression.Call(instanceCast, methodInfo, parameterExpressions); + + if (methodCall.Type == typeof(void)) + { + var lambda = Expression.Lambda>( + methodCall, instanceParameter, parametersParameter); + + Action execute = lambda.Compile(); + return (instance, parameters) => + { + execute(instance, parameters); + return null; + }; + } + else + { + var castMethodCall = Expression.Convert(methodCall, typeof(Task)); + var lambda = Expression.Lambda( + castMethodCall, instanceParameter, parametersParameter); + + return lambda.Compile(); + } + } + } class MethodCache : FastReflectionCache { protected override InvokeHandler Create(MethodInfo key) @@ -482,6 +561,8 @@ namespace CPF.Reflection //iLGenerator.Ret(); //return (InvokeHandler)dynamicMethod.CreateDelegate(typeof(InvokeHandler)); } + + } class PropertyGetCache : FastReflectionCache> @@ -578,5 +659,6 @@ namespace CPF.Reflection public delegate void SetHandler(object target, T value); public delegate T GetHandler(object target); public delegate object InvokeHandler(object target, object[] paramters); + public delegate Task AsyncInvokeHandler(object target, object[] paramters); public delegate void SetRefHandler(ref T target, V value); } diff --git a/CPF/WeakEventHandler.cs b/CPF/WeakEventHandler.cs index 7a23bce..71018c8 100644 --- a/CPF/WeakEventHandler.cs +++ b/CPF/WeakEventHandler.cs @@ -4,6 +4,7 @@ using System.Text; using CPF.Reflection; using System.Reflection; using System.Linq; +using System.Threading.Tasks; namespace CPF { @@ -48,6 +49,24 @@ namespace CPF } } + public async Task AsyncInvoke(object sender, object e) + { + if (delegates != null) + { + foreach (var item in delegates.ToArray()) + { + if (item.reference.TryGetTarget(out var target) || item.method.IsStatic) + { + await item.method.FastAsyncInvoke(target, sender, e); + } + else + { + delegates.Remove(item); + } + } + } + } + public void Dispose() { delegates = null;