Windows phone 8 是一个单任务操作系统,任何时候都只有一个应用处于活跃状态,这里的多任务是指对后台任务的支持。本节我们先讲讲应用程序的运行状态,然后看看支持的后台任务,包括:后台代理、后台音频、后台文件传输、后台辅助线程等。

快速导航:
一、应用的状态
二、后台代理
三、后台音频
四、后台文件传输
五、后台辅助线程

一、应用的状态

1)应用的运行状态

我们通过图解来分析应用的运行状态,启动并置于前台界面的应用是唯一处于运行状态的,其他的操作,比如win键,后退导出应用,打开选择器和启动器时都会让当前运行的应用进入休眠状态,如果系统内存不足,处于休眠状态的应用可能会被系统逻辑删除。下面的图示演示了这个过程。


2)如何恢复状态

当应用处于休眠状态时,它的状态信息仍然保留在内存中,用户下次切换进去后不会有任何变化。但是当应用被逻辑删除后,这些状态信息就会丢失,比如表单填写的内容都会消失,为了避免这种情况,我们需要手动保留状态信息。
    首先,我们在mainpage定义一些页面表单控件:

[XAML]

        <Grid x:Name="ContentPanel" Grid.Row="1" Margin="12,0,12,0">
<TextBox Text="{Binding TextBox1Text, Mode=TwoWay}" Height="72"
HorizontalAlignment="Left" Margin="20,20,0,0" Name="textBox1"
VerticalAlignment="Top" Width="440" />
<CheckBox IsChecked="{Binding CheckBox1IsChecked, Mode=TwoWay}"
Content="CheckBox" Height="71" Name="checkBox1" Margin="20,100,0,0"
VerticalAlignment="Top"/>
<Slider Value="{Binding Slider1Value, Mode=TwoWay}" Height="84" Name="slider1"
Width="440" Margin="20,180,0,0" VerticalAlignment="Top"/>
<RadioButton IsChecked="{Binding RadioButton1IsChecked, Mode=TwoWay}"
Content="RadioButton 1" Height="71" Name="radioButton1"
GroupName="RadioButtonGroup" Margin="20,260,0,0" VerticalAlignment="Top"/>
<RadioButton IsChecked="{Binding RadioButton1IsChecked, Mode=TwoWay}"
Content="RadioButton 2" Height="71" Name="radioButton2"
GroupName="RadioButtonGroup" Margin="20,340,0,0" VerticalAlignment="Top"/>
</Grid>

我们需要实现在应用逻辑删除后能将其状态保持到页面的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; /// <summary>
/// 新实例还是现有实例
/// </summary>
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]

        <Grid x:Name="ContentPanel" Grid.Row="1" Margin="12,0,12,0">
<StackPanel>
<StackPanel Orientation="Vertical" Name="PeriodicStackPanel" Margin="0,0,0,40">
<TextBlock Text="定期代理" Style="{StaticResource PhoneTextTitle2Style}"/>
<StackPanel Orientation="Horizontal">
<TextBlock Text="名称: " Style="{StaticResource PhoneTextAccentStyle}"/>
<TextBlock Text="{Binding Name}" />
</StackPanel>
<StackPanel Orientation="Horizontal">
<TextBlock Text="是否可用" VerticalAlignment="Center" Style="{StaticResource PhoneTextAccentStyle}"/>
<CheckBox Name="PeriodicCheckBox" IsChecked="{Binding IsEnabled}" Checked="PeriodicCheckBox_Checked" Unchecked="PeriodicCheckBox_Unchecked"/>
</StackPanel>
<StackPanel Orientation="Horizontal">
<TextBlock Text="是否已计划: " Style="{StaticResource PhoneTextAccentStyle}"/>
<TextBlock Text="{Binding IsScheduled}" />
</StackPanel>
<StackPanel Orientation="Horizontal">
<TextBlock Text="上次计划运行时间: " Style="{StaticResource PhoneTextAccentStyle}"/>
<TextBlock Text="{Binding LastScheduledTime}" />
</StackPanel>
<StackPanel Orientation="Horizontal">
<TextBlock Text="计划结束时间: " Style="{StaticResource PhoneTextAccentStyle}"/>
<TextBlock Text="{Binding ExpirationTime}" />
</StackPanel>
<StackPanel Orientation="Horizontal">
<TextBlock Text="上一次代理运行退出的原因: " Style="{StaticResource PhoneTextAccentStyle}"/>
<TextBlock Text="{Binding LastExitReason}" />
</StackPanel>
</StackPanel>
<StackPanel Orientation="Vertical" Name="ResourceIntensiveStackPanel" Margin="0,0,0,40">
<TextBlock Text="资源密集型代理" Style="{StaticResource PhoneTextTitle2Style}"/>
<StackPanel Orientation="Horizontal">
<TextBlock Text="名称: " Style="{StaticResource PhoneTextAccentStyle}"/>
<TextBlock Text="{Binding Name}" />
</StackPanel>
<StackPanel Orientation="Horizontal">
<TextBlock Text="是否可用" VerticalAlignment="Center" Style="{StaticResource PhoneTextAccentStyle}"/>
<CheckBox Name="ResourceIntensiveCheckBox" IsChecked="{Binding IsEnabled}" Checked="ResourceIntensiveCheckBox_Checked" Unchecked="ResourceIntensiveCheckBox_Unchecked"/>
</StackPanel>
<StackPanel Orientation="Horizontal">
<TextBlock Text="是否已计划: " Style="{StaticResource PhoneTextAccentStyle}"/>
<TextBlock Text="{Binding IsScheduled}" />
</StackPanel>
<StackPanel Orientation="Horizontal">
<TextBlock Text="上次计划运行时间: " Style="{StaticResource PhoneTextAccentStyle}"/>
<TextBlock Text="{Binding LastScheduledTime}" />
</StackPanel>
<StackPanel Orientation="Horizontal">
<TextBlock Text="计划结束时间: " Style="{StaticResource PhoneTextAccentStyle}"/>
<TextBlock Text="{Binding ExpirationTime}" />
</StackPanel>
<StackPanel Orientation="Horizontal">
<TextBlock Text="上一次代理运行退出的原因: " Style="{StaticResource PhoneTextAccentStyle}"/>
<TextBlock Text="{Binding LastExitReason}" />
</StackPanel>
</StackPanel>
</StackPanel>
</Grid>

[C#]

    public partial class MainPage : PhoneApplicationPage
{
/// <summary>
/// 定期代理
/// </summary>
PeriodicTask periodicTask; /// <summary>
/// 资源密集型代理
/// </summary>
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);
} /// <summary>
/// 删除代理
/// </summary>
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<AudioTrack> _playList = new List<AudioTrack>
{
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))
}; /// <summary>
/// 当前播放位置
/// </summary>
static int currentTrackNumber = 0; /// <remarks>
/// AudioPlayer 实例可共享同一进程。
/// 静态字段可用于在 AudioPlayer 实例之间共享状态
/// 或与音频流代理通信。
/// </remarks>
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();
}
} /// <summary>
/// playstate 更改时调用,但 Error 状态除外(参见 OnError)
/// </summary>
/// <param name="player">BackgroundAudioPlayer</param>
/// <param name="track">在 playstate 更改时播放的曲目</param>
/// <param name="playState">播放机的新 playstate </param>
/// <remarks>
/// 无法取消播放状态更改。即使应用程序
/// 导致状态自行更改,假定应用程序已经选择了回调。
///
/// 值得注意的 playstate 事件
/// (a) TrackEnded: 播放器没有当前曲目时激活。代理可设置下一曲目。
/// (b) TrackReady: 音轨已设置完毕,现在可以播放。
///
/// 只在代理请求完成之后调用一次 NotifyComplete(),包括异步回调。
/// </remarks>
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();
} /// <summary>
/// 在用户使用应用程序/系统提供的用户界面请求操作时调用
/// </summary>
/// <param name="player">BackgroundAudioPlayer</param>
/// <param name="track">用户操作期间播放的曲目</param>
/// <param name="action">用户请求的操作</param>
/// <param name="param">与请求的操作相关联的数据。
/// 在当前版本中,此参数仅适合与 Seek 操作一起使用,
/// 以指明请求的乐曲的位置</param>
/// <remarks>
/// 用户操作不自动对系统状态进行任何更改;如果用户操作受支持,
/// 执行用户操作(如果这些操作受支持)。
///
/// 只在代理请求完成之后调用一次 NotifyComplete(),包括异步回调。
/// </remarks>
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();
} /// <summary>
/// 实现逻辑以获取下一个 AudioTrack 实例。
/// 在播放列表中,源可以是文件、Web 请求,等等。
/// </summary>
/// <remarks>
/// AudioTrack URI 确定源,它可以是:
/// (a) 独立存储器文件(相对 URI,表示独立存储器中的路径)
/// (b) HTTP URL(绝对 URI)
/// (c) MediaStreamSource (null)
/// </remarks>
/// <returns>AudioTrack 实例,或如果播放完毕,则返回 null</returns>
private AudioTrack GetNextTrack()
{
// TODO: 添加逻辑以获取下一条音轨
if (++currentTrackNumber >= _playList.Count) currentTrackNumber = 0;
AudioTrack track = _playList[currentTrackNumber]; // 指定曲目 return track;
} /// <summary>
/// 实现逻辑以获取前一个 AudioTrack 实例。
/// </summary>
/// <remarks>
/// AudioTrack URI 确定源,它可以是:
/// (a) 独立存储器文件(相对 URI,表示独立存储器中的路径)
/// (b) HTTP URL(绝对 URI)
/// (c) MediaStreamSource (null)
/// </remarks>
/// <returns>AudioTrack 实例,或如果不允许前一曲目,则返回 null</returns>
private AudioTrack GetPreviousTrack()
{
// TODO: 添加逻辑以获取前一条音轨
if (--currentTrackNumber < 0) currentTrackNumber = _playList.Count - 1;
AudioTrack track = _playList[currentTrackNumber]; // 指定曲目 return track;
} /// <summary>
/// 每次播放出错(如 AudioTrack 未正确下载)时调用
/// </summary>
/// <param name="player">BackgroundAudioPlayer</param>
/// <param name="track">出现错误的曲目</param>
/// <param name="error">出现的错误</param>
/// <param name="isFatal">如果为 true,则播放不能继续并且曲目播放将停止</param>
/// <remarks>
/// 不保证在所有情况下都调用此方法。例如,如果后台代理程序
/// 本身具有未处理的异常,则不会回调它来处理它自己的错误。
/// </remarks>
protected override void OnError(BackgroundAudioPlayer player, AudioTrack track, Exception error, bool isFatal)
{
if (isFatal)
{
Abort();
}
else
{
NotifyComplete();
} } /// <summary>
/// 取消代理请求时调用
/// </summary>
/// <remarks>
/// 取消请求后,代理需要 5 秒钟完成其工作,
/// 通过调用 NotifyComplete()/Abort()。
/// </remarks>
protected override void OnCancel()
{ }
}

最后,我们在mainpage中添加对播放的控制。

[XAML]

    <Grid x:Name="LayoutRoot" Background="Transparent">
<Grid.RowDefinitions>
<RowDefinition Height="Auto"/>
<RowDefinition Height="*"/>
</Grid.RowDefinitions> <!--TitlePanel 包含应用程序的名称和页标题-->
<StackPanel x:Name="TitlePanel" Grid.Row="0" Margin="12,17,0,28">
<TextBlock x:Name="ApplicationTitle" Text="后台音频" Style="{StaticResource PhoneTextNormalStyle}"/>
</StackPanel> <!--ContentPanel - 在此处放置其他内容-->
<Grid x:Name="ContentPanel" Grid.Row="1" Margin="12,0,12,0">
<Button x:Name="button2" Content="〈" HorizontalAlignment="Left" Margin="13,10,0,0" VerticalAlignment="Top" Click="Button_Click_2"/>
<Button x:Name="button1" Content="▶" HorizontalAlignment="Left" Margin="75,10,0,0" VerticalAlignment="Top" Click="Button_Click_1"/>
<Button x:Name="button3" Content="〉" HorizontalAlignment="Left" Margin="136,10,0,0" VerticalAlignment="Top" Click="Button_Click_3" />
<TextBlock x:Name="textblock1" HorizontalAlignment="Left" Margin="22,87,0,0" TextWrapping="Wrap" VerticalAlignment="Top"/>
<Image x:Name="imge1" HorizontalAlignment="Left" Height="100" Margin="22,142,0,0" VerticalAlignment="Top" Width="100"/> </Grid>
</Grid>

[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]

    <Grid x:Name="LayoutRoot" Background="Transparent">
<Grid.RowDefinitions>
<RowDefinition Height="Auto"/>
<RowDefinition Height="*"/>
</Grid.RowDefinitions> <!--TitlePanel 包含应用程序的名称和页标题-->
<StackPanel x:Name="TitlePanel" Grid.Row="0" Margin="12,17,0,28">
<TextBlock x:Name="ApplicationTitle" Text="后台传输" Style="{StaticResource PhoneTextNormalStyle}"/>
</StackPanel> <!--ContentPanel - 在此处放置其他内容-->
<Grid x:Name="ContentPanel" Grid.Row="1" Margin="12,0,12,0">
<TextBlock x:Name="textblock1" HorizontalAlignment="Left" Margin="10,198,0,0" TextWrapping="Wrap" VerticalAlignment="Top"/>
<Button Content="清除传输队列中已完成的任务" HorizontalAlignment="Left" Margin="0,85,0,0" VerticalAlignment="Top" Click="Button_Click_2"/>
</Grid>
<Button x:Name="button1" Content="添加一个后台传输" HorizontalAlignment="Left" Margin="12,10,0,0" Grid.Row="1" VerticalAlignment="Top" Click="Button_Click_1"/>
</Grid>

[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<BackgroundTransferEventArgs>(transfer_TransferStatusChanged);
//当传输进度改变时:
transferRequest.TransferProgressChanged += new EventHandler<BackgroundTransferEventArgs>(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.cnblogs.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]

        <Grid x:Name="ContentPanel" Grid.Row="1" Margin="12,0,12,0">
<StackPanel>
<StackPanel Orientation="Horizontal"
HorizontalAlignment="Left" VerticalAlignment="Top"
Margin="10" >
<Button x:Name="buttonStart" Content="开始" Click="buttonStart_Click"
Width="200" />
<Button x:Name="buttonCancel" Content="取消" Click="buttonCancel_Click"
Width="200" />
</StackPanel>
<StackPanel Margin="10,50,0,0" Orientation="Horizontal">
<TextBlock Text="进度: " />
<TextBlock x:Name="tbProgress" />
</StackPanel>
</StackPanel> </Grid>

[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 <= 10; i++)
{
if ((worker.CancellationPending == true))
{
e.Cancel = true;
break;
}
else
{
// Perform a time consuming operation and report progress.
System.Threading.Thread.Sleep(500);
worker.ReportProgress(i * 10);
}
}
}
private void bw_RunWorkerCompleted(object sender, RunWorkerCompletedEventArgs e)
{
if (e.Cancelled == true)
{
this.tbProgress.Text = "Canceled!";
} else if (!(e.Error == null))
{
this.tbProgress.Text = ("Error: " + e.Error.Message);
} else
{
this.tbProgress.Text = "Done!";
}
}
private void bw_ProgressChanged(object sender, ProgressChangedEventArgs e)
{
this.tbProgress.Text = (e.ProgressPercentage.ToString() + "%");
}
}

最新文章

  1. centos7.1 x86_64系统安装openstack(Mitaka)一
  2. loadrunner --global schedule设置
  3. css3实现switch开关效果
  4. &lt;十二&gt;面向对象分析之UML核心元素之节点和设备
  5. jquery easyui防止超出浏览器边界
  6. PL/SQL:使用pragma restrict_references限制包权限
  7. Linux企业级项目实践之网络爬虫(2)——网络爬虫的结构与工作流程
  8. mysql中varchar最长多少
  9. Hdu 5050 Divided Land
  10. 用QFileSystemWatcher来监视文件和目录的改变(内部还是使用了timer)
  11. VS2013中Python学习环境搭建
  12. Maple trees(最小覆盖圆)
  13. C-Lodop获取打印机列表Create_Printer_List
  14. 如何用 async 控制流程
  15. Linux 操作系统镜像下载
  16. RabbitMQ 客户端开发向导
  17. Django 多表查询 聚合查询 分组查询 F查询 Q查询
  18. iOS 设置UILabel 的内边距
  19. bazel使用汇总
  20. webpack基本使用教程

热门文章

  1. js复习(一)
  2. java NIO经典实例
  3. jdk与jre的区别
  4. 论职务犯罪案件侦查 z
  5. DXperience-11.1.5 破解
  6. 织梦后台更新,报错DedeCMS Error:Tag disabled:"php" more...
  7. .NET 请求被挂起,前端轮询,委托
  8. 查看.netframeword版本
  9. 把exe嵌入到自己的exe中。delphi xe3
  10. Codeforces 626E Simple Skewness 「数学」「二分」