Windows phone 8 学习笔记(6) 多任务

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

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

一、应用的状态

1)应用的运行状态

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

2)如何恢复状态

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

[XAML]
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
<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#]
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
[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#]
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
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#]
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
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]
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
<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#]
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
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#]

我们需要在解决方案中添加Windows phone 音频播放代理项目,并在应用项目中添加对其的引用。修改AudioPlayer.cs代码如下:

[C#]

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

[XAML]
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
<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#]
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
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]
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
<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#]
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
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]
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
<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#]
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
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() + "%");
    }
}

 

作者:[Lipan]
出处:[http://www.cnblogs.com/lipan/]
版权声明:本文的版权归作者与博客园共有。转载时须注明原文出处以及作者,并保留原文指向型链接,不得更改原文内容。否则作者将保留追究其法律责任。
posted @   lipan  阅读(2924)  评论(2编辑  收藏  举报
编辑推荐:
· 10年+ .NET Coder 心语,封装的思维:从隐藏、稳定开始理解其本质意义
· .NET Core 中如何实现缓存的预热?
· 从 HTTP 原因短语缺失研究 HTTP/2 和 HTTP/3 的设计差异
· AI与.NET技术实操系列:向量存储与相似性搜索在 .NET 中的实现
· 基于Microsoft.Extensions.AI核心库实现RAG应用
阅读排行:
· 10年+ .NET Coder 心语 ── 封装的思维:从隐藏、稳定开始理解其本质意义
· 地球OL攻略 —— 某应届生求职总结
· 提示词工程——AI应用必不可少的技术
· Open-Sora 2.0 重磅开源!
· 周边上新:园子的第一款马克杯温暖上架
点击右上角即可分享
微信分享提示