在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();
    }

在Form_Load期间异步地向Form添加控件

老式的方法是使用BackgroundWorkergetAndDisplayData中运行阻塞工作,然后在启动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);