C# 使用 WinForms ProgressBar 进行异步/等待
声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow
原文地址: http://stackoverflow.com/questions/17972268/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me):
StackOverFlow
Async/Await with a WinForms ProgressBar
提问by Todd Sprang
I've gotten this type of thing working in the past with a BackgroundWorker, but I want to use the new async/await approach of .NET 4.5. I may be barking up the wrong tree. Please advise.
我过去曾使用 BackgroundWorker 处理过这种类型的事情,但我想使用 .NET 4.5 的新异步/等待方法。我可能在吠错树。请指教。
Goal: Create a component that will do some long-running work and show a modal form with a progress bar as it's doing the work. The component will get the handle to a window to block interaction while it's executing the long-running work.
目标:创建一个组件,该组件将执行一些长时间运行的工作,并在执行工作时显示带有进度条的模态表单。该组件将获取窗口句柄以在执行长时间运行的工作时阻止交互。
Status: See the code below. I thought I was doing well until I tried interacting with the windows. If I leave things alone (i.e. don't touch!), everything runs "perfectly", but if I do so much as click on either window the program hangs after the long-running work ends. Actual interactions (dragging) are ignored as though the UI thread is blocked.
状态:请参阅下面的代码。我以为我做得很好,直到我尝试与窗户互动。如果我不理会(即不要触摸!),一切都会“完美”运行,但是如果我只单击任一窗口,程序会在长时间运行的工作结束后挂起。实际交互(拖动)被忽略,就像 UI 线程被阻止一样。
Questions: Can my code be fixed fairly easily? If so, how? Or, should I be using a different approach (e.g. BackgroundWorker)?
问题:我的代码可以很容易地修复吗?如果是这样,如何?或者,我应该使用不同的方法(例如 BackgroundWorker)吗?
Code(Form1 is a standard form with a ProgressBar and a public method, UpdateProgress, that sets the ProgressBar's Value):
代码(Form1 是一个标准表单,带有一个 ProgressBar 和一个公共方法 UpdateProgress,它设置 ProgressBar 的值):
using System;
using System.Diagnostics;
using System.Threading;
using System.Threading.Tasks;
using System.Windows.Forms;
namespace ConsoleApplication1
{
class Program
{
static void Main(string[] args)
{
Console.WriteLine("Starting..");
var mgr = new Manager();
mgr.GoAsync();
Console.WriteLine("..Ended");
Console.ReadKey();
}
}
class Manager
{
private static Form1 _progressForm;
public async void GoAsync()
{
var owner = new Win32Window(Process.GetCurrentProcess().MainWindowHandle);
_progressForm = new Form1();
_progressForm.Show(owner);
await Go();
_progressForm.Hide();
}
private async Task<bool> Go()
{
var job = new LongJob();
job.OnProgress += job_OnProgress;
job.Spin();
return true;
}
void job_OnProgress(int percent)
{
_progressForm.UpdateProgress(percent);
}
}
class LongJob
{
public event Progressed OnProgress;
public delegate void Progressed(int percent);
public void Spin()
{
for (var i = 1; i <= 100; i++)
{
Thread.Sleep(25);
if (OnProgress != null)
{
OnProgress(i);
}
}
}
}
class Win32Window : IWin32Window
{
private readonly IntPtr _hwnd;
public Win32Window(IntPtr handle)
{
_hwnd = handle;
}
public IntPtr Handle
{
get
{
return _hwnd;
}
}
}
}
采纳答案by YK1
@StephenCleary's answer is correct. Though, I had to make a little modification to his answer to get the behavior what I think OP wants.
@StephenCleary 的回答是正确的。尽管如此,我不得不对他的答案进行一些修改,以获得我认为 OP 想要的行为。
public void GoAsync() //no longer async as it blocks on Appication.Run
{
var owner = new Win32Window(Process.GetCurrentProcess().MainWindowHandle);
_progressForm = new Form1();
var progress = new Progress<int>(value => _progressForm.UpdateProgress(value));
_progressForm.Activated += async (sender, args) =>
{
await Go(progress);
_progressForm.Close();
};
Application.Run(_progressForm);
}
回答by Stephen Cleary
The async
and await
keywords do not mean "run on a background thread." I have an async
/await
intro on my blogthat describes what they domean. You must explicitly place CPU-bound operations on a background thread, e.g., Task.Run
.
在async
与await
关键字并不意味着“在后台线程上运行。” 我有一个async
/await
在我的博客的介绍,描述他们做的意思。您必须在后台线程上显式地放置 CPU 绑定操作,例如,Task.Run
.
Also, the Task-based Asynchronous Patterndocumentation describes the common approaches with async
code, e.g., progress reporting.
此外,基于任务的异步模式文档描述了async
代码的常见方法,例如进度报告。
class Manager
{
private static Form1 _progressForm;
public async Task GoAsync()
{
var owner = new Win32Window(Process.GetCurrentProcess().MainWindowHandle);
_progressForm = new Form1();
_progressForm.Show(owner);
var progress = new Progress<int>(value => _progressForm.UpdateProgress(value));
await Go(progress);
_progressForm.Hide();
}
private Task<bool> Go(IProgress<int> progress)
{
return Task.Run(() =>
{
var job = new LongJob();
job.Spin(progress);
return true;
});
}
}
class LongJob
{
public void Spin(IProgress<int> progress)
{
for (var i = 1; i <= 100; i++)
{
Thread.Sleep(25);
if (progress != null)
{
progress.Report(i);
}
}
}
}
Note that the Progress<T>
type properly handles thread marshaling, so there's no need for marshaling within Form1.UpdateProgress
.
请注意,该Progress<T>
类型正确处理线程封送处理,因此不需要在Form1.UpdateProgress
.
回答by YK1
private async void button1_Click(object sender, EventArgs e)
{
IProgress<int> progress = new Progress<int>(value => { progressBar1.Value = value; });
await Task.Run(() =>
{
for (int i = 0; i <= 100; i++)
progress.Report(i);
});
}
Correct me if I'm wrong, but this seems to be the easiest way to update a progress bar.
如果我错了,请纠正我,但这似乎是更新进度条的最简单方法。