【问题标题】:It seems that the task doesn't get canceled completely任务似乎没有完全取消
【发布时间】:2020-11-14 14:23:50
【问题描述】:

我有一个文本框,可以防止用户编写我用正则表达式指定的不允许的字符,如果用户写了其中任何一个,就会出现一个弹出窗口并在那里停留 5 秒钟,我使用这个持续时间Task.Delay(5000, cts.Token) 也有一个取消标记,如果用户写了一个允许的字符,当弹出窗口出现在屏幕上时,弹出窗口就会消失,并且延迟任务被取消和处理,但是如果我写了一个不允许的字符,然后立即写一个允许的字符,如果我做得非常快,那么当我写一个不允许的字符打开弹出窗口时,弹出窗口会在不到 5 秒内消失(随机秒,我认为这是因为延迟任务在那一刻被取消,然后导致弹出窗口消失),这与我没有使用取消令牌取消延迟任务时的问题相同。但是使用我编写的代码,我不知道任务是如何没有完全取消的,或者可能确实取消了,但问题出在其他问题上......

    CancellationTokenSource cts;
    protected async override void OnPreviewTextInput(TextCompositionEventArgs e)
    {
        if (txtName.IsFocused)
        {
            if (cts != null && !regex.IsMatch(e.Text))
            {
                cts.Cancel();
                cts.Dispose();
            }

            cts = new CancellationTokenSource();

            if (regex.IsMatch(e.Text))
            {
                e.Handled = true;

                txtName.CaretIndex = txtName.Text.Length;

                if (AlertPopup.IsOpen == false)
                {
                    AlertPopup.IsOpen = true;
                    txtName.Focus();
                    try
                    {
                        await Task.Delay(5000, cts.Token);
                    }
                    catch (TaskCanceledException) { }
                    finally
                    {
                        AlertPopup.IsOpen = false;
                    }
                }
            }
            else
            {
                AlertPopup.IsOpen = false;
            }

            base.OnPreviewTextInput(e);
        }
    }

【问题讨论】:

    标签: c# wpf task cancellation-token


    【解决方案1】:

    OnPreviewTextInput() 方法只在 UI 线程中执行。同样,该方法中await 之后的继续只会在 UI 线程中执行。 UI 线程一次只能做一件事。这意味着在已经显示错误弹出窗口的情况下,当有新的用户输入并且代码尝试取消该显示(即通过取消延迟的取消令牌)时,await 随后会导致AlertPopup.IsOpen设置为 false 的属性实际上不会在对 OnPreviewTextInput() 的新调用返回之前执行继续。

    因此,当有新的用户输入时,该输入要么有效要么无效。如果有效,则将IsOpen 设置为false 并返回。稍后再次隐藏弹出窗口是没有问题的。但是,如果输入无效,则弹出窗口的当前状态是 IsOpen 为真,您跳过尝试显示它,方法返回,然后 然后 取消任务的继续执行,隐藏弹出窗口。

    请注意,这里的重要部分是在方法返回之后继续发生。即使您尝试无条件地显示弹出窗口(即在执行该部分之前不要检查IsOpen),弹出窗口也会显示然后立即隐藏。

    您可以通过使用不同的计时机制来解决此问题(即,您可以延长计时器而不必取消并重新启动,例如System.Threading.Timer),或者使用您现在拥有的机制但保留一个计数器每次触发无效状态时递增,每次定时器等待完成时递减,只有当计数器再次达到零时才隐藏弹出窗口。

    但实际上,我认为这一切都不是很好的用户交互。作为用户,我真的开始讨厌定时工具提示,因为它们的可见时间永远不够长。 WPF 中一种更常见的技术是使用一种内置验证机制(WPF 中的验证至少有三种不同的变体,each with their own pros and cons),然后将元素的常规工具提示值设置为错误消息, 这样用户只要将鼠标悬停在元素上就可以看到它。

    任何类型的用户输入验证都存在障碍和头痛。但是,如果您使用这些标准之一,至少您不必处理这个时间问题,并且您的用户可能会发现它对他们也更有效。 :)

    【讨论】:

      猜你喜欢
      • 2012-08-19
      • 1970-01-01
      • 2014-11-04
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2022-06-15
      • 2021-07-19
      相关资源
      最近更新 更多