Warning: file_get_contents(/data/phpspider/zhask/data//catemap/2/csharp/313.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
C# 更新任务中的UI控件_C#_Winforms_Asynchronous_Async Await_Task - Fatal编程技术网

C# 更新任务中的UI控件

C# 更新任务中的UI控件,c#,winforms,asynchronous,async-await,task,C#,Winforms,Asynchronous,Async Await,Task,如果我像这样更新任务中的UI控件,这对代码正确吗? 或者它是错误的,我需要使用类似smth的控件。调用 private async void button1_Click(object sender, EventArgs e) { textBox1.Text = await Task<string>.Factory.StartNew(() => { Foo(); return "Completed"; }); } priv

如果我像这样更新任务中的UI控件,这对代码正确吗?
或者它是错误的,我需要使用类似smth的控件。调用

private async void button1_Click(object sender, EventArgs e)
{
    textBox1.Text = await Task<string>.Factory.StartNew(() =>
    {
        Foo();
        return "Completed";
    });
}

private void Foo()
{
    for (int i = 0; i < 100; i++)
    {
        textBox1.Text = i.ToString();
        Thread.Sleep(1000);
    }
}
private async void按钮1\u单击(对象发送方,事件参数e)
{
textBox1.Text=wait Task.Factory.StartNew(()=>
{
Foo();
返回“已完成”;
});
}
私人文件
{
对于(int i=0;i<100;i++)
{
textBox1.Text=i.ToString();
睡眠(1000);
}
}

您的
Foo
方法将抛出,因为您正试图从后台线程更新UI控件。 如果
Foo
做了一些随意的事情,没有演变任何UI控件,那么您的
textBox1.Text=await Task.Factory.StartNew
就可以了,因为您使用
await
并且
SynchronizationContext
将在
await
完成后隐式捕获并使用,这将在UI线程上进行分配


这没有多大意义,但是如果您想从
任务
内部更新控件,则必须使用
控件。调用

您的
Foo
方法将抛出,因为您正试图从后台线程更新UI控件。 如果
Foo
做了一些随意的事情,没有演变任何UI控件,那么您的
textBox1.Text=await Task.Factory.StartNew
就可以了,因为您使用
await
并且
SynchronizationContext
将在
await
完成后隐式捕获并使用,这将在UI线程上进行分配


这没有多大意义,但是如果您想从
任务
内部更新控件,则必须使用
控件。调用

您只需运行代码就可以看到它不起作用,并且当您从非UI线程触摸UI时,程序将崩溃

您应该将UI更新为计划在UI线程中运行的任务的延续<代码>等待
为您完成所有这些,使代码变得非常简单

您也不应该创建一个线程池线程,而只是让它坐在那里,在固定的睡眠时间内什么也不做。使用
任务。改为延迟

private async void button1_Click(object sender, EventArgs e)
{
    for (int i = 0; i < 100; i++)
    {
        textBox1.Text = i.ToString();
        await Task.Delay(1000);
    }
    textBox1.Text = "Completed";
}
private async void按钮1\u单击(对象发送方,事件参数e)
{
对于(int i=0;i<100;i++)
{
textBox1.Text=i.ToString();
等待任务。延迟(1000);
}
textBox1.Text=“已完成”;
}

当您从非UI线程触摸UI时,只需运行代码即可查看它是否工作,以及程序是否会崩溃

您应该将UI更新为计划在UI线程中运行的任务的延续<代码>等待
为您完成所有这些,使代码变得非常简单

您也不应该创建一个线程池线程,而只是让它坐在那里,在固定的睡眠时间内什么也不做。使用
任务。改为延迟

private async void button1_Click(object sender, EventArgs e)
{
    for (int i = 0; i < 100; i++)
    {
        textBox1.Text = i.ToString();
        await Task.Delay(1000);
    }
    textBox1.Text = "Completed";
}
private async void按钮1\u单击(对象发送方,事件参数e)
{
对于(int i=0;i<100;i++)
{
textBox1.Text=i.ToString();
等待任务。延迟(1000);
}
textBox1.Text=“已完成”;
}

BCL通过在类中实现的接口解决此特定场景,以提供丰富的异步进度报告。这在.NET4.5或带有Nuget包的.NET4中提供。许多BCL类接受IProgress参数来进行进度报告

Servy的回答解决了异步操作后如何更新UI的直接问题,但这迫使您在长时间运行的操作中混合UI代码
IProgress
允许您使用报表数据进行调用,而无需考虑将调用编组到适当的线程、同步上下文、特定于UI的调用等

您的代码可以如此简单:

private async void button1_Click(object sender, EventArgs e)
{
    var progress=new Progress<string>(msg=>textBox1.Text = msg);
    await Task<string>.Factory.StartNew(() => Foo(progress));
}

private void Foo(IProgress<string> progress)
{
    for (int i = 0; i < 100; i++)
    {
        progress.OnReport(i.ToString());
        Thread.Sleep(1000);
    }
    progress.OnReport("Finished");
}
private async void按钮1\u单击(对象发送方,事件参数e)
{
var progress=新进度(msg=>textBox1.Text=msg);
wait Task.Factory.StartNew(()=>Foo(progress));
}
私人void Foo(IProgress progress)
{
对于(int i=0;i<100;i++)
{
progress.OnReport(i.ToString());
睡眠(1000);
}
进度报告(“完成”);
}
或者您可以使用更复杂的进度类型,例如

class MyProgressData
{
    public string Message{get;set;}
    public int Iteration {get;set;}
    public MyProgressData(string message,int iteration) ...
}

private async void button1_Click(object sender, EventArgs e)
{
    var progress=new Progress<MyProgressData>(msg=>{
        textBox1.Text = msg.Message;
        textBox2.Text=msg.Iteration.ToString();
    });
    await Task<string>.Factory.StartNew(() => Foo(progress));
}

private void Foo(IProgress<MyProgressData> progress)
{
    for (int i = 0; i < 100; i++)
    {
        progress.OnReport(new MyProgressData("Hi",i));
        Thread.Sleep(1000);
    }
    progress.OnReport("Finished");
}
类MyProgressData
{
公共字符串消息{get;set;}
公共整数迭代{get;set;}
公共MyProgressData(字符串消息,整数迭代)。。。
}
私有异步无效按钮1\u单击(对象发送方,事件参数e)
{
var progress=新进度(msg=>{
textBox1.Text=msg.Message;
textBox2.Text=msg.Iteration.ToString();
});
wait Task.Factory.StartNew(()=>Foo(progress));
}
私人void Foo(IProgress progress)
{
对于(int i=0;i<100;i++)
{
进度报告(新的MyProgressData(“Hi”,i));
睡眠(1000);
}
进度报告(“完成”);
}

这样做的好处在于,您可以将处理与报告完全解耦。您可以将处理代码放在与UI完全不同的类中,甚至放在项目中。

BCL通过在类中实现的接口来解决此特定场景,以提供丰富的异步进度报告。这在.NET4.5或带有Nuget包的.NET4中提供。许多BCL类接受IProgress参数来进行进度报告

Servy的回答解决了异步操作后如何更新UI的直接问题,但这迫使您在长时间运行的操作中混合UI代码
IProgress
允许您使用报表数据进行调用,而无需考虑将调用编组到适当的线程、同步上下文、特定于UI的调用等

您的代码可以如此简单:

private async void button1_Click(object sender, EventArgs e)
{
    var progress=new Progress<string>(msg=>textBox1.Text = msg);
    await Task<string>.Factory.StartNew(() => Foo(progress));
}

private void Foo(IProgress<string> progress)
{
    for (int i = 0; i < 100; i++)
    {
        progress.OnReport(i.ToString());
        Thread.Sleep(1000);
    }
    progress.OnReport("Finished");
}
private async void按钮1\u单击(对象发送方,事件参数e)
{
var进度=新进度(m