我可以等待字符串更改吗?

axb*_*eit 0 c# asynchronous task async-await uwp

我有一个带有PDFReader的UWP应用。PDFReader有一个搜索窗口。在搜索文本框中输入单词后,便可以开始搜索。当前,这样一来,如果您开始搜索,则必须等待3秒钟,直到看到一条消息,该单词的出现频率很高。

现在,我想用“正在搜索...”消息填充这3秒钟。

我有一个解决方案是:

private async void DoSearch(...)
{
//...
Task<string> searchingText = setSearchMsg();
infoBox.Text = await searchingText;
//...
}

private async Task<string> setSearchMsg()
{
infoBox.Text = "Searching...";
await Task.Delay(1);
return "";
}
Run Code Online (Sandbox Code Playgroud)

但这看起来并不正确。我注意到的一件事是,当我正在寻找一个单词并且可以在80页中找到3000次该单词时,infoBox跳过“正在搜索...”消息,并再次将信息框填充为空。为了解决这个问题,我可以将更Task.Delay改为like Task.Delay(100),这并不是我认为正确的解决方案。

Eni*_*ity 5

您应该使用Microsoft的Reactive Framework(aka Rx)-NuGet System.Reactive.Windows.Threading并添加using System.Reactive; using System.Reactive.Linq;-然后您可以做一些令人惊奇的事情。

我模拟了一些类似于您的代码。我从TextBoxTextBlock和一个用于执行搜索的虚拟方法开始:

    private async Task<string> PerformSearchAsync(string text)
    {
        await Task.Delay(TimeSpan.FromSeconds(2.0));
        return "Hello " + text;
    }
Run Code Online (Sandbox Code Playgroud)

现在,在构造函数中,this.InitializeComponent();我创建了以下可观察到的对象,以响应TextChanged我的所有事件textBox1

        IObservable<EventPattern<TextChangedEventArgs>> textChanges =
            Observable
                .FromEventPattern<TextChangedEventHandler, TextChangedEventArgs>(
                    h => textBox1.TextChanged += h,
                    h => textBox1.TextChanged -= h);
Run Code Online (Sandbox Code Playgroud)

然后我写了一个查询来响应文本更改:

        IObservable<string> query =
            textChanges
                .Select(x =>
                    Observable
                        .FromAsync(() => PerformSearchAsync(textBox1.Text))
                        .StartWith("Searching..."))
                .Switch();
Run Code Online (Sandbox Code Playgroud)

这实际上是在等待每个文本更改并启动异步调用PerformSearchAsync-这将以字符串形式返回搜索结果-并且还将立即返回该字符串"Searching..."。将.Switch()用于确保只有最新的呼叫PerformSearchAsync实际返回的结果。

现在,我可以简单地观察查询:

        _subscription =
            query
                .ObserveOnDispatcher()
                .Subscribe(x => textBlock1.Text = x);
Run Code Online (Sandbox Code Playgroud)

变量_subscription定义为private IDisposable _subscription = null;。它用于使我可以调用_subscription.Dispose()以安全地关闭订阅。

它表现出您想要的方式。

我的整个代码如下所示:

using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Reactive;
using System.Reactive.Linq;
using System.Runtime.InteropServices.WindowsRuntime;
using System.Threading.Tasks;
using Windows.Foundation;
using Windows.Foundation.Collections;
using Windows.UI.Xaml;
using Windows.UI.Xaml.Controls;
using Windows.UI.Xaml.Controls.Primitives;
using Windows.UI.Xaml.Data;
using Windows.UI.Xaml.Input;
using Windows.UI.Xaml.Media;
using Windows.UI.Xaml.Navigation;

namespace App5
{
    /// <summary>
    /// An empty page that can be used on its own or navigated to within a Frame.
    /// </summary>
    public sealed partial class MainPage : Page
    {
        private IDisposable _subscription = null;

        public MainPage()
        {
            this.InitializeComponent();

            IObservable<EventPattern<TextChangedEventArgs>> textChanges =
                Observable
                    .FromEventPattern<TextChangedEventHandler, TextChangedEventArgs>(
                        h => textBox1.TextChanged += h,
                        h => textBox1.TextChanged -= h);

            IObservable<string> query =
                textChanges
                    .Select(x =>
                        Observable
                            .FromAsync(() => PerformSearchAsync(textBox1.Text))
                            .StartWith("Searching..."))
                    .Switch();

            _subscription =
                query
                    .ObserveOnDispatcher()
                    .Subscribe(x => textBlock1.Text = x);
        }

        private async Task<string> PerformSearchAsync(string text)
        {
            await Task.Delay(TimeSpan.FromSeconds(2.0));
            return "Hello " + text;
        }
    }
}
Run Code Online (Sandbox Code Playgroud)

您应该能够对此进行调整以使其与代码一起使用。