本章介绍一个使用Bing搜索引擎背景图接口实现的一个应用——Bing在线壁纸,讲解如何使用网络的接口来实现一个壁纸下载,壁纸列表展示和网络请求封装的内容。通过该例子我们可以学习到如何使用网络编程的知识和开放的接口来实现一些有趣的应用程序,如何在项目中封装相关的功能模块,从而进一步地了解Windows 10应用程序开发的过程。
微软的Bing搜索引擎每天都会挑选出一张图片作为今天的主题,并且会对图片的含义或者图片所代表的意思进行一番解说,每天的图片和故事都不一样,并且有时候不同国家挑选的图片和故事也不一样。你在网页上打开Bing搜索的中国区主页(http://cn.bing.com/)那么就可以看到今天的bing壁纸的内容和故事,每天都给用户带来有内涵有深度的图片和故事。在桌面的浏览器看到的Bing壁纸会很大,除此之外Bing也有手机的版本,对于手机版本Bing也是适配和符合手机分辨率的壁纸大小。
接下来我们要实现的Bing在线壁纸的应用程序是使用了微软的Bing壁纸获取的接口,把Bing搜索引擎每天的壁纸和故事通过网络接口来获取,然后在应用程序中显示出来。因为Bing壁纸每天都是不一样的,所以我们要实现的应用可以让用户不仅仅可以看到今天的壁纸和故事,也可以获取到以前的壁纸和故事。在Bing在线壁纸应用里面主要实现了两个功能,一个是显示出今天中国的壁纸主题,第二个是可以根据时间来获取壁纸的主题和故事,然后使用列表控件把主要国家的壁纸和故事展示出来,同时用户可以对壁纸进行相关的操作,如在浏览器打开,收藏等功能。
Bing壁纸获取的网络接口网址格式如下所示:
http://appserver.m.bing.net/BackgroundImageService/TodayImageService.svc/GetTodayImage?dateOffset=0&urlEncodeHeaders=true&osName=windowsPhone&osVersion=8.10&orientation=480x800&deviceName=WP8&mkt=en-US
这个接口可以通过HTTP的Get请求来获取,可以直接在浏览器中打开链接变可以获取到图片的显示效果如图23.1所示。下面我们来看一下接口链接所传递过去的一些参数的说明。
(1 )dateOffset : 表示已经过去的天数,比如0表示是今天,1则表示是昨天,4表示是获取4天前的壁纸,以此类推。
(2 )orientation : 表示是获取的壁纸图片的分辨率,你可以设置为480×800、1024×768和800×480。
(3 )mkt : 表示是国家地区的语言编码,你可以通过不同的语言编码来获取不同的国家地区的Bing壁纸图片和信息,比如可以设置为zh-CN、en-US等。
那么直接从使用该接口进行下载获取到的数据是整张图片的数据,如果要获取到图片的主题和故事,需要通过HTTP头相关的参数来获取。我们再利用一下IE浏览器,在IE浏览器上再次打开上面的链接,然后从浏览器的设置选项,找到“F12开发人员工具”的选项,点击启动IE浏览器的开发人员工具,然后在这里查看网络请求的头信息。找到网络类别,选择“详细信息”->“响应标头”就可以看到HTTP请求返回的响应头的信息,如图23.2所示。在这个响应头里面我们主要是要找到关于图片主题和故事的信息,如下所示:
(1 )Image-Info-Credit (包含了图片的主题和版权信息)
Microphotograph%20of%20giant%20salvinia,%20a%20water%20fern%20(%C2%A9%20Martin%20Oeggerli/Visuals%20Unlimited,%20Inc.)
(2 )Image-Info-Hotspot-1 (图片的热点说明1 )
Let's%20switch%20the%20magnification%20back%20to...;images;Something%20more%20familiar;giant%20salvinia;;
(3 )Image-Info-Hotspot-2 (图片的热点说明2 )
Microscope%20slides%20may%20remind%20you%20of%20science%20class,%20but%20don't%20worry:%20There%20won't%20be%20a%20quiz.;images;Just%20marvel%20at%20how%20amazing%20plants%20can%20look%20up%20close;microphotography%20plants;;
在我们掌握了Bing壁纸的接口信息规则之后,接下来要做的事情就是根据业务的规则来封装壁纸请服务,把与壁纸请求关联的业务规则通过一个类来封装起来,然后提供相关的方法给外部来调用。通过对应用程序相关的服务封装成为公共的方法或者接口这是在程序架构上最基本的原则,这样的好处是不仅仅可以实现程序代码的共享,还可以实现解耦,程序逻辑清晰等好处。在我们要封装bing壁纸请求服务的时候,我们首先要考虑的问题是业务的情景,然后根据这个情景我们需要封装一个什么样的方法?传递进来的是什么参数?然后返回什么样的返回值?通过什么方式返回结果?首先根据我们的网络接口和业务情景,可以分析出来我们要实现的功能是,根据时间来获取当前的壁纸详情,壁纸详情会包括壁纸地址、壁纸主题、壁纸热点说明这些信息,然后同一天还需要获取多个国家的壁纸信息。通过这些分析,我们知道要传递进来的参数是时间,然后获取到的结果是多个国家在当天的壁纸信息。因为多个国家的壁纸信息,根据目前的接口情况是需要发起多次的请求才能获取到,并且HTTP的请求是异步请求,所在当发起这个网络服务请求时我们是很多必要去提供当前的进度信息,因此我们可以提供一个进度条的事件来返回进度结果、异常消息、结果信息等。通过上面的分析,现在基本清晰了这个壁纸请求服务的类该怎么封装了,下面我们来看一下封装的类和代码。
PictureInfo类:表示是壁纸信息的类,会包含了壁纸地址、主题、热点信息。 ------------------------------------------------------------------------------------------------------------------ /// <summary> /// 壁纸图片信息类 /// </summary> public class PictureInfo { // 壁纸的热点说明信息 public List<string> hotspot { get; set; } // 壁纸的主题 public string imgTitle { get; set; } // 壁纸图片的地址 public Uri imageUri { get; set; } // 壁纸位图对象 public BitmapImage image { get; set; } // 国家代码 public string countryCode { get; set; } // 壁纸图片信息类的初始化 public PictureInfo(string _countryCode, string _imgTitle, string _imgUri) { countryCode = _countryCode; imgTitle = _imgTitle; imageUri = new Uri(_imgUri); } }
ProgressEventArgs类:表示获取壁纸信息的进度返回的参数类。 ------------------------------------------------------------------------------------------------------------------ // 下载进度参数类 public class ProgressEventArgs : EventArgs { // 进度的百分比值 public int ProgressValue { get; set; } // 是否完成了所有图片的下载 public bool Complete { get; set; } // 是否发生异常 public bool IsException { get; set; } // 异常消息 public string ExceptionInfo { get; set; } // 下载的图片列表信息,未完成时为null public List<PictureInfo> Pictures { get; set; } }
WallpapersService类:表示获取壁纸信息的服务类,该类使用单例模式来设计,因为对于整个应用程序,这个服务类也只需要一个对象,更加适合设计成单例模式。 ------------------------------------------------------------------------------------------------------------------ /// <summary> /// 获取壁纸信息的服务类 /// </summary> public class WallpapersService { // 距离今天的天数,表示获取壁纸的时间 private int selectedDay; // 国家代码 private List<string> countries = new List<string>(new string[] { "zh-CN", "fr-FR", "de-DE","en-US", "ja-JP","en-GB"}); // 需要获取的请求次数 private int http_times; // 总的请求次数 private int http_times_all; // 是否已经开始下载了 private bool downloading = false; // 已经下载的图片信息 private Dictionary<int, List<PictureInfo>> allHaveDownloadPictures; // 完成进度事件 public EventHandler<ProgressEventArgs> GetOneDayWallpapersProgressEvent; // 触发完成进度事件的方法 private void OnGetOneDayWallpapersProgressEvent(ProgressEventArgs progressEventArgs) { if (GetOneDayWallpapersProgressEvent != null) { GetOneDayWallpapersProgressEvent.Invoke(this, progressEventArgs); } } private static WallpapersService _Current; // 单例对象 public static WallpapersService Current { get { if (_Current == null) _Current = new WallpapersService(); return _Current; } } // 初始化对象 private WallpapersService() { allHaveDownloadPictures = new Dictionary<int, List<PictureInfo>>(); } /// <summary> /// 获取所选时间的图片 /// </summary> /// <param name="selectedDay">表示距离今天的时间,0表示今天,1表示昨天……</param> public void GetOneDayWallpapers(int day) { // 如果当前正在下载图片则跳出该方法的调用 if (downloading) return; // 把当前的服务标志位正在下载 downloading = true; selectedDay = day; // 如果当前并没有该日期的数据则需要在字典对象里面添加上 if(!allHaveDownloadPictures.Keys.Contains(selectedDay)) { allHaveDownloadPictures.Add(selectedDay, new List<PictureInfo>()); } // 拼接网络请求地址 string format = "http://appserver.m.bing.net/BackgroundImageService/TodayImageService.svc/GetTodayImage?dateOffset=-{0}&urlEncodeHeaders=true&osName=windowsPhone&osVersion=8.10&orientation=480x800&deviceName=WP8Device&mkt={1}"; http_times_all = http_times = countries.Count<string>(); foreach (string country in countries) { // 判断该请求是否已经请求过,通过其所对应的日期和国家 if (allHaveDownloadPictures[selectedDay].Select(item => item.countryCode == country).Count() == 0) { //图片下载url string bingUrlFmt = string.Format(format, selectedDay, country); //开始下载 HttpWebRequest request = (HttpWebRequest)WebRequest.Create(bingUrlFmt); request.Method = "GET"; request.BeginGetResponse(result => this.responseHandler(result, request, country, bingUrlFmt), null); } else { // 如果壁纸信息已经获取过则不要再重复获取,直接返回进度信息 SetProgress(); } } } // 获取的壁纸图片信息回调方法 private void responseHandler(IAsyncResult asyncResult, HttpWebRequest request, string myloc, string _imgUri) { HttpWebResponse response; try { response = (HttpWebResponse)request.EndGetResponse(asyncResult); } catch(Exception e) { downloading = false; // 返回异常信息 OnGetOneDayWallpapersProgressEvent( new ProgressEventArgs { IsException = true, Complete = false, ExceptionInfo = e.Message, ProgressValue = 0, Pictures = null }); return; } if (request.HaveResponse) { // 图片的热点说明信息 List<string> _hotspot = new List<string>(); string _imgTitle = ""; // 通过HTTP请求头传输图片相关的信息 foreach (string str in response.Headers.AllKeys) { string str2 = str; string str3 = response.Headers[str]; // 获取图片的说明信息和版权信息 if (str2.Contains("Image-Info-Credit")) { _imgTitle = WebUtility.UrlDecode(str3); } // 获取图片的热点介绍信息 else if (str2.Contains("Image-Info-Hotspot-")) { string[] strArray = WebUtility.UrlDecode(str3).Replace(" ","").Split(new char[] { ';' }); _hotspot.AddRange(strArray); } } PictureInfo info = new PictureInfo(myloc, _imgTitle, _imgUri); info.hotspot = _hotspot; allHaveDownloadPictures[selectedDay].Add(info); } Debug.WriteLine("allHaveDownloadPictures[selectedDay].Count:" + allHaveDownloadPictures[selectedDay].Count); // 返回进度信息 SetProgress(); } // 返回进度的信息 private void SetProgress() { http_times--; bool finish = http_times == 0; if (finish) downloading = false; // 返回结果 OnGetOneDayWallpapersProgressEvent( new ProgressEventArgs { IsException = false, Complete = finish, ExceptionInfo = "", ProgressValue = (int)(((float)(http_times_all - http_times) / (float)http_times_all) * 100), Pictures = allHaveDownloadPictures[selectedDay] }); } }
那么在这一小节我们要实现的功能是Bing在线壁纸的首页页面,在首页页面主要是添加上最近几天的壁纸的链接,点击直接跳转到壁纸详情列表里面,因为一般情况大部分用户会比较关注最近的几天壁纸信息和主题,微软的这个壁纸通常都是会和最近的新闻或者节日相关。当然我们在首页也提供自定义的选择,让用户可以自定义选择几天前的壁纸信息的获取。首页的背景直接使用了当天中国地区的壁纸图片。
MainPage.xaml页面主要代码:应用首页的设计和逻辑。 ------------------------------------------------------------------------------------------------------------------ <Page.Resources> <!--定义了一个显示出用户自定义日期的面板的动画资源,动画的效果是从上往下拉出来--> <Storyboard x:Name="showMorePicture"> <DoubleAnimation Storyboard.TargetName="topTransform" Storyboard.TargetProperty="Y" From="-300" To="0" Duration="0:0:0.3"></DoubleAnimation> </Storyboard> </Page.Resources> <Grid> <Grid.RowDefinitions> <RowDefinition Height="Auto"/> <RowDefinition Height="*"/> </Grid.RowDefinitions> <Grid Opacity="0.5" Grid.RowSpan="2"> <Grid.Background> <ImageBrush> <ImageBrush.ImageSource> <BitmapImage x:Name="background"></BitmapImage> </ImageBrush.ImageSource> </ImageBrush> </Grid.Background> </Grid> <StackPanel x:Name="TitlePanel" Grid.Row="0" Margin="12,0,0,28"> <TextBlock Text="Bing壁纸" FontSize="20" /> </StackPanel> <Grid x:Name="ContentPanel" Grid.Row="1" Margin="12,0,12,0"> <!--添加了5个HyperlinkButton控件,其中最后一个按钮会触发动画来显示出用户自定义日期的面板--> <StackPanel VerticalAlignment="Bottom"> <HyperlinkButton Content="今天壁纸" x:Name="today" Click="today_Click"></HyperlinkButton> <HyperlinkButton Content="昨天壁纸" x:Name="yesterday" Click="yesterday_Click"></HyperlinkButton> <HyperlinkButton Content="2天前壁纸" x:Name="twodayago" Click="twodayago_Click"></HyperlinkButton> <HyperlinkButton Content="3天前壁纸" x:Name="threedayago" Click="threedayago_Click"></HyperlinkButton> <HyperlinkButton Content="更早的壁纸" x:Name="other" Click="other_Click"></HyperlinkButton> </StackPanel> </Grid> <!--自定义日期的面板--> <StackPanel x:Name="topStackPanel" Orientation="Vertical" Grid.RowSpan="2" Background="{ThemeResource ApplicationPageBackgroundThemeBrush}" Height="300" VerticalAlignment="Top"> <!--面板默认是在屏幕最上方顶上,用户一开始并看不到--> <StackPanel.RenderTransform> <TranslateTransform x:Name="topTransform" Y="-300"></TranslateTransform> </StackPanel.RenderTransform> <TextBlock Text="选择时间:" Margin="12 30 0 0" VerticalAlignment="Center"></TextBlock> <StackPanel Orientation="Horizontal" Margin="12 30 0 0"> <!--使用两个AppBarButton来控制日期的增加和减少--> <AppBarButton Icon="Remove" IsCompact="True" x:Name="minus_bar" Click="minus_bar_Click" VerticalAlignment="Center"/> <TextBlock Text="4" x:Name="dayNumber" Margin="20 0 20 0" FontSize="20" VerticalAlignment="Center"></TextBlock> <AppBarButton Icon="Add" IsCompact="True" x:Name="plus_bar" Click="plus_bar_Click" VerticalAlignment="Center"/> <TextBlock Text="天前的壁纸" VerticalAlignment="Center" Margin="24 0 0 0"></TextBlock> </StackPanel> <!--通过按钮事件来触发跳转到壁纸详情列表页面--> <Button Content="查看壁纸" x:Name="go" Click="go_Click" Margin="20 30 0 0"></Button> </StackPanel> </Grid>
MainPage.xaml.cs页面主要代码 ------------------------------------------------------------------------------------------------------------------ public sealed partial class MainPage : Page { private string TodayPictureUri = "http://appserver.m.bing.net/BackgroundImageService/TodayImageService.svc/GetTodayImage?dateOffset=-0&urlEncodeHeaders=true&osName=windowsphone&osVersion=8.10&orientation=480x800&deviceName=WP8Device&mkt=zh-CN"; public MainPage() { this.InitializeComponent(); } // 进入当前的页面 protected override void OnNavigatedTo(NavigationEventArgs e) { background.UriSource = new Uri(TodayPictureUri); } // 查看今天壁纸 private void today_Click(object sender, RoutedEventArgs e) { Frame.Navigate(typeof(DayPicturesPage), 0); } // 查看昨天壁纸 private void yesterday_Click(object sender, RoutedEventArgs e) { Frame.Navigate(typeof(DayPicturesPage), 1); } // 查看两天前壁纸 private void twodayago_Click(object sender, RoutedEventArgs e) { Frame.Navigate(typeof(DayPicturesPage), 2); } // 查看三天前壁纸 private void threedayago_Click(object sender, RoutedEventArgs e) { Frame.Navigate(typeof(DayPicturesPage), 3); } // 查看更早壁纸 private void other_Click(object sender, RoutedEventArgs e) { showMorePicture.Begin(); } // 查看更早壁纸,减少天数的图标按钮事件 private void minus_bar_Click(object sender, RoutedEventArgs e) { int day = Int32.Parse(dayNumber.Text); if(day>0) { day--; dayNumber.Text = day.ToString(); } } // 查看更早壁纸,增加天数的图标按钮事件 private void plus_bar_Click(object sender, RoutedEventArgs e) { int day = Int32.Parse(dayNumber.Text); day++; dayNumber.Text = day.ToString(); } // 前往查看自定义天数的壁纸 private void go_Click(object sender, RoutedEventArgs e) { Frame.Navigate(typeof(DayPicturesPage), Int32.Parse(dayNumber.Text)); } }
Bing在线壁纸的首页显示的效果如图23.3所示,点击“更早的壁纸”按钮会出现一个动画,自定义的面板会从上面下拉下来,显示的效果如图23.4所示。
接下来我们要实现壁纸列表详情页面,在该列表里会展示某一天的壁纸信息,采用水平滚动的列表来显示壁纸的
在这里对两个地方做了特殊的适配,一个是“更早的壁纸”弹出层做了适配,还有一个地方是首页的背景图。下面先看下“更早的壁纸”弹出层的适配。
“更早的壁纸”弹出层的适配采用了适配触发器的方式去实现,当应用程序的宽度大于600像素的时候,弹出层的StackPanel面板采用水平排列的方式,并且高度从300像素改为150像素,如果手机横屏放置那么该适配效果也一样会生效。添加的适配代码如下所示:
<VisualStateManager.VisualStateGroups> <VisualStateGroup> <VisualState x:Name="SideBySideState"> <VisualState.StateTriggers> <AdaptiveTrigger MinWindowWidth="600" /> </VisualState.StateTriggers> <VisualState.Setters> <Setter Target="topStackPanel.Orientation" Value="Horizontal"/> <Setter Target="topStackPanel.Height" Value="150"/> </VisualState.Setters> </VisualState> </VisualStateGroup> </VisualStateManager.VisualStateGroups>
当宽度大于500像素的时候,弹出层的显示效果如图23.5所示:
对首页背景图片的适配采用了后台C#代码来实现,当应用程序的宽度大于500像素的时候采用大分辨率的Bing图片,实现的思路是通过Window.Current.SizeChanged事件来获取应用程序的窗体变化情况,然后再进行判断是否选择大分辨率的图片。代码如下所示:
public MainPage() { this.InitializeComponent(); Window.Current.SizeChanged += Current_SizeChanged; } private void Current_SizeChanged(object sender, Windows.UI.Core.WindowSizeChangedEventArgs e) { if (e.Size.Width <= 500) { background.UriSource = new Uri(TodayPictureUri); } else { background.UriSource = new Uri(TodayPictureUri_Big); } }
接下来我们要实现壁纸列表详情页面,在该列表里会展示某一天的壁纸信息,采用水平滚动的列表来显示壁纸的图片显示和壁纸的热点信息说明,同时还提供了在浏览器打开和保存到应用文件的两个按钮操作功能。在该页面就需要去调用获取壁纸信息的服务类WallpapersService类的GetOneDayWallpapers方法来获取壁纸列表的信息,同时通过GetOneDayWallpapersProgressEvent事件把请求的进度在页面上显示出来,获取完成之后再把壁纸的信息绑定到列表控件。
DayPicturesPage.xaml文件主要代码:使用列表显示壁纸图片和信息的详情。 ------------------------------------------------------------------------------------------------------------------ <Page.Resources> <!--绑定转换器:把国家代码转化国家的名称--> <local:CountryNameConverter x:Key="CountryNameConverter" /> </Page.Resources> …… <!--使用ItemsControl列表控件来显示壁纸的信息--> <ItemsControl Grid.Row="1" x:Name="pictureList" > <ItemsControl.ItemsPanel> <ItemsPanelTemplate> <!--设置列表控件的项目水平排列--> <StackPanel Orientation="Horizontal"></StackPanel> </ItemsPanelTemplate> </ItemsControl.ItemsPanel> <ItemsControl.Template> <ControlTemplate> <!--设置列表控件的面板为水平垂直可滚动--> <ScrollViewer ScrollViewer.HorizontalScrollBarVisibility="Visible" ScrollViewer.VerticalScrollBarVisibility = "Visible"> <ItemsPresenter /> </ScrollViewer> </ControlTemplate> </ItemsControl.Template> <ItemsControl.ItemTemplate> <DataTemplate> <!--列表控件模板绑定壁纸的显示和相关的信息--> <Grid Width="340"> <Image Opacity="0.5" Stretch="Uniform" > <Image.Source> <BitmapImage UriSource="{Binding imageUri}" ></BitmapImage> </Image.Source> </Image> <!--绑定国家代码--> <TextBlock FontSize="20" Text="{Binding countryCode,Converter={StaticResource CountryNameConverter}}" Margin="24 30 0 0"></TextBlock> <StackPanel Margin="24 90 0 0" > <!--绑定壁纸主题--> <TextBlock FontSize="20" Text="{Binding imgTitle}" TextWrapping="Wrap"></TextBlock> <!--使用ListView列表绑定热点说明信息--> <ListView ItemsSource="{Binding hotspot}" Height="300"> <ListView.ItemTemplate> <DataTemplate> <TextBlock Text="{Binding}" FontSize="15" TextWrapping="Wrap"></TextBlock> </DataTemplate> </ListView.ItemTemplate> </ListView> </StackPanel> <!--保存和查看按钮--> <StackPanel Orientation="Horizontal" HorizontalAlignment="Right" VerticalAlignment="Top" > <AppBarButton Icon="View" Label="查看" x:Name="view" Click="view_Click"/> <AppBarButton Icon="Save" Label="保存" x:Name="save" Click="save_Click"/> </StackPanel> </Grid> </DataTemplate> </ItemsControl.ItemTemplate> </ItemsControl> <!--进度信息面板,在网络请求的过程显示,请问完成之后隐藏--> <StackPanel VerticalAlignment="Center" x:Name="tips"> <!--进度条--> <ProgressBar x:Name="progress" ></ProgressBar> <!--进度信息显示--> <TextBlock Text="" x:Name="info" HorizontalAlignment="Center" FontSize="30" TextWrapping="Wrap"></TextBlock> </StackPanel>
DayPicturesPage.xaml.cs文件主要代码 ------------------------------------------------------------------------------------------------------------------ public sealed partial class DayPicturesPage : Page { public DayPicturesPage() { this.InitializeComponent(); } // 进入页面即开始加载网络的壁纸图片和信息 protected override void OnNavigatedTo(NavigationEventArgs e) { if (e.Parameter != null && e.Parameter is int) { // 订阅进度事件的处理程序 WallpapersService.Current.GetOneDayWallpapersProgressEvent += OnOneDayWallpapersProgressEvent; // 调用壁纸请求服务类来获取壁纸信息 WallpapersService.Current.GetOneDayWallpapers((int)e.Parameter); } } // 离开当前的页面则移除订阅的进度事件 protected override void OnNavigatedFrom(NavigationEventArgs e) { WallpapersService.Current.GetOneDayWallpapersProgressEvent -= OnOneDayWallpapersProgressEvent; base.OnNavigatedFrom(e); } // 进度事件的处理程序 private async void OnOneDayWallpapersProgressEvent(object sender, ProgressEventArgs e) { await this.Dispatcher.RunAsync(Windows.UI.Core.CoreDispatcherPriority.Normal, () => { if(e.IsException) { // 如果发生异常则显示异常信息 info.Text = "获取图片异常:"+e.ExceptionInfo; } else { // 正常返回设置进度条的值 progress.Value = e.ProgressValue; // 进度完整 if(e.Complete) { // 把显示进度的面板隐藏 tips.Visibility = Visibility.Collapsed; // 把壁纸信息绑定到列表中 pictureList.ItemsSource = e.Pictures; Debug.WriteLine("e.Pictures.Count:" + e.Pictures.Count); } } }); } // 查看按钮的事件处理程序 private async void view_Click(object sender, RoutedEventArgs e) { PictureInfo pictureInfo = (sender as AppBarButton).DataContext as PictureInfo; // 在浏览器打开壁纸 await Launcher.LaunchUriAsync(pictureInfo.imageUri); } // 保存按钮的事件处理程序 private async void save_Click(object sender, RoutedEventArgs e) { PictureInfo pictureInfo = (sender as AppBarButton).DataContext as PictureInfo; List<Byte> allBytes = new List<byte>(); // 把壁纸的图片文件保存到当前的应用文件里面 using (var response = await HttpWebRequest.Create(pictureInfo.imageUri).GetResponseAsync()) { using (Stream responseStream = response.GetResponseStream()) { byte[] buffer = new byte[4000]; int bytesRead = 0; while ((bytesRead = await responseStream.ReadAsync(buffer, 0, 4000)) > 0) { allBytes.AddRange(buffer.Take(bytesRead)); } } } var file = await ApplicationData.Current.LocalFolder.CreateFileAsync( "bingPicture"+DateTime.Now.Ticks+".jpg", CreationCollisionOption.ReplaceExisting); await FileIO.WriteBytesAsync(file, allBytes.ToArray()); } }