WPF: 如何在单独的线程中延迟一段时间后调用方法

4
我有一个TextBox,用户可以在其中输入搜索词。它与我的view-model中的string Term属性绑定。当其内容更改时,我想进行搜索查询。但我想要在一个单独的线程上进行查询,并且带有一定的延迟。

例如,当用户输入一个字母时,我想等待0.3秒,如果用户在此期间(0.3秒)内更改输入,则计时器重置并重新开始。否则,我将启动一个新线程并进行搜索查询。在查询正在执行时,如果用户再次更改术语,则中止先前的查询并重新开始。

我知道如何使用线程和Timer类在Windows窗体中实现此目的。但我是WPF的新手,我正在寻找是否有专门针对WPF线程功能的方法(或者可能有更好性能的方法)。

你有什么想法吗?你能帮我吗?

3个回答

2

您可以使用DispatcherTimer。每次按键时,如果计时器已经在运行,则停止计时器,然后启动它。我认为(您应该检查一下!)这将重新设置计时器。

如果计时器触发,那么您就需要在单独的线程中开始执行文本框中的当前值(例如使用Task.Factory.StartNew(如果您使用的是.NET 4),或者BackgroundWorker,或者只是创建一个新线程)。

基本上,这将把“创建新线程”部分与“我是否真的想做某事”部分分开,直到您决定您确实去做某事并且您知道要使用的值为止,所有内容都保持在UI线程上。


嗯,你是Jon Skeet,我不太明白你在说什么!!! :D 我对WPF非常陌生,只有187个SO点。你的解决方案似乎比Robaticus的更好,但我需要一些代码来理解它:D 我在考虑,我应该只有一个线程(而不是在每个查询上都有一个新线程,这似乎更好),然后运行或中止它。这是真的吗?同时很高兴和你交流。非常感谢。 - amiry jd
1
@king.net:您不想中止线程,但如果输入发生了更改,您可以始终忽略结果。关于这方面的内容,请阅读“DispatcherTimer”,然后单独处理线程。 - Jon Skeet
1
@king.net:停止进一步的“ticks”吗?绝对可以。 - Jon Skeet
非常感谢,它起作用了。还要感谢您让我学会了DispatcherTimer。然而,有一种情况可能会导致逻辑错误:1:术语更改,2:timer.stop()timer.start() 3:用户在0.3秒内未更改术语,因此4:查询开始调用,5:当查询完成后,我再次停止计时器,但是6:调用查询需要一段时间(例如2秒),7:在查询正在调用时,用户更改了术语,8:发生timer.stop()(之后也是timer.start()),但只有计时器停止了,查询仍在运行!我该如何中止它?请问您有任何想法吗? - amiry jd
1
@king.net:一旦您发出查询,除非用户键入另一个字符,否则您根本不需要计时器。但是,通常情况下,您不会中止查询-您只需忽略结果即可。当然,如果您的查询确实支持取消,则很好-但是我们需要了解更多有关搜索的信息,以便知道如何在这方面为您提供帮助。 - Jon Skeet
显示剩余4条评论

0

你可能想要了解微软的响应式扩展。Rx 提供了一种将这些类型的事件聚合成单个事件的方法,一旦经过一定的延迟。

Phil Haack(曾在微软工作)在他的博客上有一篇很好的文章,其中讨论了节流能力。


0

这只是对Jon Skeets所说的内容的补充。请给他打勾。 .stop() 函数似乎可以重置计时器timer

public MainWindow()
{
    InitializeComponent();

    backgroundWorker1 = new BackgroundWorker();
    backgroundWorker1.WorkerReportsProgress = true;
    backgroundWorker1.WorkerSupportsCancellation = true;
    backgroundWorker1.DoWork += new DoWorkEventHandler(backgroundWorker1_DoWork);
    backgroundWorker1.RunWorkerCompleted += new RunWorkerCompletedEventHandler(backgroundWorker1_RunWorkerCompleted);
    backgroundWorker1.ProgressChanged += new ProgressChangedEventHandler(backgroundWorker1_ProgressChanged);

    dispatcherTimer.Tick += new EventHandler(dispatcherTimer_Tick);
    dispatcherTimer.Interval = new TimeSpan(0, 0, 4);
}

public string Input
{
    get { return input; }
    set
    {
        if (value == input) return;
        value = value.Trim();
        input = value;
        NotifyPropertyChanged("Input");

        if (backgroundWorker1.IsBusy) backgroundWorker1.CancelAsync();
        dispatcherTimer.Stop();                
        dispatcherTimer.Start();
    }
 }

private void dispatcherTimer_Tick(object sender, EventArgs e)
{
    dispatcherTimer.Stop();
    if (!backgroundWorker1.IsBusy)
    {
        backgroundWorker1.RunWorkerAsync(Input);
    }
}

正如John所说,如果我们有一个支持取消的查询,那么BackgroundWorker就不是必需的了。不过还是谢谢。 - amiry jd
在我的情况下,这不是一个查询。 - paparazzo
我完成了第一部分。但是还有另一个问题。你能看一下这个链接的解决方案吗:http://stackoverflow.com/questions/10534290/gui-froze-while-dispatcher-begininvoke-or-task-startnew - amiry jd

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接