Windows phone 8 是一个单任务操作系统,任何时候都只有一个应用处于活跃状态,这里的多任务是指对后台任务的支持。本节我们先讲讲应用程序的运行状态,然后看看支持的后台任务,包括:后台代理、后台音频、后台文件传输、后台辅助线程等。 
  快速导航: 
    一、应用的状态 
    二、后台代理 
    三、后台音频 
    四、后台文件传输 
    五、后台辅助线程 
 
 
一、应用的状态 
 
1)应用的运行状态 
  我们通过图解来分析应用的运行状态,启动并置于前台界面的应用是唯一处于运行状态的,其他的操作,比如win键,后退导出应用,打开选择器和启动器时都会让当前运行的应用进入休眠状态,如果系统内存不足,处于休眠状态的应用可能会被系统逻辑删除。下面的图示演示了这个过程。 
   
 
 
 
2)如何恢复状态 
  当应用处于休眠状态时,它的状态信息仍然保留在内存中,用户下次切换进去后不会有任何变化。但是当应用被逻辑删除后,这些状态信息就会丢失,比如表单填写的内容都会消失,为了避免这种情况,我们需要手动保留状态信息。 
    首先,我们在mainpage定义一些页面表单控件: 
 
 
[XAML] 
 
 
         
 
 
 
 
 
 
 
  我们需要实现在应用逻辑删除后能将其状态保持到页面的State字典中,但是需要我们的数据源支持序列化,所以我们定义与表单关联的ViewModel如下: 
 
[C#] 
 
 
    [DataContract] 
public class ViewModel : INotifyPropertyChanged 
{ 
private string _textBox1Text; 
private bool _checkBox1IsChecked; 
private bool _radioButton1IsChecked; 
private bool _radioButton2IsChecked; 
private double _slider1Value; 
[DataMember] 
public string TextBox1Text 
{ 
get { return _textBox1Text; } 
set 
{ 
_textBox1Text = value; 
NotifyPropertyChanged("TextBox1Text"); 
} 
} 
[DataMember] 
public bool CheckBox1IsChecked 
{ 
get { return _checkBox1IsChecked; } 
set 
{ 
_checkBox1IsChecked = value; 
NotifyPropertyChanged("CheckBox1IsChecked"); 
} 
} 
[DataMember] 
public double Slider1Value 
{ 
get { return _slider1Value; } 
set 
{ 
_slider1Value = value; 
NotifyPropertyChanged("Slider1Value"); 
} 
} 
[DataMember] 
public bool RadioButton1IsChecked 
{ 
get { return _radioButton1IsChecked; } 
set 
{ 
_radioButton1IsChecked = value; 
NotifyPropertyChanged("RadioButton1IsChecked"); 
} 
} 
[DataMember] 
public bool RadioButton2IsChecked 
{ 
get { return _radioButton2IsChecked; } 
set 
{ 
_radioButton2IsChecked = value; 
NotifyPropertyChanged("RadioButton2IsChecked"); 
} 
} 
public event PropertyChangedEventHandler PropertyChanged; 
private void NotifyPropertyChanged(string propertyName) 
{ 
if (null != PropertyChanged) 
PropertyChanged(this, new PropertyChangedEventArgs(propertyName)); 
} 
} 
 
  我需要对mainpage代码添加页面导航入、导航出的事件。导航出页面的时候,如果不是向后导航,则存储状态。导航入的时候,我们需要判断页面是否为逻辑删除后正在恢复的状态,如果是,则通过状态字典恢复状态。mainpage代码如下: 
 
[C#] 
 
 
    public partial class MainPage : PhoneApplicationPage 
{ 
// 构造函数 
public MainPage() 
{ 
InitializeComponent(); 
_isNewPageInstance = true; 
} 
ViewModel _viewModel = null; 
///  
/// 新实例还是现有实例 
///  
bool _isNewPageInstance = false; 
private void Button_Click_1(object sender, RoutedEventArgs e) 
{ 
NavigationService.Navigate(new Uri("/Page1.xaml", UriKind.Relative)); 
} 
protected override void OnNavigatedFrom(System.Windows.Navigation.NavigationEventArgs e) 
{ 
//如果不是向后导航,则保存状态 
if (e.NavigationMode != System.Windows.Navigation.NavigationMode.Back) 
{ 
State["ViewModel"] = _viewModel; 
} 
} 
protected override void OnNavigatedTo(System.Windows.Navigation.NavigationEventArgs e) 
{ 
if (_isNewPageInstance) 
{ 
if (_viewModel == null) 
{ 
if (State.Count > 0) 
{ 
_viewModel = (ViewModel)State["ViewModel"]; 
} 
else 
{ 
_viewModel = new ViewModel(); 
} 
} 
DataContext = _viewModel; 
} 
_isNewPageInstance = false; 
} 
 
} 
 
  然后我们添加一page1页面,该页添加一个返回按钮。用于测试。为了达到调试时即时进入逻辑删除的效果,我们需要设置下。右键项目文件,点属性,在调试选项卡勾选“在调试期间取消激活时逻辑删除”。 
   
 
二、后台代理 
  后台代理可以在应用退出以后独立在系统后台运行,它包含两种类型的代理,分别是定期代理和资源密集型代理,前者用于频繁执行小任务,后者用于在系统空闲时执行耗时大任务。要使用后台代理,我们需要添加一个名为Windows phone 计划任务代理的项目,并在应用的项目中添加对其的引用,现在我们要实现在后台代理中弹出Toast,我们需要如下修改ScheduledAgent.cs的OnInvoke方法,代码如下 
 
[C#] 
 
 
        protected override void OnInvoke(ScheduledTask task) 
{ 
string toastMessage = ""; 
if (task is PeriodicTask) 
{ 
toastMessage = "定期代理正在运行"; 
} 
else 
{ 
toastMessage = "资源密集型代理正在运行"; 
} 
// 用于向用户显示Toast,如果当前任务的前台正在运行,则不显示 
ShellToast toast = new ShellToast(); 
toast.Title = "标题"; 
toast.Content = toastMessage; 
toast.Show(); 
// 在调试的时候需要及时执行查看效果 
#if DEBUG_AGENT 
ScheduledActionService.LaunchForTest(task.Name, TimeSpan.FromSeconds(15)); 
#endif 
NotifyComplete(); 
} 
 
  接着,我们在应用项目的mainpage中调用代理,代码如下: 
 
[XAML] 
 
 
         
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
[C#] 
 
 
    public partial class MainPage : PhoneApplicationPage 
{ 
///  
/// 定期代理 
///  
PeriodicTask periodicTask; 
///  
/// 资源密集型代理 
///  
ResourceIntensiveTask resourceIntensiveTask; 
string periodicTaskName = "PeriodicAgent"; 
string resourceIntensiveTaskName = "ResourceIntensiveAgent"; 
public bool agentsAreEnabled = true; 
// 构造函数 
public MainPage() 
{ 
InitializeComponent(); 
} 
//启动定期代理 
private void StartPeriodicAgent() 
{ 
agentsAreEnabled = true; 
// 获取当前名称的定期代理,如果存在则移除 
periodicTask = ScheduledActionService.Find(periodicTaskName) as PeriodicTask; 
if (periodicTask != null) 
{ 
RemoveAgent(periodicTaskName); 
} 
periodicTask = new PeriodicTask(periodicTaskName); 
periodicTask.Description = "这是一个定期代理的描述信息。"; 
try 
{ 
ScheduledActionService.Add(periodicTask); 
PeriodicStackPanel.DataContext = periodicTask; 
//在调试的时候需要及时执行查看效果 
#if(DEBUG_AGENT) 
ScheduledActionService.LaunchForTest(periodicTaskName, TimeSpan.FromSeconds(60)); 
#endif 
} 
catch (InvalidOperationException exception) 
{ 
if (exception.Message.Contains("BNS Error: The action is disabled")) 
{ 
MessageBox.Show("本应用的后台计划被用户禁用。"); 
agentsAreEnabled = false; 
PeriodicCheckBox.IsChecked = false; 
} 
if (exception.Message.Contains("BNS Error: The maximum number of ScheduledActions of this type have already been added.")) 
{ 
MessageBox.Show("定期代理数量达到最大限制。"); 
} 
PeriodicCheckBox.IsChecked = false; 
} 
catch (SchedulerServiceException) 
{ 
PeriodicCheckBox.IsChecked = false; 
} 
} 
private void StartResourceIntensiveAgent() 
{ 
agentsAreEnabled = true; 
// 获取当前名称的资源密集型代理,如果存在则移除 
resourceIntensiveTask = ScheduledActionService.Find(resourceIntensiveTaskName) as ResourceIntensiveTask; 
if (resourceIntensiveTask != null) 
{ 
RemoveAgent(resourceIntensiveTaskName); 
} 
resourceIntensiveTask = new ResourceIntensiveTask(resourceIntensiveTaskName); 
resourceIntensiveTask.Description = "这是一个资源密集型代理的描述信息。"; 
try 
{ 
ScheduledActionService.Add(resourceIntensiveTask); 
ResourceIntensiveStackPanel.DataContext = resourceIntensiveTask; 
//在调试的时候需要及时执行查看效果 
#if(DEBUG_AGENT) 
ScheduledActionService.LaunchForTest(resourceIntensiveTaskName, TimeSpan.FromSeconds(15)); 
#endif 
} 
catch (InvalidOperationException exception) 
{ 
if (exception.Message.Contains("BNS Error: The action is disabled")) 
{ 
MessageBox.Show("本应用的后台计划被用户禁用。"); 
agentsAreEnabled = false; 
} 
ResourceIntensiveCheckBox.IsChecked = false; 
} 
catch (SchedulerServiceException) 
{ 
ResourceIntensiveCheckBox.IsChecked = false; 
} 
} 
 
bool ignoreCheckBoxEvents = false; 
private void PeriodicCheckBox_Checked(object sender, RoutedEventArgs e) 
{ 
if (ignoreCheckBoxEvents) 
return; 
StartPeriodicAgent(); 
} 
private void PeriodicCheckBox_Unchecked(object sender, RoutedEventArgs e) 
{ 
if (ignoreCheckBoxEvents) 
return; 
RemoveAgent(periodicTaskName); 
} 
private void ResourceIntensiveCheckBox_Checked(object sender, RoutedEventArgs e) 
{ 
if (ignoreCheckBoxEvents) 
return; 
StartResourceIntensiveAgent(); 
} 
private void ResourceIntensiveCheckBox_Unchecked(object sender, RoutedEventArgs e) 
{ 
if (ignoreCheckBoxEvents) 
return; 
RemoveAgent(resourceIntensiveTaskName); 
} 
///  
/// 删除代理 
///  
private void RemoveAgent(string name) 
{ 
try 
{ 
ScheduledActionService.Remove(name); 
} 
catch (Exception) 
{ 
} 
} 
protected override void OnNavigatedTo(System.Windows.Navigation.NavigationEventArgs e) 
{ 
ignoreCheckBoxEvents = true; 
periodicTask = ScheduledActionService.Find(periodicTaskName) as PeriodicTask; 
if (periodicTask != null) 
{ 
PeriodicStackPanel.DataContext = periodicTask; 
} 
resourceIntensiveTask = ScheduledActionService.Find(resourceIntensiveTaskName) as ResourceIntensiveTask; 
if (resourceIntensiveTask != null) 
{ 
ResourceIntensiveStackPanel.DataContext = resourceIntensiveTask; 
} 
ignoreCheckBoxEvents = false; 
} 
} 
 
   
 
三、后台音频 
  通过后台音频的功能我们可以实现在系统后台播放音乐的功能,由于后台音频代理只能访问本地文件夹,所以我们务必要先把需要播放的音乐文件拷贝到本地文件夹中。本示例是把安装文件夹的音频文件拷贝到本地文件夹,代码如下: 
 
[C#] 
 
 
        //把安装文件夹下的文件拷贝到本地文件夹 
private void CopyToIsolatedStorage() 
{ 
using (IsolatedStorageFile storage = IsolatedStorageFile.GetUserStoreForApplication()) 
{ 
string[] files = new string[] { "Ring01.wma", "Ring02.wma", "Ring03.wma" }; 
foreach (var _fileName in files) 
{ 
if (!storage.FileExists(_fileName)) 
{ 
string _filePath = "Audio/" + _fileName; 
StreamResourceInfo resource = Application.GetResourceStream(new Uri(_filePath, UriKind.Relative)); 
using (IsolatedStorageFileStream file = storage.CreateFile(_fileName)) 
{ 
int chunkSize = 4096; 
byte[] bytes = new byte[chunkSize]; 
int byteCount; 
while ((byteCount = resource.Stream.Read(bytes, 0, chunkSize)) > 0) 
{ 
file.Write(bytes, 0, byteCount); 
} 
} 
} 
} 
string[] icons = new string[] { "Ring01.jpg", "Ring02.jpg", "Ring03.jpg" }; 
foreach (var _fileName in icons) 
{ 
if (!storage.FileExists(_fileName)) 
{ 
string _filePath = "Images/" + _fileName; 
StreamResourceInfo iconResource = Application.GetResourceStream(new Uri(_filePath, UriKind.Relative)); 
using (IsolatedStorageFileStream file = storage.CreateFile( _fileName)) 
{ 
int chunkSize = 4096; 
byte[] bytes = new byte[chunkSize]; 
int byteCount; 
while ((byteCount = iconResource.Stream.Read(bytes, 0, chunkSize)) > 0) 
{ 
file.Write(bytes, 0, byteCount); 
} 
} 
} 
} 
} 
} 
 
  我们需要在解决方案中添加Windows phone 音频播放代理项目,并在应用项目中添加对其的引用。修改AudioPlayer.cs代码如下: 
 
[C#] 
 
 
    public class AudioPlayer : AudioPlayerAgent 
{ 
private static volatile bool _classInitialized; 
private static List _playList = new List 
{ 
new AudioTrack(new Uri("Ring01.wma", UriKind.Relative),"曲目1","艺术家1","专辑1",new Uri("Ring01.jpg", UriKind.Relative)), 
new AudioTrack(new Uri("Ring02.wma", UriKind.Relative),"曲目2","艺术家2","专辑2",new Uri("Ring02.jpg", UriKind.Relative)),  
new AudioTrack(new Uri("Ring03.wma", UriKind.Relative),"曲目3","艺术家3","专辑3",new Uri("Ring03.jpg", UriKind.Relative))                     
}; 
///  
/// 当前播放位置 
///  
static int currentTrackNumber = 0; 
 
///  
/// AudioPlayer 实例可共享同一进程。 
/// 静态字段可用于在 AudioPlayer 实例之间共享状态 
/// 或与音频流代理通信。 
///  
public AudioPlayer() 
{ 
if (!_classInitialized) 
{ 
_classInitialized = true; 
// 订阅托管异常处理程序 
Deployment.Current.Dispatcher.BeginInvoke(delegate 
{ 
Application.Current.UnhandledException += AudioPlayer_UnhandledException; 
}); 
} 
} 
/// 出现未处理的异常时执行的代码 
private void AudioPlayer_UnhandledException(object sender, ApplicationUnhandledExceptionEventArgs e) 
{ 
if (System.Diagnostics.Debugger.IsAttached) 
{ 
// 出现未处理的异常;强行进入调试器 
System.Diagnostics.Debugger.Break(); 
} 
} 
///  
/// playstate 更改时调用,但 Error 状态除外(参见 OnError) 
///  
/// BackgroundAudioPlayer 
/// 在 playstate 更改时播放的曲目 
/// 播放机的新 playstate  
///  
/// 无法取消播放状态更改。即使应用程序 
/// 导致状态自行更改,假定应用程序已经选择了回调。 
///  
/// 值得注意的 playstate 事件 
/// (a) TrackEnded: 播放器没有当前曲目时激活。代理可设置下一曲目。 
/// (b) TrackReady: 音轨已设置完毕,现在可以播放。 
///  
/// 只在代理请求完成之后调用一次 NotifyComplete(),包括异步回调。 
///  
protected override void OnPlayStateChanged(BackgroundAudioPlayer player, AudioTrack track, PlayState playState) 
{ 
switch (playState) 
{ 
case PlayState.TrackEnded: 
player.Track = GetPreviousTrack(); 
break; 
case PlayState.TrackReady: 
player.Play(); 
break; 
case PlayState.Shutdown: 
// TODO: 在此处理关机状态(例如保存状态) 
break; 
case PlayState.Unknown: 
break; 
case PlayState.Stopped: 
break; 
case PlayState.Paused: 
break; 
case PlayState.Playing: 
break; 
case PlayState.BufferingStarted: 
break; 
case PlayState.BufferingStopped: 
break; 
case PlayState.Rewinding: 
break; 
case PlayState.FastForwarding: 
break; 
} 
NotifyComplete(); 
} 
 
///  
/// 在用户使用应用程序/系统提供的用户界面请求操作时调用 
///  
/// BackgroundAudioPlayer 
/// 用户操作期间播放的曲目 
/// 用户请求的操作 
/// 与请求的操作相关联的数据。 
/// 在当前版本中,此参数仅适合与 Seek 操作一起使用, 
/// 以指明请求的乐曲的位置 
///  
/// 用户操作不自动对系统状态进行任何更改;如果用户操作受支持, 
/// 执行用户操作(如果这些操作受支持)。 
///  
/// 只在代理请求完成之后调用一次 NotifyComplete(),包括异步回调。 
///  
protected override void OnUserAction(BackgroundAudioPlayer player, AudioTrack track, UserAction action, object param) 
{ 
switch (action) 
{ 
case UserAction.Play: 
if (player.PlayerState != PlayState.Playing) 
{ 
player.Play(); 
} 
break; 
case UserAction.Stop: 
player.Stop(); 
break; 
case UserAction.Pause: 
player.Pause(); 
break; 
case UserAction.FastForward: 
player.FastForward(); 
break; 
case UserAction.Rewind: 
player.Rewind(); 
break; 
case UserAction.Seek: 
player.Position = (TimeSpan)param; 
break; 
case UserAction.SkipNext: 
player.Track = GetNextTrack(); 
break; 
case UserAction.SkipPrevious: 
AudioTrack previousTrack = GetPreviousTrack(); 
if (previousTrack != null) 
{ 
player.Track = previousTrack; 
} 
break; 
} 
NotifyComplete(); 
} 
 
///  
/// 实现逻辑以获取下一个 AudioTrack 实例。 
/// 在播放列表中,源可以是文件、Web 请求,等等。 
///  
///  
/// AudioTrack URI 确定源,它可以是: 
/// (a) 独立存储器文件(相对 URI,表示独立存储器中的路径) 
/// (b) HTTP URL(绝对 URI) 
/// (c) MediaStreamSource (null) 
///  
/// AudioTrack 实例,或如果播放完毕,则返回 null 
private AudioTrack GetNextTrack() 
{ 
// TODO: 添加逻辑以获取下一条音轨 
if (++currentTrackNumber >= _playList.Count) currentTrackNumber = 0; 
AudioTrack track = _playList[currentTrackNumber]; 
// 指定曲目 
return track; 
} 
 
///  
/// 实现逻辑以获取前一个 AudioTrack 实例。 
///  
///  
/// AudioTrack URI 确定源,它可以是: 
/// (a) 独立存储器文件(相对 URI,表示独立存储器中的路径) 
/// (b) HTTP URL(绝对 URI) 
/// (c) MediaStreamSource (null) 
///  
/// AudioTrack 实例,或如果不允许前一曲目,则返回 null 
private AudioTrack GetPreviousTrack() 
{ 
// TODO: 添加逻辑以获取前一条音轨 
if (--currentTrackNumber < 0) currentTrackNumber = _playList.Count - 1; 
AudioTrack track = _playList[currentTrackNumber]; 
// 指定曲目 
return track; 
} 
///  
/// 每次播放出错(如 AudioTrack 未正确下载)时调用 
///  
/// BackgroundAudioPlayer 
/// 出现错误的曲目 
/// 出现的错误 
/// 如果为 true,则播放不能继续并且曲目播放将停止 
///  
/// 不保证在所有情况下都调用此方法。例如,如果后台代理程序  
/// 本身具有未处理的异常,则不会回调它来处理它自己的错误。 
///  
protected override void OnError(BackgroundAudioPlayer player, AudioTrack track, Exception error, bool isFatal) 
{ 
if (isFatal) 
{ 
Abort(); 
} 
else 
{ 
NotifyComplete(); 
} 
} 
///  
/// 取消代理请求时调用 
///  
///  
/// 取消请求后,代理需要 5 秒钟完成其工作, 
/// 通过调用 NotifyComplete()/Abort()。 
///  
protected override void OnCancel() 
{ 
} 
} 
 
  最后,我们在mainpage中添加对播放的控制。 
 
[XAML] 
 
 
     
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
[C#] 
 
 
    public partial class MainPage : PhoneApplicationPage 
{ 
// 构造函数 
public MainPage() 
{ 
InitializeComponent(); 
BackgroundAudioPlayer.Instance.PlayStateChanged += new EventHandler(Instance_PlayStateChanged); 
} 
//刚加载时确定播放状态 
protected override void OnNavigatedTo(NavigationEventArgs e) 
{ 
if (PlayState.Playing == BackgroundAudioPlayer.Instance.PlayerState) 
{ 
button1.Content = "■"; 
textblock1.Text = "曲目:" + BackgroundAudioPlayer.Instance.Track.Title 
+ " 艺术家:" + BackgroundAudioPlayer.Instance.Track.Artist 
+ " 专辑:" + BackgroundAudioPlayer.Instance.Track.Album 
+ " 曲目长度:" +BackgroundAudioPlayer.Instance.Track.Duration.Minutes + ":" + BackgroundAudioPlayer.Instance.Track.Duration.Seconds; 
using (IsolatedStorageFile storage = IsolatedStorageFile.GetUserStoreForApplication()) 
{ 
var stream = storage.OpenFile(BackgroundAudioPlayer.Instance.Track.AlbumArt.OriginalString, System.IO.FileMode.Open); 
var bitmapImage = new BitmapImage(); 
bitmapImage.SetSource(stream); 
imge1.Source = bitmapImage; 
stream.Close(); 
} 
} 
else 
{ 
button1.Content = "?"; 
textblock1.Text = "未播放曲目"; 
} 
} 
void Instance_PlayStateChanged(object sender, EventArgs e) 
{ 
switch (BackgroundAudioPlayer.Instance.PlayerState) 
{ 
case PlayState.Playing: 
button1.Content = "■"; 
button2.IsEnabled = true; 
button3.IsEnabled = true; 
break; 
case PlayState.Paused: 
case PlayState.Stopped: 
button1.Content = "?"; 
break; 
} 
if (null != BackgroundAudioPlayer.Instance.Track && BackgroundAudioPlayer.Instance.PlayerState!= PlayState.Stopped) 
{ 
textblock1.Text = "曲目:" + BackgroundAudioPlayer.Instance.Track.Title 
+ " 艺术家:" + BackgroundAudioPlayer.Instance.Track.Artist 
+ " 专辑:" + BackgroundAudioPlayer.Instance.Track.Album 
+ " 曲目长度:" + BackgroundAudioPlayer.Instance.Track.Duration.Minutes + ":" + BackgroundAudioPlayer.Instance.Track.Duration.Seconds; 
using (IsolatedStorageFile storage = IsolatedStorageFile.GetUserStoreForApplication()) 
{ 
var stream = storage.OpenFile(BackgroundAudioPlayer.Instance.Track.AlbumArt.OriginalString, System.IO.FileMode.Open); 
var bitmapImage = new BitmapImage(); 
bitmapImage.SetSource(stream); 
imge1.Source = bitmapImage; 
stream.Close(); 
} 
} 
} 
 
//播放/暂停 
private void Button_Click_1(object sender, RoutedEventArgs e) 
{ 
if (PlayState.Playing == BackgroundAudioPlayer.Instance.PlayerState) 
BackgroundAudioPlayer.Instance.Pause(); 
else 
BackgroundAudioPlayer.Instance.Play(); 
} 
//向前 
private void Button_Click_2(object sender, RoutedEventArgs e) 
{ 
BackgroundAudioPlayer.Instance.SkipPrevious(); 
button2.IsEnabled = false; 
} 
//向后 
private void Button_Click_3(object sender, RoutedEventArgs e) 
{ 
BackgroundAudioPlayer.Instance.SkipNext(); 
button3.IsEnabled = false; 
} 
} 
 
   
 
四、后台文件传输 
  后台文件传输允许我们实现下载上传文件的功能,他限制系统中同时运行的传输任务不能超过两个,并且下载的文件只能存放在本地文件夹的/shared/transfers目录下。下面我们实现一个后台传输任务,下载博客相册中的一张照片。 
 
[XAML] 
 
 
     
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
[C#] 
 
 
    public partial class MainPage : PhoneApplicationPage 
{ 
// 构造函数 
public MainPage() 
{ 
InitializeComponent(); 
} 
protected override void OnNavigatedTo(System.Windows.Navigation.NavigationEventArgs e) 
{ 
initTransferRequest(); 
base.OnNavigatedTo(e); 
} 
private void initTransferRequest() 
{ 
//获取第一个后台传输任务 
var transferRequest = BackgroundTransferService.Requests.FirstOrDefault(); 
if (transferRequest == null) 
{ 
textblock1.Text = "无后台传输任务"; 
button1.IsEnabled = true; 
return; 
} 
//当传输状态改变时: 
transferRequest.TransferStatusChanged += new EventHandler(transfer_TransferStatusChanged); 
//当传输进度改变时: 
transferRequest.TransferProgressChanged += new EventHandler(transfer_TransferProgressChanged); 
updatesStatus(transferRequest); 
button1.IsEnabled = false; 
} 
void transfer_TransferStatusChanged(object sender, BackgroundTransferEventArgs e) 
{ 
updatesStatus(e.Request); 
} 
void transfer_TransferProgressChanged(object sender, BackgroundTransferEventArgs e) 
{ 
updatesStatus(e.Request); 
} 
 
void updatesStatus(BackgroundTransferRequest transferRequest) 
{ 
textblock1.Text = "传输状态:" + transferRequest.TransferStatus.ToString() 
+ " 已下载字节:" + transferRequest.BytesReceived 
+ "总字节:" + transferRequest.TotalBytesToReceive; 
} 
private void Button_Click_1(object sender, RoutedEventArgs e) 
{ 
string fileurlstring = "http://images.iyunv.com/cnblogs_com/lipan/319399/o_Large.png"; 
Uri uri = new Uri(Uri.EscapeUriString(fileurlstring), UriKind.RelativeOrAbsolute); 
BackgroundTransferRequest transferRequest = new BackgroundTransferRequest(uri); 
transferRequest.Method = "GET"; 
using (IsolatedStorageFile isoStore = IsolatedStorageFile.GetUserStoreForApplication()) 
{ 
if (!isoStore.DirectoryExists("/shared/transfers")) 
{ 
isoStore.CreateDirectory("/shared/transfers"); 
} 
} 
//文件下载后存放位置(为本地文件夹相对位置) 
transferRequest.DownloadLocation = new Uri("shared/transfers/1.png", UriKind.RelativeOrAbsolute); 
//外接电源、WiFi的可用性对传输的影响 
transferRequest.TransferPreferences = TransferPreferences.AllowCellularAndBattery; 
try 
{ 
//添加到后台传输队列中 
BackgroundTransferService.Add(transferRequest); 
} 
catch (Exception ex) 
{ 
MessageBox.Show("无法添加后台传输请求。" + ex.Message); 
} 
initTransferRequest(); 
} 
//清除传输队列已完成的任务 
private void Button_Click_2(object sender, RoutedEventArgs e) 
{ 
foreach (var transferRequest in BackgroundTransferService.Requests) 
{ 
if (transferRequest.TransferStatus == TransferStatus.Completed) 
{ 
try 
{ 
BackgroundTransferService.Remove(transferRequest); 
} 
catch 
{ 
} 
} 
} 
initTransferRequest(); 
} 
} 
 
   
 
五、后台辅助线程 
  后台辅助线程虽然名字这么叫,但是它不能在后台运行,我们可以用它来执行一个任务,并且可以实时获取执行的进度,实现代码如下: 
 
[XAML] 
 
 
         
 
 
 
 
 
 
 
 
 
 
 
 
[C#] 
 
 
    public partial class MainPage : PhoneApplicationPage 
{ 
private BackgroundWorker bw = new BackgroundWorker(); 
public MainPage() 
{ 
InitializeComponent(); 
bw.WorkerReportsProgress = true; 
bw.WorkerSupportsCancellation = true; 
bw.DoWork += new DoWorkEventHandler(bw_DoWork); 
bw.ProgressChanged += new ProgressChangedEventHandler(bw_ProgressChanged); 
bw.RunWorkerCompleted += new RunWorkerCompletedEventHandler(bw_RunWorkerCompleted); 
} 
private void buttonStart_Click(object sender, RoutedEventArgs e) 
{ 
if (bw.IsBusy != true) 
{ 
bw.RunWorkerAsync(); 
} 
} 
private void buttonCancel_Click(object sender, RoutedEventArgs e) 
{ 
if (bw.WorkerSupportsCancellation == true) 
{ 
bw.CancelAsync(); 
} 
} 
private void bw_DoWork(object sender, DoWorkEventArgs e) 
{ 
BackgroundWorker worker = sender as BackgroundWorker; 
for (int i = 1; i |