【问题标题】:How to update a Windows Form textbox value while waiting for a web response如何在等待 Web 响应时更新 Windows 窗体文本框值
【发布时间】:2020-03-09 17:48:16
【问题描述】:

我正在等待返回网络响应时尝试更新状态消息。该调用将文件发布到服务器,有时可能需要 30 多秒。

如果通话花费的时间比预期的要长,我想更新消息(Windows 窗体文本框文本)。如果呼叫已等待 15 秒,请将消息更新为“这需要一段时间,但应该很快完成。”

我试过了:

  • 异步触发并忘记
  • 使用调用的计时器
  • task.run
  • 两个任务都是异步的,等待网络调用任务
  • 使用 dowork 和进度工作的后台 Worker

似乎没有任何效果。是否有可能在任务锁定线程时更新主线程?

我正在使用简单的调用进行测试:


private void worker_DoWork(object sender, DoWorkEventArgs e)
{
    while (!worker.CancellationPending)
    {
        Task.Delay(1000).Wait();
        this.Invoke((MethodInvoker)delegate
        {
            this.box.Text += '.';
            this.box.Update();
        });
    }
}

private void MakeCall()
{
    worker.RunWorkerAsync();
    using (WebClient client = new WebClient())
    {
        //Just runs Task.Delay(10000) then returns "Complete"
        var res = client.DownloadString("https://localhost:44343/api/TestDelay");
        MessageBox.Show(res);
    }
    worker.CancelAsync();
}

private void button_Click(object sender, EventArgs e)
{
    MakeCall();
}

【问题讨论】:

  • 您需要调用文本框才能更新文本。 this.Invoke 不是文本框。上面在StartMinder 中也给出了它被标记为async,使用invoke 与该方法相矛盾,因为invoke 也是一个阻塞调用。
  • 另外,private async Task MakeCall() VS 应该吐了,因为您在该例程中没有 awaiting 任何东西。
  • 它确实给出了标准的无等待错误,但我将它标记为异步作为我测试的一部分,以尝试让它们根据需要工作。将调用切换到 this.box.invoke 也不起作用。
  • @Çöđěxěŕ 您可以调用父级。无论如何,这就是预期的封送处理程序。
  • @Jimi 我知道你可以,我没说你不能。在这种情况下,为什么 OP 会调用父级?

标签: c# winforms


【解决方案1】:

我想我看到了你的问题。您在 UI 线程上下载而从未离开它,因此后台工作人员也永远无法进入它。

试试这个代码:

编辑:使用两个任务而不是后台工作人员

private void MakeCall()
{
    // it'd be a good idea to disable the button here
    ManualResetEventSlim waiter = new ManualResetEventSlim(false);
    Task.Run(async () => 
    {
        while(!waiter.IsSet) 
        {
            await Task.Delay(1000);  
            this.Invoke((MethodInvoker)delegate
            {
                this.box.Text += '.';
                this.box.Update();
            });                
        } 
    });
    Task.Run(() => 
    {
        using (WebClient client = new WebClient())
        {
            var res = client.DownloadString("https://localhost:44343/api/TestDelay");
            MessageBox.Show(res);
        }

        waiter.Set();
        // hop back on the UI thread and re-enable your button here
    });
}

private void button_Click(object sender, EventArgs e)
{
    MakeCall();
}

【讨论】:

  • 谢谢。我试过这个方法,结果还是一样。 worker_DoWork 在 MakeCall 完成之前不会运行
  • @Seth 好的,试试我放在那里的最新更新代码。如果这不起作用,现在就向我开枪。
  • 实际上,您的第一个建议有效(我认为两者都有效)。我的构建版本错误,只是没有显示正确的结果。谢谢!
【解决方案2】:

我在这里提供了一个额外的答案,因为虽然 outbred 的答案在我的测试中有效,但它在我的原始程序中不起作用,因为表单需要保持打开状态,锁定主表单,运行定义的任务之一,然后自动关闭。这意味着异步触发和忘记方法不是一个选项。 我所做的是重载表单 ShowDialog 方法以采取异步操作,触发它,显示对话框,然后在完成时关闭对话框。 此方法完美运行,锁定父级并允许后台工作人员更新文本。

internal DialogResult ShowDialog(Action action) => ShowDialog(async () => await Task.Run(action));
internal DialogResult ShowDialog(Func<Task> action)
{
    action.Invoke().ContinueWith(task => this.DialogResult = DialogResult.OK);
    return this.ShowDialog();
}

那么你可以使用以下方式之一调用它:

using (Form1 form = new Form1())
    form.ShowDialog(form.MakeCall);
using (Form1 form = new Form1())
    form.ShowDialog(() => { form.MakeCall("HelloWorld");} );
using (Form1 form = new Form1())
    form.ShowDialog(async () => { await form.MakeCallAsync("HelloWorld");} );

它将表单显示为对话框(锁定父级),运行任务完成,然后关闭表单。

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2022-01-19
    • 2021-05-07
    • 2022-08-23
    • 2020-05-09
    • 1970-01-01
    • 2011-01-29
    相关资源
    最近更新 更多