在Form_Load期间异步地向Form添加控件
本文关键字:Form 控件 异步 添加 Load | 更新日期: 2023-09-27 18:10:58
我正在制作一个实用程序,用于将目录复制到多个USB棒。当Form1加载时,我想要一个标签来显示状态"检测磁盘驱动器…",然后调用一个方法来读取驱动器并使用信息填充表单。我有它的工作,除了当表单加载它调用显示标签之前的方法。因此,它看起来是挂着的(标签实际上是灰色背景上的白色盒子)。我尝试过计时器和线程以及我能想到的所有方法,但每种方法都有不同的死胡同。在调用读取驱动器的方法之前,我还没有找到一种方法来更新标签。
方法getAndDisplayData()是等待'挂起'我的程序。我希望它不会被调用,直到表单更新了lblDisplayStatus的文本。文本我也不希望用户在调用方法之前必须与表单交互。
下面是我的c#代码: private void USB_Utility_Load(object sender, EventArgs e)
{
lblDisplayStatus.Text = "Detecting Disk Drives...";
}
private void tabUSB_Prep_Enter(object sender, EventArgs e)
{
tabUSB_Prep.Controls.Clear();
getAndDisplayData();
}
任何帮助都将是非常感激的。
下面是我最后写的代码: BackgroundWorker _worker;
private void USB_Utility_Load(object sender, EventArgs e)
{
_worker = new BackgroundWorker(); // Should be a field on the form.
_worker.DoWork += DoWork;
_worker.RunWorkerCompleted += RunWorkerCompleted;
lblDisplayStatus.Text = "Detecting Disk Drives...";
_worker.RunWorkerAsync();
}
//Background Worker
private void RunWorkerCompleted(object sender, RunWorkerCompletedEventArgs e)
{
lblDisplayStatus.Text = "Done...";
displayData();
}
private void DoWork(object sender, DoWorkEventArgs e)
{
getData();
}
老式的方法是使用BackgroundWorker
在getAndDisplayData
中运行阻塞工作,然后在启动worker之前更新标签,并在worker完成时再次更新标签。
现在我假设你也可以使用任务来获得完全相同的结果,但我还没有真正尝试过,因为WinForms通常不是新项目的首选。
BackgroundWorker _worker;
public void Form_Load(object sender, EventArgs e) {
_worker = new BackgroundWorker(); // Should be a field on the form.
_worker.DoWork += DoWork;
_worker.RunWorkerCompleted += RunWorkerCompleted;
lblDisplayStatus.Text = "Detecting Disk Drives...";
_worker.RunWorkerAsync();
}
private void RunWorkerCompleted(object sender, RunWorkerCompletedEventArgs e) {
lblDisplayStatus.Text = "Done...";
}
private void DoWork(object sender, DoWorkEventArgs e) {
getAndDisplayData();
}
关于后台工作者
你可以试试这个
private void USB_Utility_Load(object sender, EventArgs e)
{
lblDisplayStatus.Text = "Detecting Disk Drives...";
}
private void tabUSB_Prep_Enter(object sender, EventArgs e)
{
tabUSB_Prep.Controls.Clear();
Task<List<string>> t = new Task<List<string>>(DetectDrivesMethod());
t.ContinueWith((result)=>DisplayDrives(result.Result),TaskScheduler.FromCurrentSynchronizationContext);
t.Start();
}
您可以调整代码以满足您的需求。在DetectDriveMethod中,你将有逻辑在后台线程中获取数据,在continue with中,你可以有逻辑来更新UI。传递同步上下文是很重要的,否则将导致交叉线程异常。
如果你想使用新的(ish) async/await模式,你需要使用TaskScheduler从原始线程更新UI。下面是一个例子:
// clear the form
tabUSB_Prep.Controls.Clear();
// This is just to show crossing a "context" works
string test = "";
// get the UI's current TaskScheduler
var scheduler = TaskScheduler.FromCurrentSynchronizationContext();
// This can be used to wrap a method that doesn't
// directly implement async/await
Task.Run(() =>
{
// Your method to GET the data (don't update the UI here)
test = "I can set a variable in this context!";
}).ContinueWith(task =>
{
if (task.Status == TaskStatus.RanToCompletion)
{
// update your UI here
// Again, this is just to show how crossing the context works
MessageBox.Show(test);
}
else
{
// update UI with an error message, or display a MessageBox?
}
}, scheduler);