我希望在我的MainForm正在执行一些与数据库相关的工作时显示带有Progressbar的新表单。我已经读过你不想将UI传递给新的线程,所以我想知道解决这个问题的最佳解决方案是什么?
我无法在另一个线程上执行与数据库相关的工作,因为它使用了对UI和MainForm的十几个调用。我也不能使用Application.DoEvents()
,因为MainForm的线程在一个类中工作,据我所知,你不应该对类的父类进行任何调用。
任何建议都将不胜感激!
修改
澄清我实际想知道的事情:
我的程序看起来有点像这样:
... Content = DatabaseClass.LoadContent();
ApplyContentToUI(Content);
我想做的是以下内容:
//Show a form here with a progressbar
... Content = DatabaseClass.LoadContent();
ApplyContentToUI(Content);
//Close the form here
但是,因为执行ApplyContentToUI的时间是aprox。 1秒我决定做以下事情:
//Show a form here with a progressbar
... Content = DatabaseClass.LoadContent();
//Close the form here
ApplyContentToUI(Content);
如果您和我不同,需要在重新加载用户界面时显示进度条,我建议您查看下面的评论和答案。
答案 0 :(得分:9)
我无法在另一个线程上完成与数据库相关的工作
假。
正如许多人在评论中提到的那样,您应该重构代码以将业务逻辑与UI逻辑分开。每个.NET版本都有已知的模式和实践,可以实现清晰的分离。我已经将一些工具和链接放在他们的用法中供您查看。
Control.BeginInvoke
方法可用于异步操作(.NET 2.0 +)。 BeginInvoke
用于在控件源自的线程上异步运行代码。这将允许在原始线程是UI线程的情况下发生UI操作。 BackgroundWorker
将此用于ReportProgress
。这将允许UI在长时间运行操作时保持响应。
这不应该直接嵌入到业务逻辑中。但是,它可以用于将消息中继回UI线程。
请参阅:
BackgroundWorker
将在一个单独的线程上运行,它具有事件驱动的进展(.NET 2.0 +)。 BackgroundWorker
是一个事件驱动的控件,它将在另一个线程上执行代码。它具有内置功能,可以使用ReportProgress
向听众通知进度。
在幕后,BackgroundWorker
使用Control.BeginInvoke
ReportProgress
,可以有效地分离业务逻辑。
请参阅:
Dispatcher
可以将操作转发到UI线程(.NET 3.0 +); 为WPF添加了Dispatcher
,但它仍然可以在WinForms中使用(尽管不太理想)。 Dispatcher
将以与BeginInvoke
相同的方式运行,并且可以同步或异步运行。
请参阅:
Task(T)
类使得从UI线程交织逻辑变得简单。 (.NET 4.0 +,带有TPL包的.NET 3.5+)。添加了任务并行库,以提供一组并行和并发的抽象。 Task
是一个消息驱动的,允许封装非阻塞操作。这也是.NET 4.5 async
/ await
模式的基础。
Task
用于流操作,以便它们可以链接在一起并生成结果。这比BackgroundWorker
更精简,它可以更好地组合操作。这也使实施producer-consumer scenarios更简单。
请参阅:
答案 1 :(得分:1)
@Servy建议我不是混合UI和逻辑,而是首先执行逻辑,然后将结果应用到UI。虽然这不符合最初的目的,但它似乎是解决我问题的最简单方法。 (具有进度条的表单仅在执行逻辑时可见,但现在在更新UI时可见。)UI更新将在aprox中完成。因为时间很短,所以最后并不需要进度条。
对于那些在加载UI时仍然需要进度条的人,在这个问题下给出了一些好的答案/解决方案。