gpt4 book ai didi

c# - 在异步处理程序中最初将 handled 属性设置为 true 后,如何重新传播事件?

转载 作者:太空狗 更新时间:2023-10-29 20:35:46 25 4
gpt4 key购买 nike

我正在尝试通过调用运行计时器的 bool 任务在 WPF 应用程序中创建具有可变延迟的触摸和保持事件处理程序。如果计时器超时,则任务返回 true。如果发生另一个事件,例如触摸离开或触摸结束,任务立即返回 false。下面是我的事件处理程序代码:

private static async void Element_PreviewTouchDown(object sender, TouchEventArgs e)
{
// Set handled to true to avoid clicks
e.Handled = true;

var isTouchHold = await TouchHold((FrameworkElement)sender, variableTimespan);
if (isTouchHold)
TouchHoldCmd?.Execute(someParam);
else
{
// Here is where I would like to re initiate bubbling up of the event.
// This doesn't work:
e.Handled = false;
}
}

我希望它传播事件的原因是,例如,如果用户想要平移该元素所属的滚动查看器,并且通过触摸我的元素开始平移手势,我的触摸点将按预期工作触摸并按住命令不会被触发,但滚动查看器也不会开始平移。

我尝试手动引发事件,但这似乎也不起作用:

bool firedBySelf;
private static async void Element_PreviewTouchDown(object sender, TouchEventArgs e)
{
if(firedBySelf)
{
firedBySelf = false;
return;
}

...
else
{
firedBySelf = true;
e.Handled = false;
((FrameworkElement)sender).RaiseEvent(e);
}
}

我怎样才能实现我的目标?

编辑:这是包含任务的类:

public static class TouchHoldHelper
{
private static DispatcherTimer _timer;
private static TaskCompletionSource<bool> _task;
private static FrameworkElement _element;

private static void MouseUpCancel(object sender, MouseButtonEventArgs e) => CancelHold();
private static void MouseLeaveCancel(object sender, System.Windows.Input.MouseEventArgs e) => CancelHold();
private static void TouchCancel(object sender, TouchEventArgs e) => CancelHold();

private static void AddCancellingHandlers()
{
if (_element == null) return;

_element.PreviewMouseUp += MouseUpCancel;
_element.MouseUp += MouseUpCancel;
_element.MouseLeave += MouseLeaveCancel;

_element.PreviewTouchUp += TouchCancel;
_element.TouchUp += TouchCancel;
_element.TouchLeave += TouchCancel;
}

private static void RemoveCancellingHandlers()
{
if (_element == null) return;

_element.PreviewMouseUp -= MouseUpCancel;
_element.MouseUp -= MouseUpCancel;
_element.MouseLeave -= MouseLeaveCancel;

_element.PreviewTouchUp -= TouchCancel;
_element.TouchUp -= TouchCancel;
_element.TouchLeave -= TouchCancel;
}

private static void CancelHold()
{
if (_timer != null)
{
_timer.Stop();
_timer.Tick -= _timer_Tick;
_timer = null;
}
if (_task?.Task.Status != TaskStatus.RanToCompletion)
_task?.TrySetResult(false);

RemoveCancellingHandlers();
}

private static void _timer_Tick(object sender, EventArgs e)
{
var timer = sender as DispatcherTimer;
timer.Stop();
timer.Tick -= _timer_Tick;
timer = null;
_task.TrySetResult(true);
RemoveCancellingHandlers();
}

public static Task<bool> TouchHold(this FrameworkElement element, TimeSpan duration)
{
_element = element;

_timer = new DispatcherTimer();
_timer.Interval = duration;
_timer.Tick += _timer_Tick;

_task = new TaskCompletionSource<bool>();

AddCancellingHandlers();

_timer.Start();
return _task.Task;
}
}

编辑:为了更好地解释我的预期行为,请考虑智能手机屏幕上的图标是如何工作的。如果我点击该图标,它会启动该图标代表的应用程序。如果我触摸并在图标上移动,它会平移屏幕。如果我触摸并按住图标,它允许我移动图标,这样我就可以将它放在其他地方而无需平移屏幕。如果我触摸并按住图标但我没有保持足够长的时间来触发图标的移动,它就像我点击它一样启动应用程序。我正在尝试复制这最后两种行为。

我并不是说我当前的实现是正确的方法,但这是我能够想出的方法。如果有任何替代方法,我很乐意探索它。

最佳答案

您将 e.Handled 设置为 true 然后想再次将其设置回 false 的工作流程让我觉得很奇怪。

来自 When to Mark Events as Handled

Another way to consider the "handled" issue is that you should generally mark a routed event handled if your code responded to the routed event in a significant and relatively complete way.

看起来要么是您使用了错误的事件,要么是 Microsoft 的人弄错了 ;)

// Set handled to true to avoid clicks

不,他们甚至想到了这一点,引用 Remarks .

您可以设置 Stylus.IsPressAndHoldEnabled="False" 以禁用“点击行为”。允许您回退到处理事件或让它发生的默认 WPF 模式 tunnel (in this case)转发。

private static async void Element_PreviewTouchDown(object sender, TouchEventArgs e)
{
var isTouchHold = await TouchHold((FrameworkElement)sender, variableTimespan);
if (isTouchHold)
{
TouchHoldCmd?.Execute(someParam);
e.Handled = true;
}
}

但是,正如您在评论中恰如其分地指出的那样:

The issue is that the event handler (Element_PreviewTouchDown) is finished executing before the task is. By the time the task is finished, it doesn't make any difference if I change the e.Handled value.

鉴于船已经起航并且您不想干扰 UI 元素的正常功能,我们可以删除将事件标记为已处理的所有行。

private static async void Element_PreviewTouchDown(object sender, TouchEventArgs e)
{
var isTouchHold = await TouchHold((FrameworkElement)sender, variableTimespan);
if (isTouchHold)
{
TouchHoldCmd?.Execute(someParam);
}
}

关于c# - 在异步处理程序中最初将 handled 属性设置为 true 后,如何重新传播事件?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/57270218/

25 4 0
Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
广告合作:1813099741@qq.com 6ren.com