WinForms:有关使我的UI独立于BLL层运行的实现问题?

时间:2020-03-06 15:03:09  来源:igfitidea点击:

我正在尝试以MVP风格制作Windows窗体应用程序,并且在弄乱之前没有对线程做太多事情。

我的UI是一组非常简单的表单。每个表单都实现一个接口,并包含对位于业务逻辑层中的介体类的引用,反之亦然。
因此,简化图如下所示:

CheckInForm : ICheckIn                      <-------> CheckInMediator : ICheckInMediator
----------------------------------------------------------------------------------------
CheckInForm.Show()                          <--------
                                            --------> AttemptCheckIn(CheckInInfo)
CheckInForm.DisplayCheckInInfo(DisplayInfo) <-------- 
                                            --------> CompleteCheckIn(AdditionalCheckInInfo)
  PleaseWaitDialog.Show()                   <--------
  PleaseWaitDialog.Close()                  <--------
CheckInForm.Close()                         <--------

如我们所见,介体类控制UI,告诉UI何时显示数据,启动,关闭等。它们甚至表示何时应该出现模式对话框以及何时应该关闭模式对话框(即上面的PleaseWaitDialog)。 UI所做的是在屏幕上显示数据,并将输入中继回调解器。

这种体系结构很好并且相互分离,并且非常容易测试和原型化。现在,我将所有内容放在一起,但是我开始遇到线程问题。例如,如果我希望我的PleaseWaitDialog在CheckInForm上以模态形式显示(使用ShowDialog()),直到由调解员控制的计时器计数了5秒(请记住,这是一种简化),我将得到一个跨线程错误如果我从计时器的回调中调用PleaseWaitDialog.Close()。同样,如果我有一个模式对话框阻止用户与UI交互,除非我另外指定(例如,使用确认对话框),否则我不希望它阻止业务层中的活动。

我想我想做的是在主线程上运行调解器和业务逻辑,在完全独立的线程上运行UI,我的第一个问题是这样做有意义吗?

我的第二个问题是,如何执行在单独的线程中运行类的操作?以及我如何让两者交流?我正在阅读.NET线程,但有一个截止日期,还有一些示例,说明如何在主线程上创建一个类,从而产生一个包含UI的线程并使它们之间的对象相互交谈确实很有帮助。

解决方案

我们是否看过BackgroundWorker类?这对于在后台类型过程中执行许多简化的处理非常有用,并且可以列出事件以使GUI显示进度。

我们可以从另一个线程操纵WinForms控件,但是我们需要使用Control.Invoke(),由于上下文切换和相关的后台CLR,我们将为每个跨线程调用付出可观的性能损失伏都教

如果要在多线程应用程序中将GUI与业务逻辑和基础结构代码分开,建议使用线程安全队列切换到消息传递模型。每当较低层需要告诉GUI做某事时,它们就会将消息对象放入队列,GUI元素通过Forms.Timer定期轮询。这对于大型,处理器密集型应用程序特别有效,因为我们可以通过调整更新计时器频率来在一定程度上限制GUI更新的处理需求。

对于以另一种方式返回的调用(GUI->较低层),我们可以仅从GUI代码调用调解器方法,只要这些调用返回的速度相当快,我们就需要非常小心地延迟GUI线程,因为响应性整个应用程序将遭受损失。如果我们有一些呼叫难以迅速返回,则可以添加另一个队列。