使用 cancellationToken 取消搜索

Canceling search using a cancellationToken

我的挑战是一个相当普遍的挑战,我有一个人口稠密的树视图,我想过滤。为此,我想要一个文本框,用户可以在其过滤文本中输入该文本框,并且在树视图被过滤后显示其 header.

中具有该特定过滤文本的节点

所以我选择做的是有一个文本框,然后有一个文本更改事件,在它开始过滤过程之前有一个延迟,现在很明显,如果过滤文本在延迟完成之前发生变化,我想取消该过程并使用新的 fitler 文本开始新的过程。

using System;
using System.Threading;
using System.Threading.Tasks;
using System.Windows.Controls;

namespace pav.skillsToCompetenciesMapper.Views
{
    public partial class MapSkillsPage : Page
    {
        CancellationTokenSource cts;

        private async void Search_TEXTBOX_TextChanged(object sender, TextChangedEventArgs e)
        {
            if (cts != null) cts.Cancel();
            var searchText = Search_TEXTBOX.Text;

            try
            {
                using (cts = cts ?? new CancellationTokenSource())
                    await Task.Delay(3000, cts.Token).ContinueWith(tr =>
                    {
                        var st = searchText;
                        //Do search here
                    }, TaskContinuationOptions.NotOnCanceled);

            }
            catch (OperationCanceledException) { }
            finally { cts = null; }
        }
    }
}

现在上面的方法似乎对我有用,我只是担心这个 try catch 解决方案有点笨拙,看起来好像我应该能够使用 TaskContinuation.OnlyOnCanceled 来避免使用尝试抓住逻辑。对我来说就像代码的味道,但这是旁注。

我真正的问题出现在我尝试实际搜索 Treeview 时,上面的 "Do Search Here" 评论是

foreach (TreeViewItem category in Abilities_TreeView.Items)
    foreach (DragableTreeViewItem ability in category.Items)
        if (!ability.Header.ToString().Contains(filterText))
            ability.Visibility = Visibility.Hidden;

如能提供任何帮助,我将不胜感激,我怀疑这与尝试从后台线程访问 UI 线程有关,但我不能 100% 确定我是否我叫对了树。

如果您不想处理 OperationCanceledException,您可以使用仅接受继续操作的 ContinueWith 方法的重载并检查 IsCanceled 的值属性 在此操作中:

try
{
    using (cts = cts ?? new CancellationTokenSource())
        await Task.Delay(3000, cts.Token).ContinueWith(tr =>
        {
            if (!tr.IsCanceled)
            {
                var st = searchText;
                //Do search here
            }
        });
}
finally { cts = null; }

thanks, sorry i was a bit trigger happy, and didn't finish asking my question

除了最初创建它的调度程序线程之外,您无法从任何其他线程访问 TreeView,但您可以通过使用重载确保继续操作将在此线程上执行接受 TaskScheduler:

await Task.Delay(3000, cts.Token).ContinueWith(tr =>
{
    if (!tr.IsCanceled)
    {
        var st = searchText;
        //Do search here
    }
}, CancellationToken.None, TaskContinuationOptions.None, TaskScheduler.FromCurrentSynchronizationContext());