Silverlight 中的 BackgroundWorker






4.33/5 (4投票s)
为什么不为 Silverlight 创建我们自己的 BackgroundWorker?
引言
在当前的Silverlight 1.1公开Alpha版本中,没有像WinForms开发人员(自2.0版以来)使用的优秀的BackgroundWorker
这样的调度程序,并且在Silverlight中使用Thread
非常不容易。对于“WinFormer”来说,这是一个巨大的缺点。如果没有BackgroundWorker
,你只能在Silverlight主线程中使用或创建控件(除非你喜欢跨线程异常……)。在WinForms中,逻辑是在主线程中操作控件,并在另一个“逻辑”线程中进行逻辑和数学计算。每隔一段时间,就会进行一次刷新以协调操作。BackgroundWorker
专门用于此类任务。让我们开始创建我们自己的Silverlight BackgroundWorker
。
首先:一个计时器
这个想法是通过代码创建一个持续时间为100毫秒的Storyboard(你可以更改此值)。每次Storyboard完成后,你必须分析当前运行的关联线程,并在需要时生成事件。我不使用HtmlTimer
,因为此类已弃用。
/// <summary>
/// <para>Instanciate a new <see cref="BackgroundWorker"/> object.</para>
/// </summary>
/// <param name="host">A control witch can host children.</param>
public BackgroundWorker(Panel host)
{
if (host == null)
throw new ArgumentNullException("host", "Host must be valid");
this._host = host;
this.ConfigureStoryboard();
}
ConfigureStoryboard
方法创建计时器
/// <summary>
/// <para>Configure the ticks timer storyboard.</para>
/// </summary>
private void ConfigureStoryboard()
{
if (!BackgroundWorker._hostsToStoryboards.ContainsKey(this._host))
{
string xamlCode = string.Format(BackgroundWorker._tickTimerStoryboardXaml,
Guid.NewGuid().ToString());
Storyboard storyboard = XamlReader.Load(xamlCode) as Storyboard;
this._host.Resources.Add(storyboard);
StoryboardData data = new StoryboardData() { Storyboard=storyboard,
IsStarted = false, NumberOfHostManaged=0 };
BackgroundWorker._hostsToStoryboards.Add(this._host, data);
}
bool isStarted = BackgroundWorker._hostsToStoryboards[this._host].IsStarted;
BackgroundWorker._hostsToStoryboards[this._host].Storyboard.Duration =
new TimeSpan(0, 0, 0, 0, this._refreshInterval);
if (isStarted)
this.StartTicks();
}
hostsToStoryboards
字段是一个Dictionary
,它将Host与Storyboard关联起来。这有助于仅让一个计时器由Host运行(而不是由BackgroundWorker
运行)。tickTimerStoryboardXaml
字段包含一个序列化的XAML Storyboard。
private static string _tickTimerStoryboardXaml =
@"<Storyboard xmlns:x=""http://schemas.microsoft.com/winfx" +
@"/2006/xaml""x:Name=""TickTimer_{0}""></Storyboard>";
StoryboardData
类告知是否启动了Storyboard,以及Storyboard当前侦听的线程数。RefreshInterval
属性只是Timer
周期。
BackgroundWorker._hostsToStoryboards[this._host].Storyboard.Duration =
new TimeSpan(0, 0, 0, 0, this._refreshInterval);
每次Storyboard完成后,都会分析逻辑线程。
private void BackgroundTickOccurs(object sender, EventArgs e)
{
if (this._progressChangedEventArgs != null)
{
if (this.ProgressChanged != null)
this.ProgressChanged(this, this._progressChangedEventArgs);
this._progressChangedEventArgs = null;
}
if (this._completed && !this._completedLaunched)
{
BackgroundWorker._hostsToStoryboards[this._host].NumberOfHostManaged--;
if (this.RunWorkerCompleted != null)
this.RunWorkerCompleted(this, this._runWorkerCompletedEventArgs);
this._isBusy = false;
this._completedLaunched = true;
}
if (BackgroundWorker._hostsToStoryboards[this._host].NumberOfHostManaged <= 0)
{
BackgroundWorker._hostsToStoryboards[this._host].Storyboard.Stop();
BackgroundWorker._hostsToStoryboards[this._host].Storyboard.Seek(
new TimeSpan(0, 0, 0, 0, 0));
}
BackgroundWorker._hostsToStoryboards[this._host].NumberOfHostManaged--;
if (!this._completedLaunched)
this.StartTicks();
}
如果逻辑线程实例化了一个ProgressChangedEventArgs
对象,我将生成一个ProgressChanged
事件。如果线程已完成并且未启动Completed
事件,我将生成一个RunWorkerCompleted
事件。如果Storyboard没有要分析的线程,则将其停止。
逻辑线程
InternalJob
方法是线程的ThreadStart
委托。此方法从主线程异步调用。
/// <summary>
/// <para>Execute the internal job represented
/// by the linked DoWork method.</para>
/// </summary>
private void InternalJob()
{
this._doWorkEventArgs = new DoWorkEventArgs(this._argument);
this._runWorkerCompletedEventArgs = new RunWorkerCompletedEventArgs(null);
try
{
if (this.DoWork != null)
this.DoWork(this, this._doWorkEventArgs);
this._runWorkerCompletedEventArgs.Result = this._doWorkEventArgs.Result;
}
catch (Exception e)
{
this._runWorkerCompletedEventArgs.Error = e;
}
this._completed = true;
}
DoWork
链接方法在try-catch
块内调用。如果方法顺利完成,则runWorkerCompletedEvent
将获取传递给DoWork
方法的doWorkEventArgs
的返回值。如果捕获到异常,则runWorkerCompletedEvent
将获取该异常。
调用Worker
首先实例化Worker,并为计时器传递Host。
this._worker = new BackgroundWorker(this.Host);
然后,链接你的事件方法。
this._worker.DoWork += new DoWorkEventHandler(_worker_DoWork);
this._worker.ProgressChanged +=
new ProgressChangedEventHandler(_worker_ProgressChanged);
...
最后,启动Worker。
this._worker.RunWorkerAsync();
示例
我做了一个简单的示例,其中BackgroundWorker
将在画布上动画显示一个X形状。当你点击链接时,将在客户端区域添加一个形状。
致歉
这篇文章可能难以阅读:我的英语非常糟糕,对此我深感抱歉。我希望这段代码能帮助你,就像它帮助我一样。请随时向我发送评论!(如果你可以的话,请用英语)。