原文:Windows 8.1 store app 开发笔记
零、简介
一切都要从博彦之星比赛说起。今年比赛的主题是使用Bing API(主要提到的有Bing Map API、Bing Translate API和Bing AD API)设计移动应用(Windows store app和Windows phone app)。从7月初开始设计到现在,应用的功能已经基本完成,就差美工来给界面优化一下。下面是我设计的应用的功能和实现的方法,
一、BING MAP API
作为一个以Bing Map API为主的应用,主要有以下的功能:
1、定位:
1 private LocationRange range = null; 2 private CancellationTokenSource cts = null; 3 private Geolocator geolocator = null; 4 5 private async void locateButton_Click(object sender, RoutedEventArgs e) 6 { 7 // 根据定位按钮的标签判定是“定位”或“取消定位” 8 if (locateButton.Label == "定位") 9 { 10 locateButton.Label = "取消定位"; 11 try 12 { 13 // 获得cancellation token 14 cts = new CancellationTokenSource(); 15 CancellationToken token = cts.Token; 16 this.infoBlock.Text = "正在定位"; 17 18 // 获得位置 19 Geoposition pos = await geolocator.GetGeopositionAsync().AsTask(token); 20 this.infoBlock.Text = "定位成功"; 21 // App.location是在App.xaml.cs中用于保存定位位置的全局变量 22 App.location = new Location(pos.Coordinate.Point.Position.Latitude, pos.Coordinate.Point.Position.Longitude); 23 24 // 设置默认地图缩放等级 25 double zoomLevel = 13.0f; 26 // range是一个自定义控件,用一个大圆来涵盖定位区域 27 MapLayer.SetPosition(range, App.location); 28 rangeLayer.Children.Add(range); 29 zoomLevel = 15.0f; 30 31 // 设置地图视野到给定的位置和缩放等级 32 map.SetView(App.location, zoomLevel); 33 } 34 catch (System.UnauthorizedAccessException) 35 { 36 this.infoBlock.Text = "定位请求被拒绝"; 37 } 38 catch (TaskCanceledException) 39 { 40 this.infoBlock.Text = "定位被取消"; 41 } 42 catch (System.Exception) 43 { 44 this.infoBlock.Text = "暂时无法获得您的位置"; 45 } 46 finally 47 { 48 cts = null; 49 } 50 // 重置按钮 51 locateButton.Label = "定位"; 52 //locateButton.Icon = ; 53 } 54 else 55 { 56 // 取消定位 57 if (cts != null) 58 { 59 cts.Cancel(); 60 cts = null; 61 } 62 // 重置按钮 63 locateButton.Label = "定位"; 64 //locateButton.Icon = ; 65 } 66 }
locateButton_Click
2、添加图钉:
1 private async void AddPinButton_Click(object sender, RoutedEventArgs e) 2 { 3 // 实例化一个图钉类(这个图钉类是我自定义的) 4 pin = new MapediaPin(map); 5 // 为图钉添加Drag和Tap触发方法(当然还有Hold等) 6 pin.drag += pin_Dragged; 7 pin.Tapped += pin_Tapped; 8 //将图钉显示到地图的中央 9 MapLayer.SetPosition(pin, map.Center); 10 pinLayer.Children.Add(pin); 11 }
AddPinButton_Click
3、拖动图钉:
1 private bool isDragging = false; // pin正在拖拽标志 2 3 // 当pin被拖动时激活 4 public Action<Location> drag; 5 // 当pin开始被拖动时激活 6 public Action<Location> dragStart; 7 // 当pin停止拖动时激活 8 public Action<Location> dragEnd; 9 // 当pin被按下时,得到被按下pin的ID,判断是否可拖动,执行操作 10 protected override void OnPointerPressed(PointerRoutedEventArgs e) 11 { 12 base.OnPointerPressed(e); 13 14 // 如果可以被拖动则开始拖动 15 if (draggable) 16 { 17 if (map != null) 18 { 19 center = map.Center; 20 // 为map的下列方面重写 21 map.ViewChanged += map_ViewChanged; 22 map.PointerReleasedOverride += map_PointerReleased; 23 map.PointerMovedOverride += map_PointerMoved; 24 } 25 // 得到当前鼠标位置 26 var pointerPosition = e.GetCurrentPoint(map); 27 Location location = null; 28 // 开始拖动 29 if (dragStart != null) 30 { 31 dragStart(location); 32 } 33 34 this.isDragging = true; 35 } 36 } 37 38 // 当pin被移动时 39 private void map_PointerMoved(object sender, PointerRoutedEventArgs e) 40 { 41 // 检查是否正在被拖动 42 if (this.isDragging) 43 { 44 // 随着鼠标移动图标 45 var pointerPosition = e.GetCurrentPoint(map); 46 47 Location location = null; 48 49 if (map.TryPixelToLocation(pointerPosition.Position, out location)) 50 { 51 // 将图钉(this)移到到当前鼠标的位置(location) 52 MapLayer.SetPosition(this, location); 53 } 54 if (drag != null) 55 { 56 drag(location); 57 } 58 } 59 } 60 61 // 当pin被松开时 62 private void map_PointerReleased(object sender, PointerRoutedEventArgs e) 63 { 64 if (this.isDragging) 65 { 66 if (map != null) 67 { 68 map.ViewChanged -= map_ViewChanged; 69 map.PointerReleasedOverride -= map_PointerReleased; 70 map.PointerMovedOverride -= map_PointerMoved; 71 } 72 73 var pointerPosition = e.GetCurrentPoint(map); 74 Location location; 75 map.TryPixelToLocation(pointerPosition.Position, out location); 76 // 得到最终的经纬度 77 latitude = location.Latitude; 78 longitude = location.Longitude; 79 80 location = null; 81 82 if (dragEnd != null) 83 { 84 dragEnd(location); 85 } 86 87 this.isDragging = false; 88 this.draggable = false; 89 } 90 }
PinDrag
二、BING TRANSLATE API
用的这个API的地方,是在对图钉上面的信息进行翻译的时候:
翻译:
1 private HttpClient client = null; // 用于通信的HTTP客户端 2 3 private async void translateButton_Click(object sender, RoutedEventArgs e) 4 { 5 // 根据translateButton的标签判定是“翻译”或“取消翻译” 6 if (translateButton.Label == "翻译") 7 { 8 // 进行网络检查(方法见 五、UTILITIES) 9 if (!App.CheckNetwork()) 10 { 11 //this.infoBlock.Text = "无网络连接,请检查网络"; 12 } 13 else 14 { 15 try 16 { 17 this.infoBlock.Text = "正在检查网络连接..."; 18 string clientID = "你的clientID"; 19 string clientSecret = "你的clientSecret"; 20 //AzureDataMarket可以从网上找到,是一个已经写好的用于在Azure进行验证的类 21 var _Authentication = new AzureDataMarket(clientID, clientSecret); 22 AzureDataMarket.Token m_Token = await _Authentication.GetTokenAsync(); 23 string auth = m_Token.Header; 24 //实例化该类,以便于后面发送Http请求获取网络数据 25 client = new HttpClient(); 26 //设置读取响应内容时缓冲区的最大字节数 27 client.MaxResponseContentBufferSize = 256000; 28 //设置请求头部 29 client.DefaultRequestHeaders.Add("Authorization", auth); 30 } 31 catch 32 { 33 //this.infoBlock.Text = "连接到服务器失败"; 34 } 35 } 36 } 37 else 38 { 39 this.translateButton.Label = "翻译"; 40 } 41 } 42 43 // 将descriptionBlock中的内容翻译成language所表示的语言 44 private async void translate(String language) 45 { 46 //language可表示的语言:ar bg ca zh-CHS zh-CHT cs da nl en et fi fr de el ht he hi mww hu id it ja tlh tlh-Qaak ko lv lt ms mt no fa pl pt ro ru sk sl es sv th tr uk ur vi 47 string url = "http://api.microsofttranslator.com/v2/Http.svc/Translate?text=" + System.Net.WebUtility.UrlEncode(this.descriptionBlock.Text) + "&to=" + language; 48 //try 49 { 50 string strTranslated = await client.GetStringAsync(url); 51 XDocument xTranslation = XDocument.Parse(strTranslated); 52 string strTransText = xTranslation.Root.FirstNode.ToString(); 53 this.titleTranslateBlock.Text = strTransText; 54 55 this.translateButton.Label = "取消翻译"; 56 } 57 //catch (Exception ex) 58 { 59 // this.infoBlock.Text = "不能访问Bing Translate API,错误原因:" + ex.Message.ToString(); 60 } 61 62 } 63 // 当englishButton按下时,翻译成英语 64 private void englishButton_Click(object sender, RoutedEventArgs e) 65 { 66 translate("en"); 67 } 68 // 当simChineseButton按下时,翻译成简体中文 69 private void simChineseButton_Click(object sender, RoutedEventArgs e) 70 { 71 translate("zh-CHS"); 72 }
translateButton_Click
三、BING AD API
待应用......
四、LIVE SDK
1、登入和登出live帐号:
1 // live SDK使用范围:登入、获得用户基本信息、使用onedrive上传功能 2 private readonly string[] scopes = new string[] { 3 "wl.signin", "wl.basic", "wl.photos", "wl.skydrive", "wl.skydrive_update"}; 4 private LiveAuthClient authClient = null; 5 private LiveConnectClient liveClient = null; 6 7 private void loginButton_Click(object sender, RoutedEventArgs e) 8 { 9 // 根据loginButton判定是“登入”或“登出” 10 if (this.loginButton.Label == "登出") 11 { 12 this.authClient.Logout(); 13 this.loginButton.Label = "Live帐号登入"; 14 this.loginBlock.Text = "未登入"; 15 } 16 else 17 { 18 login(); 19 } 20 } 21 22 private async void login() 23 { 24 try 25 { 26 this.loginButton.IsEnabled = false; 27 28 this.authClient = new LiveAuthClient(); 29 this.loginBlock.Text = "登入中"; 30 // 登入时显示进度的进度环 31 this.loginProgress.IsActive = true; 32 // 检验网络状态 33 if (App.CheckNetwork()) 34 { 35 // 得到登入结果 36 LiveLoginResult loginResult = await this.authClient.LoginAsync(this.scopes); 37 if (loginResult.Status == LiveConnectSessionStatus.Connected) 38 { 39 App.Session = loginResult.Session; 40 this.liveClient = new LiveConnectClient(loginResult.Session); 41 LiveOperationResult operationResult = await this.liveClient.GetAsync("me"); 42 43 // 当用户登入后 44 dynamic meResult = operationResult.Result; 45 this.loginButton.Label = "登出"; 46 if (meResult.first_name != null && meResult.last_name != null) 47 { 48 //显示用户的登录信息 49 this.loginBlock.Text = "欢迎! " + meResult.first_name + " " + meResult.last_name; 50 } 51 else 52 { 53 this.loginBlock.Text = "欢迎! "; 54 } 55 } 56 else 57 { 58 this.loginBlock.Text = "未登入"; 59 } 60 } 61 else 62 { 63 this.infoBlock.Text = "无网络连接,请检查网络"; 64 this.loginBlock.Text = "未登入"; 65 } 66 } 67 catch (LiveAuthException) 68 { 69 this.infoBlock.Text = "登入请求被拒绝"; 70 } 71 finally 72 { 73 // CanLogout为false的情况:应用使用windows已登入的账号时 74 if (this.loginButton.Label == "登出" && this.authClient != null && !this.authClient.CanLogout) 75 { 76 this.loginButton.IsEnabled = false; 77 } 78 else 79 { 80 this.loginButton.IsEnabled = true; 81 } 82 this.loginProgress.IsActive = false; 83 } 84 }
loginButton_Click
2、上传图片至OneDrive:
1 private async void syncButton_Click(object sender, RoutedEventArgs e) 2 { 3 // App.Session是用来记录当前登入账户的一次会话,登入live帐号之后产生 4 if (App.Session != null) 5 { 6 // 得到本地的photo文件夹 7 IReadOnlyList<StorageFolder> folders = await App.photosFolder.GetFoldersAsync(); 8 // 在Onedrive中新建一个文件夹,名字为“新建文件夹” 9 string skyDriveFolder = await CreateDirectoryAsync("新建文件夹", "me/skydrive"); 10 // 对photo文件夹中的子文件夹进行遍历 11 foreach (StorageFolder folder in folders) 12 { 13 // 获得子文件夹下的所有文件 14 IReadOnlyList<StorageFile> files = await folder.GetFilesAsync(); 15 foreach (StorageFile file in files) 16 { 17 // 将文件上传 18 LiveOperationResult result = await liveClient.BackgroundUploadAsync(skyDriveFolder, file.DisplayName + ".jpg", file, OverwriteOption.Overwrite); 19 } 20 } 21 } 22 } 23 24 private async Task<string> CreateDirectoryAsync(string folderName, string parentFolder) 25 { 26 string folderId = null; 27 // 查询OneDrive中是否含有folderName文件夹 28 var queryFolder = parentFolder + "/files?filter=folders,albums"; 29 var opResult = await liveClient.GetAsync(queryFolder); 30 dynamic result = opResult.Result; 31 32 foreach (dynamic folder in result.data) 33 { 34 // 如果存在这个文件夹,则返回文件夹名 35 if (folder.name == folderName) 36 { 37 folderId = folder.id; 38 break; 39 } 40 } 41 42 if (folderId == null) 43 { 44 // 不存在则创建 45 var folderData = new Dictionary<string, object>(); 46 folderData.Add("name", folderName); 47 opResult = await liveClient.PostAsync(parentFolder, folderData); 48 result = opResult.Result; 49 folderId = result.id; 50 } 51 52 return folderId; 53 }
syncButton_Click
五、微博 SDK
1、连接微博帐号:
1 private void shareButton_Click(object sender, RoutedEventArgs e) 2 { 3 // 检查网络状态 4 if (App.CheckNetwork()) 5 { 6 // 检查该应用是否已和微博连接 7 if (App.oauthClient.IsAuthorized == false) 8 { 9 App.oauthClient.LoginCallback += (isSucces, err, response) => 10 { 11 if (isSucces) // 如果成功 12 { 13 // TODO: deal the OAuth result. 14 } 15 else 16 { 17 this.titleBox.Text = err.errMessage; 18 } 19 }; 20 App.oauthClient.BeginOAuth(); // 开始验证 21 } 22 } 23 else 24 { 25 // no internet. 26 } 27 if (App.oauthClient.IsAuthorized == true) // 验证成功,开始分享 28 { 29 // TODO SHARE 30 } 31 else 32 { 33 this.titleBox.Text = "验证失败"; 34 } 35 }
shareButton_Click
2、发布(带图片)微博
1 private async void shareWithPhoto(String path) 2 { 3 var engine = new SdkNetEngine(); 4 // 微博sdk提供的方法,实例化一个cmd类 5 ISdkCmdBase cmdBase = new CmdPos MsgWithPic() 6 { 7 // 发布的文本消息 8 Status = shareTextBox.Text, 9 // 发布的图片绝对路径 10 PicPath = path 11 }; 12 // 发布微博 13 var result = await engine.RequestCmd(SdkRequestType.POST_MESSAGE_PIC, cmdBase); 14 15 if (result.errCode == SdkErrCode.SUCCESS) 16 { 17 // 发布成功 18 } 19 // 发布失败 20 else 21 { 22 // TODO: deal the error. 23 // 失败的状态码 24 switch (result.errCode) 25 { 26 case SdkErrCode.NET_UNUSUAL: this.descriptionBox.Text = "NET_UNUSUAL"; break; 27 case SdkErrCode.SERVER_ERR: this.descriptionBox.Text = "SERVER_ERR"; break; 28 case SdkErrCode.TIMEOUT: this.descriptionBox.Text = "TIMEOUT"; break; 29 case SdkErrCode.USER_CANCEL: this.descriptionBox.Text = "USER_CANCEL"; break; 30 case SdkErrCode.XPARAM_ERR: this.descriptionBox.Text = "XPARAM_ERR"; break; 31 } 32 this.descriptionBox.Text = result.specificCode; 33 } 34 }
shareWithPhoto
六、UTILITIES
1、检查网络状态
1 public static Boolean CheckNetwork() 2 { 3 bool isOnline = false; 4 //获得当前的网络连接状态(using Windows.Networking.Connectivity 5 ConnectionProfile connectionProfile = NetworkInformation.GetInternetConnectionProfile(); 6 7 if (connectionProfile == null) 8 { 9 //TODO No net work 10 } 11 else 12 { 13 isOnline = true; 14 } 15 return isOnline; 16 }
CheckNetwork
2、向服务器post请求(服务器端我用的是Servlet+Tomcat+MySQL来接收和处理)
1 private readonly static String url = "请求地址"; 2 3 private static async Task<string> postData(String data) 4 { 5 String result = String.Empty; 6 // 设置字符编码 7 Encoding encoding = Encoding.UTF8; 8 // 将请求的数据转换为字节流 9 Byte[] bytes = encoding.GetBytes(data); 10 // 实例化请求对象,有多种请求对象可以使用 11 WebRequest req = WebRequest.Create(url); 12 // 请求方式和类型 13 req.Method = "POST"; 14 req.ContentType = "application/x-www-form-urlencoded"; 15 using (Stream outputStream = await req.GetRequestStreamAsync()) 16 { 17 // 发送字节流信息 18 outputStream.Write(bytes, 0, bytes.Length); 19 } 20 using (WebResponse webResponse = await req.GetResponseAsync()) 21 { 22 // 得到响应信息 23 StreamReader sr = new StreamReader(webResponse.GetResponseStream()); 24 result = sr.ReadToEnd(); 25 } 26 27 return result; 28 }
View Code
七、总结
第一次上手C#和xaml,而且是在这略浩大的工程中(对我而言),在开发过程中不免会遇到各种困难,当然也积累了各种经验。经历了多次Visual Studio 2013的安装(从Update1到Update3,从vs安装时写临时文件夹拒绝访问到designer安装失败),各种神奇的、离奇的失败最后都在谷歌和MSDN上找到了答案,也锻炼了我查找和阅读答案的能力。希望以上我的分享能给刚上手win8 app开发的同行们减轻一些查api(当然api也是要仔细看的)查谷歌的负担,共同进步。