Button.AsyncClick

This commit is contained in:
luxiaoqi 2023-11-23 15:20:43 +08:00
parent 8decf9e554
commit c439a46e38
15 changed files with 249 additions and 137 deletions

View File

@ -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
{

View File

@ -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
{

View File

@ -15,5 +15,7 @@
<ProjectReference Include="..\CPF.Windows\CPF.Windows.csproj" />
<ProjectReference Include="..\CPF\CPF.csproj" />
</ItemGroup>
<ItemGroup>
<PackageReference Include="Microsoft.CSharp" Version="4.7.0" />
</ItemGroup>
</Project>

View File

@ -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();
}
}
}

View File

@ -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");
});
}
}
}

View File

@ -21,5 +21,7 @@
<ItemGroup>
<ProjectReference Include="..\CPF\CPF.csproj" />
</ItemGroup>
<ItemGroup>
<PackageReference Include="Microsoft.CSharp" Version="4.7.0" />
</ItemGroup>
</Project>

View File

@ -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;
}
}
}

View File

@ -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<Task> execute)
{
this.execute = execute;
}
private readonly Func<Task> 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;
}
}
}

View File

@ -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();
}
}

View File

@ -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);

View File

@ -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,15 +258,10 @@ 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)
@ -266,6 +269,18 @@ namespace CPF.Controls
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;
}
/// <summary>
/// 获取或设置 Click 事件何时发生。
/// </summary>
@ -296,6 +311,12 @@ namespace CPF.Controls
add { AddHandler(value); }
remove { RemoveHandler(value); }
}
public event AsyncEventHandler<RoutedEventArgs> AsyncClick
{
add { AddHandler(value); }
remove { RemoveHandler(value); }
}
}
/// <summary>

View File

@ -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
/// <param name="eventName"></param>
public void RaiseEvent<TEventArgs>(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>(TEventArgs eventArgs, string eventName)
{
OnRaiseEvent(eventArgs, eventName);
if (observers != null && eventArgs is EventArgs args)
{
EventObserver<EventArgs, CpfObject> eventObserver = new EventObserver<EventArgs, CpfObject>(eventName, args, this);
foreach (var observer in observers)
{
observer.OnNext(eventObserver);
}
}
if (commands != null)
{
List<Command> list;
if (commands.commands.TryGetValue(eventName, out list))
{
foreach (var item in list)
{
if (item.Action == null)
{
var objs = new List<object>();
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<TEventArgs>(in TEventArgs eventArgs, string eventName)
{

View File

@ -0,0 +1,9 @@
using System;
using System.Collections.Generic;
using System.Text;
using System.Threading.Tasks;
namespace CPF.Input
{
public delegate Task AsyncEventHandler<TEventArgs>(object sender, TEventArgs e);
}

View File

@ -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<Type, ConcurrentDictionary<string, InvokeHandler>> methods = new ConcurrentDictionary<Type, ConcurrentDictionary<string, InvokeHandler>>();
static ConcurrentDictionary<Type, ConcurrentDictionary<string, AsyncInvokeHandler>> asyncMethods = new ConcurrentDictionary<Type, ConcurrentDictionary<string, AsyncInvokeHandler>>();
static KeyValuePair<Type, ConcurrentDictionary<string, InvokeHandler>>[] saveMethods;
static ConcurrentDictionary<Type, ConcurrentDictionary<string, SetHandler<object>>> setValues = new ConcurrentDictionary<Type, ConcurrentDictionary<string, SetHandler<object>>>();
@ -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<string, AsyncInvokeHandler>();
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);
}
/// <summary>
/// 反射快速调用
/// </summary>
@ -115,10 +139,15 @@ namespace CPF.Reflection
/// <returns></returns>
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();
/// <summary>
/// 快速动态设置对象的属性值
@ -369,7 +398,57 @@ namespace CPF.Reflection
}
}
class AsyncMethodCache : FastReflectionCache<MethodInfo, AsyncInvokeHandler>
{
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<Expression>();
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<Action<object, object[]>>(
methodCall, instanceParameter, parametersParameter);
Action<object, object[]> execute = lambda.Compile();
return (instance, parameters) =>
{
execute(instance, parameters);
return null;
};
}
else
{
var castMethodCall = Expression.Convert(methodCall, typeof(Task));
var lambda = Expression.Lambda<AsyncInvokeHandler>(
castMethodCall, instanceParameter, parametersParameter);
return lambda.Compile();
}
}
}
class MethodCache : FastReflectionCache<MethodInfo, InvokeHandler>
{
protected override InvokeHandler Create(MethodInfo key)
@ -482,6 +561,8 @@ namespace CPF.Reflection
//iLGenerator.Ret();
//return (InvokeHandler)dynamicMethod.CreateDelegate(typeof(InvokeHandler));
}
}
class PropertyGetCache : FastReflectionCache<PropertyInfo, GetHandler<object>>
@ -578,5 +659,6 @@ namespace CPF.Reflection
public delegate void SetHandler<T>(object target, T value);
public delegate T GetHandler<T>(object target);
public delegate object InvokeHandler(object target, object[] paramters);
public delegate Task AsyncInvokeHandler(object target, object[] paramters);
public delegate void SetRefHandler<T, V>(ref T target, V value);
}

View File

@ -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;