如何使用backgroundworker更新GUI?
如何使用backgroundworker更新GUI?
我整天都在尝试让我的应用程序使用线程,但是一直没有成功。我阅读了很多相关文档,但仍然出现很多错误,所以希望你能帮助我。
我有一个耗时很长的方法,它调用数据库并更新GUI。这个操作必须一直进行(大约每30秒一次)。
使用这种方法,我会得到一个异常,因为后台工作线程不是STA线程(但据我了解,这是我应该使用的)。我已经尝试过使用STA线程,但是又出现了其他错误。
我认为问题是因为我在后台线程中进行数据库调用时尝试更新GUI。我应该只是进行数据库调用,然后以某种方式切换回主线程。主线程执行完后,应该再切回后台线程,如此往复。但是我不知道如何做到这一点。
应用程序应该在数据库调用之后立即更新GUI。触发事件似乎不起作用。后台线程只是进入这些事件。
编辑:
一些非常好的答案 🙂 这是新代码:
public class UpdateController{
private UserController _userController;
private BackgroundWorker _backgroundWorker;
public UpdateController(LoginController loginController, UserController userController)
{
_userController = userController;
loginController.LoginEvent += Update;
_backgroundWorker = new BackgroundWorker();
_backgroundWorker.DoWork += backgroundWorker_DoWork;
_backgroundWorker.RunWorkerCompleted += backgroundWorker_RunWorkerCompleted;
}
public void _backgroundWorker_ProgressChanged(object sender, ProgressChangedEventArgs e)
{
_userController.UpdateUsersOnMap();
}
public void Update()
{
_backgroundWorker.RunWorkerAsync();
}
void backgroundWorker_RunWorkerCompleted(object sender, RunWorkerCompletedEventArgs e)
{
// 更新UI
System.Threading.Thread.Sleep(10000);
Update();
}
public void backgroundWorker_DoWork(object sender, DoWorkEventArgs e)
{
// 大的数据库任务
}
}
但是,如何每10秒运行一次?System.Threading.Thread.Sleep(10000)只会导致GUI冻结,而在Update()中使用while(true)循环会引发异常(线程过于繁忙)。
如何使用BackgroundWorker更新GUI?
问题原因:
问题出现的原因是在代码中使用了无限循环(while(true)),导致无限添加事件处理程序并无限次地调用它们。
解决方法:
解决这个问题的方法是删除无限循环(while(true))的部分代码,以避免无限添加事件处理程序和无限调用它们。
以下是修复后的代码示例:
private void button1_Click(object sender, EventArgs e) { // 创建并启动BackgroundWorker BackgroundWorker bgWorker = new BackgroundWorker(); bgWorker.DoWork += bgWorker_DoWork; bgWorker.RunWorkerCompleted += bgWorker_RunWorkerCompleted; bgWorker.RunWorkerAsync(); } private void bgWorker_DoWork(object sender, DoWorkEventArgs e) { // 在这里执行耗时的操作 } private void bgWorker_RunWorkerCompleted(object sender, RunWorkerCompletedEventArgs e) { // 更新GUI }
在修复后的代码中,我们使用了BackgroundWorker来执行耗时的操作,并在操作完成后更新GUI。通过将事件处理程序添加到BackgroundWorker的相关事件中,可以确保在后台线程执行操作时不会阻塞主线程。
请注意,这只是一个示例,你需要根据自己的实际需求来修改代码。希望这篇文章能帮助你解决如何使用BackgroundWorker更新GUI的问题。
如何使用BackgroundWorker更新GUI?
在这个问题中,出现的原因是BackgroundWorker无法同时运行多个任务。解决方法是将循环放在单个的DoWork方法中,并通过ReportProgress方法向UI线程发送通知。
首先,需要在声明和配置BackgroundWorker时只进行一次。然后,在循环中调用RunWorkerAsync方法。
具体代码如下:
public class UpdateController { private UserController _userController; private BackgroundWorker _backgroundWorker; public UpdateController(LoginController loginController, UserController userController) { _userController = userController; loginController.LoginEvent += Update; _backgroundWorker = new BackgroundWorker(); _backgroundWorker.DoWork += new DoWorkEventHandler(backgroundWorker_DoWork); _backgroundWorker.ProgressChanged += new ProgressChangedEventHandler(backgroundWorker_ProgressChanged); _backgroundWorker.WorkerReportsProgress= true; } public void Update() { _backgroundWorker.RunWorkerAsync(); } public void backgroundWorker_DoWork(object sender, DoWorkEventArgs e) { while (true) { // 在这里执行长时间的任务,并可选择将更新发送回UI线程... int p = 0;// 如果适用,设置进度 object param = "something"; // 可以使用此参数将任何附加参数传递回UI _backgroundWorker.ReportProgress(p, param); } } // 此事件处理程序更新UI private void backgroundWorker_ProgressChanged(object sender, ProgressChangedEventArgs e) { // 在这里更新UI // _userController.UpdateUsersOnMap(); } }
然而,这段代码会抛出异常:"This BackgroundWorker is currently busy and cannot run multiple tasks concurrently."。解决方法是将循环放在单个的DoWork方法中。修改后的代码如下:
public class UpdateController { private UserController _userController; private BackgroundWorker _backgroundWorker; public UpdateController(LoginController loginController, UserController userController) { _userController = userController; loginController.LoginEvent += Update; _backgroundWorker = new BackgroundWorker(); _backgroundWorker.DoWork += new DoWorkEventHandler(backgroundWorker_DoWork); _backgroundWorker.ProgressChanged += new ProgressChangedEventHandler(backgroundWorker_ProgressChanged); _backgroundWorker.WorkerReportsProgress= true; } public void Update() { _backgroundWorker.RunWorkerAsync(); } public void backgroundWorker_DoWork(object sender, DoWorkEventArgs e) { while (true) { // 在这里执行长时间的任务,并可选择将更新发送回UI线程... int p = 0;// 如果适用,设置进度 object param = "something"; // 可以使用此参数将任何附加参数传递回UI _backgroundWorker.ReportProgress(p, param); } } // 此事件处理程序更新UI private void backgroundWorker_ProgressChanged(object sender, ProgressChangedEventArgs e) { // 在这里更新UI // _userController.UpdateUsersOnMap(); } }
此时,你可以在循环中通过ReportProgress方法向UI线程发送通知,如果需要,还可以为Completed添加方法处理程序以跳出无限循环。此外,你还可以通过在worker线程中使用Sleep()方法让线程等待10秒,而不会影响UI线程。
然而,有人反馈说这种方法导致屏幕之间的动画不起作用。这可能是因为线程的原因,需要进一步排查。
问题的出现原因是在后台线程中更新GUI,而GUI操作只能在主线程中执行,所以需要一种方法将GUI操作转移到主线程。解决方法是使用Control.InvokeRequired属性判断是否在后台线程,然后使用Control.Invoke方法调用修改UI的逻辑,强制UI操作在主线程上执行。需要创建一个委托并将其传递给Control.Invoke方法。代码示例中的UpdateController类演示了如何使用BackgroundWorker来更新GUI。
public class UpdateController { private UserController _userController; BackgroundWorker backgroundWorker = new BackgroundWorker(); public UpdateController(LoginController loginController, UserController userController) { _userController = userController; loginController.LoginEvent += Update; } public void Update() { // 这里的while循环是不必要的 backgroundWorker.DoWork += new DoWorkEventHandler(backgroundWorker_DoWork); backgroundWorker.RunWorkerAsync(); } public delegate void DoUIWorkHandler(); public void backgroundWorker_DoWork(object sender, DoWorkEventArgs e) { // 在这里必须检查是否在后台线程执行 // UI操作只允许在主应用程序线程上执行 if (someControlOnMyForm.InvokeRequired) { // 这是如何强制将逻辑调用在主应用程序线程上的方法 someControlOnMyForm.Invoke(new DoUIWorkHandler(_userController.UpdateUsersOnMap)); } else { _userController.UpdateUsersOnMap(); } } }
这段代码展示了如何在后台线程中使用BackgroundWorker更新GUI。通过在Update方法中使用BackgroundWorker的DoWork事件和RunWorkerAsync方法启动后台线程,然后在backgroundWorker_DoWork方法中检查是否需要在主线程上执行UI操作。如果需要,使用Control.Invoke方法将UI操作转移到主线程上执行。这样就可以在后台线程中更新GUI而不会导致线程冲突。
这种方法可以解决在后台线程中更新GUI的问题,但在代码示例中提到可能需要对程序进行大量更改才能适应这种方法。所以,在实际应用中,需要根据具体情况考虑是否使用这种方法。