要实现我们讨论的行为,显然需要实现Controller类。为了使此架构能够在多数方案中应用,我们还会定义一些正式接口,可以由Controller在与UI(或客户端)和辅助线程交互时使用。
财管家.园.fs119.net
通过为客户端和辅助线程定义正式接口,我们可以在不同的情况下使用相同的Controller对象,还可以根据需要使用不同的UI要求和不同的Worker对象。
财管.家园.fs119.net
下面的UML类图表显示了Controller类以及IClient和IWorker接口。它还显示了IController接口,辅助代码将通过它与Controller对象交互。 财管家园 fs119.net
财 管家园 fs119.net
IClient接口定义的方法将由Controller对象调用,用于向客户端UI通报Worker的开始时间、结束时间和任何中间状态消息。它还包含一个指示辅助代码失败的方法。
财管家园,fs119.net
多数情况下,我们可以将这些方法作为由Controller对象发出而由UI处理的事件来实现。但是,从辅助线程发出事件然后由UI线程正确处理并非易事,因而我们将其作为一组方法来进行实现。 财,软联盟,fs119.net
使控制器代码(在辅助代码上运行)调用UI中的这些方法并由UI线程进行处理,这样相对要简单得多。
同样,IWorker接口定义了由Controller对象调用的、使其可以与辅助代码交互的方法。使用Initialize方法可以为辅助代码提供对Controller对象的引用,而使用Start方法可以启动后台线程上的操作。
由于线程的工作方式,Start方法无法包含任何参数。启动新线程时,必须将不接受任何参数的方法的地址传递给线程。 财软.联盟.fs119.net
请注意,IWorker接口中不存在Cancel或Stop方法。我们不能强制辅助代码停止,同时也没有这个必要;但是辅助代码可以使用IController接口询问Controller对象是否存在取消请求。 财软,联盟,fs119.net
IController接口定义了辅助代码可以在Controller对象上调用的方法。它允许辅助代码检查Running标志。如果存在取消请求,Running标志即为False。它还允许辅助代码在工作完成或无法完成时告诉Controller,并允许使用状态消息和完成百分比值(0到100之间的Integer)更新Controller。 财管家园.fs119.net
最后我们定义了Controller对象。该对象中包含一些可以被UI代码调用的方法。其中包括Start方法,该方法可以通过为Controller对象提供对Worker对象的引用来启动后台操作。还包括Cancel方法,该方法用于请求取消操作。UI也可以检查Running属性,查看是否存在取消请求;还可以检查Percent属性,查看任务完成的百分比。 财管家园,fs119.net
Controller类中包含的constructor方法接受IClient作为参数,还允许UI为Controller提供对窗体(用于处理Worker中的显示消息)的引用。 财管家 园 fs119.net
为了实现一系列动画点来显示线程的活动,我们将创建一个简单Windows窗体控件,该控件使用计时器以更改一系列PictureBox控件中的颜色。 财 管家园 fs119.net
财管家,园,fs119.net
财软联盟.fs119.net
财软联.盟.fs119.net
财软联盟,fs119.net
财 软联盟 fs119.net
我们将在ClassLibrary(类库)项目中实现此架构,使其可用于需要运行后台进程的应用程序。
打开VisualStudio.NET,然后创建一个名为Background的新ClassLibrary(类库)应用程序。由于此库将包含Windows窗体控件和窗体,因此需要使用AddReferences(添加引用)对话框引用System.Windows.Forms.dll和System.Windows.Drawing.dll。此外,我们可以使用项目的属性对话框在这些项目范围内导入命名空间,如图6所示。
财管 家园 fs119.net
此操作完成后,就可以开始编码了。让我们先从创建接口开始。
在名为IClient的项目中添加一个类,并用以下代码替换其代码:
财管家园,fs119.net
然后添加名为IWorker的类,并用以下代码替换其代码: 财管 家园 fs119.net
最后添加名为IController的类,代码如下:
至此,我们已定义了本文前面所述的所有类图表中的接口。现在可以实现Controller类了。 财软联.盟.fs119.net
现在,我们可以实现架构的核心,Controller类。此类中包含的代码可用于启动辅助线程,以及在辅助线程完成之前充当UI线程和辅助线程之间的媒介。
在名为Controller的项目中添加一个新类。首先添加Imports,并声明一些变量:
财管家 园 fs119.net
然后需要声明一些委托。委托是方法的正式指针,而且方法的委托必须具有与方法本身相同的方法签名(参数类型等)。 财,管家园,fs119.net
委托的用途很广。在我们的示例中,委托非常重要,因为委托使我们可以让一个线程调用窗体上的方法,使其在该窗体的UI线程上运行。正如IClient所定义的那样,要在窗体上调用的三个方法都需要委托: 财管.家园.fs119.net
IClient还定义了Start方法,但是该方法可以从UI线程调用,因此不需要委托。
下面编写将从UI线程调用的代码。代码中包括constructor方法、Start和Cancel方法以及Percent属性。我将这些内容放入Region中,便于大家清楚地了解它们是从UI线程调用的。
财软.联盟.fs119.net
此处唯一比较特殊的代码位于Start方法中,我们可以在该方法中创建辅助线程然后启动该线程: 财软联 盟 fs119.net
要创建线程,需要在Worker对象的IWorker接口上传递Start方法的地址。然后,只需调用线程对象的Start方法即可开始操作。此时我们要特别注意,UI不应直接与Worker交互,Worker也不应直接与UI交互。 财管家园 fs119.net
请注意,Cancel方法只设置一个标志,表明我们不希望继续运行。辅助代码应定期查看此标志,以确定是否应该停止运行。
财管家园.fs119.net
现在,我们可以实现Worker对象运行时将由辅助线程调用的代码。此代码比较有趣,因为它必须将Display和Completed从辅助线程中转至UI线程,同时还要在UI线程上完成此操作。 财管 家园 fs119.net
要完成此操作,我们可以使用Form对象的Invoke方法。此方法接受窗体应该调用的方法的委托指针,以及包含该方法的参数的Object类型数组。 财软联盟.fs119.net
Invoke方法不直接调用窗体上的方法,而是请求窗体返回并使用窗体的UI线程调用该方法。此操作可通过向窗体发送Windows消息在后台完成。这说明窗体获得这些方法调用的方式与从操作系统中获得click或keypress事件的方式基本相同。 财软联盟,fs119.net
财管家,园,fs119.net
财管家园.fs119.net
通常,这些细节不会影响大局。结果由Invoke方法触发一个进程,通过该进程窗体将终止其UI线程上运行的方法,这就是我们要实现的目标。 财软联 盟 fs119.net
再次重申,此代码位于Region内,目的是为了明确它将在辅助线程上调用:
财管家园.fs119.net
Failed和Completed方法利用窗体的Invoke方法。例如,Failed方法可以执行以下操作: 财管家,园,fs119.net
财软联,盟,fs119.net
首先创建一个委托,从IClient接口指向客户端窗体的Failed方法。然后声明包含向方法传递参数值的Object类型数组。最后调用客户端窗体的Invoke方法,将委托指针和参数数组传递给窗体。 财软.联盟.fs119.net
窗体将在UI线程(窗体在这里可以安全运行以更新显示)上使用这些参数调用此方法。
整个进程是同步进行的,即对窗体进行调用时辅助线程将停止。尽管可以在显示错误消息或完成消息时停止辅助线程,但我们并不希望显示每个小状态时都停止辅助线程。
财 软联盟 fs119.net
财管.家园.fs119.net
为了避免显示状态时停止辅助线程,Display方法将使用BeginInvoke,而不使用Invoke。BeginInvoke使窗体上的方法调用异步进行,这样辅助线程可以一直保持运行状态,不需要等待窗体上的显示方法完成:
mClient.BeginInvoke(disp,ar)
以这种方式使用BeginInvoke可以防止辅助线程停止,使辅助线程具有尽可能高的性能。 财 管家园 fs119.net
最后,我们来创建显示动画点的ActivityBar控件。
在名为ActivityBar的项目中添加一个用户控件。 财 软联盟 fs119.net
将该控件的宽度调整为约110,高度调整为约20。可以通过拖动边界进行调整,也可以通过在Properties(属性)窗口中设置Size属性进行调整。
其余的操作将通过代码完成。要创建一系列在显示时不停闪烁的动画“灯”,可以使用带有Timer控件的一系列PictureBox控件。每次Timer控件关闭时,我们将使下一个PictureBox呈绿色显示,并将已经呈绿色显示的PictureBox更改为窗体的背景色。 财 软联盟 fs119.net
将WindowsForms(Windows窗体)选项卡中的Timer控件放入窗体中,然后将其名称更改为tmAnim。同时将Interval属性设置为300,以获得较好的动画速度。
顺便说一句,Components(组件)选项卡中有一个不同的Timer控件。它是一个多线程计时器。也就是说,该计时器将在后台线程中引发Elapsed事件,而不是象Windows窗体计时器那样在UI线程上引发Elapsed事件。建立UI时这种方法通常会产生相反的效果,因为Elapsed事件中的代码显然不能直接与我们的UI进行交互。
财软联,盟,fs119.net
现在,在控件中添加以下代码:
窗体的Load事件创建PictureBox控件并将它们放入数组,这样便于我们在它们之间循环。Timer控件的Tick事件循环显示,使各个控件依次呈绿色。 财 管家园 fs119.net
所有操作由Start方法开始,由Stop事件结束。由于Stop是一个保留字,因此把这个方法名放在方括号内:[Stop]。Stop方法不仅可以停止计时器,还可以灰显所有框,告诉用户这些框中当前没有活动。 财,管家园,fs119.net
财.管家园.fs119.net
财管,家园,fs119.net
财软联 盟 fs119.net
本文前面已简单介绍了Worker类。因为我们已经定义了IWorker接口,所以可以增强该类,以利用我们创建的Controller。 财软联 盟 fs119.net
首先创建Background.dll文件。此步骤很重要,因为如果不完成此步骤,ActivityBar控件将无法在我们建立测试窗体时显示在工具箱上。 财管家 园 fs119.net
在解决方案中添加名为bgTest的WindowsFormsApplication(Windows窗体应用程序)。在SolutionExplorer(解决方案资源浏览器)中用右键单击该项目并选择相应的菜单项,将该程序设置为启动项目。
财管家.园.fs119.net
然后使用AddReferences(添加引用)对话框中的Projects(项目)选项卡,添加对Background项目的引用。
财管家园 fs119.net
现在,在名为Worker的项目中添加一个类。其中部分代码与前面所述的代码相同,但还包含一些不同的代码,用以实现IWorker接口(此处突出显示的部分):
ImportsBackground
PublicClassWorker
ImplementsIWorker
PrivatemControllerAsIController
PrivatemInnerAsInteger
PrivatemOuterAsInteger
PublicSubNew(ByValInnerSizeAsInteger,ByValOuterSizeAsInteger)
mInner=InnerSize
mOuter=OuterSize
EndSub
'由Controller调用,以便获取
'Controller的引用
PrivateSubInit(ByValControllerAsIController)_
ImplementsIWorker.Initialize
mController=Controller
EndSub
PrivateSubWork()ImplementsIWorker.Start
DiminnerIndexAsInteger
DimouterIndexAsInteger
DimvalueAsDouble
Try
ForouterIndex=0TomOuter
IfmController.RunningThen
mController.Display("Outerloop"&outerIndex&"starting")
mController.SetPercent(CInt(outerIndex/mOuter*100))
Else
'它们请求取消
mController.Completed(True)
ExitSub
EndIf
ForinnerIndex=0TomInner
'此处进行一些有意思的计算
value=Math.Sqrt(CDbl(innerIndex-outerIndex))
Next
Next
mController.SetPercent(100)
mController.Completed(False)
CatcheAsException
mController.Failed(e)
EndTry
EndSub
EndClass
我们添加了能够实现IWorker.Initialize的Init方法。Controller将调用此方法,因此以后我们可以引用Controller对象。 财软联盟.fs119.net
我们还将Work方法更改为Private,只是为了实现IWorker.Start方法。此方法将在辅助线程上运行。 财软联盟,fs119.net
我们增强了Work方法,使其可以使用Try..Catch块。这样我们可以使用Controller上的Failed方法捕捉任何错误并将其返回给UI。 财,管家园,fs119.net
假设代码正在运行,我们调用Controller对象的Display和SetPercent方法,使它们随着代码的运行更新其状态和完成的百分比。
我们还定期检查Controller对象的Running属性,查看是否存在取消请求。如果存在取消请求,则停止进程,并指示由于取消请求而停止操作 财软 联盟 fs119.net
财软联 盟 fs119.net财.软联盟.fs119.net
Google.cn搜索相关文章:
谷歌中搜索全球网 在VisualBasic.NET中实现后台进程(二)
百度中搜索 在VisualBasic.NET中实现后台进程(二)
谷歌中搜索www.fs119.net 在VisualBasic.NET中实现后台进程(二)
下一篇:在VisualBasic.NET中实现后台进程(三)