自從wp8面世,一直都想做wp8開發,苦於各種原因(主要是.net基礎不好),一直沒有學成。六月底換了新工作, 有幸進入一家wp應用的公司,也開始我正式學習wp開發之路。
拽點廢話,類似於小說的背景,各位看官請勿吐槽。
首先,本人使用使用諾基亞將近一年了,每天有個習慣,就是稍微閑着點,就經常逛逛博客園,可以說我的零碎時間,讓博客園占去了一大半。但應用商店里下載的各種博客園總覺得不是太好,也說不上來哪里不好。現在機會來了,在學習的過程中,一步步打造自己的專屬博客園。
原理很簡單,就是通過http請求,抓取博客園的頁面,文章列表用ListBox顯示,點擊后,將地址傳到另一個頁面,進行詳細內容的抓取,顯示在WebBrowser中。中間用到了HtmlAgilityPack插件,這個插件也是剛剛接觸,上次在我的博文《使用HttpWebRequest和HtmlAgilityPack抓取網頁(拒絕亂碼,拒絕正則表達式)》中提到過,后來被園友們一通吐槽,給我推薦了Jumony(地址),這個比HtmlAgilityPack牛逼多了,瞬間把HtmlAgilityPack拋棄了。苦逼的是,但我開始動手做的時候,發現Jumony竟然不能在wp8中使用,我又瞬間石化了,然后又把HtmlAgilityPack撿了起來。
下面上幫助類代碼,及時Post請求,和Get請求,暫時只用到了Get請求, post請求是准備做用戶中心的時候使用的。
public class HttpHelper
{
public static CookieContainer cc = new CookieContainer();
/// <summary>
/// 發起異步post的請求,可用於模擬登陸。
/// </summary>
/// <param name="url">post的地址</param>
/// <param name="poststr">參數字符串</param>
/// <param name="action">回調函數</param>
public static void HttpPostAsync(string url, string poststr, Action<string> action)
{
HttpWebRequest req = (HttpWebRequest)HttpWebRequest.Create(url);
req.Method = "POST";
req.CookieContainer = cc;
req.Accept = "application/json, text/javascript, */*; q=0.01";
req.ContentType = "application/x-www-form-urlencoded";
req.AllowAutoRedirect = false;
req.BeginGetRequestStream(new AsyncCallback(asy =>
{
HttpWebRequest req1 = (HttpWebRequest)asy.AsyncState;
using (Stream stream1 = req1.EndGetRequestStream(asy))
{
byte[] postData = Encoding.UTF8.GetBytes(poststr);
stream1.Write(postData, 0, postData.Length);
}
//開始異步接收數據
req1.BeginGetResponse(new AsyncCallback(res =>
{
HttpWebRequest req2 = (HttpWebRequest)res.AsyncState;
//結束返回的請求數據
HttpWebResponse response = (HttpWebResponse)req2.EndGetResponse(res);
using (Stream stream2 = response.GetResponseStream())
{
StreamReader reader = new StreamReader(stream2);
string resStr = reader.ReadToEnd();
action(resStr);
}
}), req1);
}), req);
}
public static void HttpGetAsync(string url,Action<string> action)
{
if (Utils.GetNetStates()=="None")
{
action("no network");//無網絡
return;
}
HttpWebRequest req1 = (HttpWebRequest)HttpWebRequest.Create(url);
req1.CookieContainer = cc;
//開始異步接收數據
req1.BeginGetResponse(new AsyncCallback(res =>
{
try
{
HttpWebRequest req2 = (HttpWebRequest)res.AsyncState;
//結束返回的請求數據
HttpWebResponse response = (HttpWebResponse)req2.EndGetResponse(res);
using (Stream stream2 = response.GetResponseStream())
{
StreamReader reader = new StreamReader(stream2);
string resStr = reader.ReadToEnd();
action(resStr);
}
}
catch (Exception)
{
action("network anomaly");//請求失敗,網絡異常
}
}), req1);
}
}
下面是判斷手機的網絡狀態的代碼
public class Utils
{
#region wp聯網狀態
/// <summary>
/// 獲取設備當前的網絡狀態
/// </summary>
/// <returns></returns>
public static string GetNetStates()
{
var info = Microsoft.Phone.Net.NetworkInformation.NetworkInterface.NetworkInterfaceType;
switch (info)
{
case Microsoft.Phone.Net.NetworkInformation.NetworkInterfaceType.Ethernet: return "Ethernet";
case Microsoft.Phone.Net.NetworkInformation.NetworkInterfaceType.MobileBroadbandCdma: return "CDMA";
case Microsoft.Phone.Net.NetworkInformation.NetworkInterfaceType.MobileBroadbandGsm: return "GSM";
case Microsoft.Phone.Net.NetworkInformation.NetworkInterfaceType.None: return "None";
case Microsoft.Phone.Net.NetworkInformation.NetworkInterfaceType.Wireless80211: return "Wifi";
default: return "None";
}
}
#endregion
}
第一次做wp應用,有點小丑,先上圖吧。





頁面首頁用Panorama布局,有四個欄目,分別是 首頁,熱門分類,知識庫,(個人中心)暫時沒做,后期加上。
首先說首頁,主要是抓取博客園首頁文章列表,然后分析頁面后,加載到ListBox中。這里有個我所認為的難點:到頁面滾動到底部的時候自動加載下一頁。找了好多資料,最后還是找到了,先把主要代碼貼出來
1 #region 獲取ListBox的滾動條,實現下拉自動刷新 2 private void RegisterScrollListBoxEvent() 3 { 4 List<ScrollBar> controlScrollBarList = UIHelper.GetVisualChildCollection<ScrollBar>(this.artList); 5 if (controlScrollBarList == null) 6 return; 7 8 foreach (ScrollBar queryBar in controlScrollBarList) 9 { 10 if (queryBar.Orientation == System.Windows.Controls.Orientation.Vertical) 11 queryBar.ValueChanged += queryBar_ValueChanged; 12 } 13 } 14 15 async void queryBar_ValueChanged(object sender, RoutedPropertyChangedEventArgs<double> e) 16 { 17 if (probar.Visibility == Visibility.Visible) 18 { 19 return; 20 } 21 ScrollBar scrollBar = (ScrollBar)sender; 22 object valueObj = scrollBar.GetValue(ScrollBar.ValueProperty); 23 object maxObj = scrollBar.GetValue(ScrollBar.MaximumProperty); 24 object minObj = scrollBar.GetValue(ScrollBar.MinimumProperty); 25 26 if (valueObj != null && maxObj != null) 27 { 28 double value = (double)valueObj; 29 double max = (double)maxObj; 30 double min = (double)minObj; 31 32 if (value >= Math.Floor(max)) 33 { 34 probar.Visibility = System.Windows.Visibility.Visible; 35 page += 1; 36 await GetHomeArtcle(page); 37 } 38 39 if (Math.Floor(value) <= min) 40 { 41 42 } 43 } 44 } 45 #endregion
只需在頁面加載的時候注冊下事件就OK了。
下面是抓取的方法代碼
1 async Task GetHomeArtcle(int pageindex) 2 { 3 string url = "http://www.cnblogs.com/"; 4 if (pageindex > 1) 5 { 6 url = string.Format("http://www.cnblogs.com/sitehome/p/{0}", pageindex); 7 } 8 await Task.Run(() => 9 { 10 HttpHelper.HttpGetAsync(url, html => 11 { 12 if (html == "no network") 13 { 14 Dispatcher.BeginInvoke(() => 15 { 16 MessageBox.Show("sorry,貌似您沒有聯網哦"); 17 }); 18 return; 19 } 20 if (html == "network anomaly") 21 { 22 Dispatcher.BeginInvoke(() => 23 { 24 MessageBox.Show("sorry,您的網絡貌似有異常,請檢查后再試吧!"); 25 }); 26 return; 27 } 28 HtmlDocument doc = new HtmlDocument(); 29 doc.LoadHtml(html); 30 var artlist = doc.DocumentNode.SelectNodes("//div[@class='post_item']"); 31 List<art> art1 = new List<art>(); 32 foreach (var item in artlist) 33 { 34 HtmlDocument adoc = new HtmlDocument(); 35 adoc.LoadHtml(item.InnerHtml); 36 var html_a = adoc.DocumentNode.SelectSingleNode("//a[@class='titlelnk']"); 37 var html_content = adoc.DocumentNode.SelectSingleNode("//p[@class='post_item_summary']"); 38 var comment = adoc.DocumentNode.SelectNodes("//a[@class='gray']"); 39 html_content.RemoveChild(html_content.FirstChild, true); 40 var foot = adoc.DocumentNode.SelectSingleNode("//div[@class='post_item_foot']"); 41 foot.RemoveAll(); 42 art1.Add(new art 43 { 44 Title = html_a.InnerText, 45 Content = html_content.InnerText, 46 Comment = comment.First().InnerText.Replace("\r\n", ""), 47 Read = comment.Last().InnerText, 48 AddTime = foot.InnerText, 49 Link = html_a.Attributes["href"].Value 50 }); 51 //Response.Write(string.Format("標題為:{0},鏈接為:{1}<br>", html_a.InnerText, html_a.Attributes["href"].Value)); 52 } 53 Dispatcher.BeginInvoke(() => 54 { 55 for (int i = 0; i < artlist.Count; i++) 56 { 57 artsource.Add(art1[i]); 58 } 59 60 probar.Visibility = System.Windows.Visibility.Collapsed; 61 }); 62 }); 63 }); 64 }
另外,定義了抓頁面所需要的類
1 public class art 2 { 3 private string title; 4 public string Title 5 { 6 get { return title; } 7 set 8 { 9 title = value; 10 NotifyPropertyChanged("Title"); 11 } 12 } 13 private string content; 14 public string Content 15 { 16 get { return content; } 17 set 18 { 19 content = value; 20 NotifyPropertyChanged("Content"); 21 } 22 } 23 private string comment; 24 /// <summary> 25 /// 評論 26 /// </summary> 27 public string Comment 28 { 29 get { return comment; } 30 set 31 { 32 comment = value; 33 NotifyPropertyChanged("Comment"); 34 } 35 } 36 private string read; 37 /// <summary> 38 /// 閱讀 39 /// </summary> 40 public string Read 41 { 42 get { return read; } 43 set 44 { 45 read = value; 46 NotifyPropertyChanged("Read"); 47 } 48 } 49 50 /// <summary> 51 /// 發布時間 52 /// </summary> 53 public string AddTime { get; set; } 54 public string Link { get; set; } 55 public event PropertyChangedEventHandler PropertyChanged; 56 private void NotifyPropertyChanged(string info) 57 { 58 if (PropertyChanged != null) 59 { 60 PropertyChanged(this, new PropertyChangedEventArgs(info)); 61 } 62 } 63 } 64 65 public class ArtCollection : ObservableCollection<art> 66 { 67 public ArtCollection() 68 : base() 69 { 70 71 } 72 }
這些就是首頁欄目的主要代碼了,下面會把源碼貼出來,所以就不一一貼在這里了。
綁定后列表后,就要綁定Tap事件了,個人覺得這個事件可以理解為web開發中的單擊事件。代碼如下:
1 private void Border_Tap(object sender, System.Windows.Input.GestureEventArgs e) 2 { 3 try 4 { 5 string link = ((Border)sender).Tag.ToString();//獲取頁面鏈接 6 NavigationService.Navigate(new Uri(string.Format("/show.xaml?linkurl={0}", link), UriKind.Relative)); 7 } 8 catch (Exception) 9 { 10 11 } 12 }
其次說熱門分類
熱門分類沒有做抓取,只是手工將博客園中的一級分類寫出來,把單擊事件里傳遞對應的地址,到列表頁面。
列表頁面是采用的Pivot布局,將分類依次排列,當從熱門分類中跳轉過來時,根據參數進行判斷,加載對應的分類列表
1 protected override void OnNavigatedTo(NavigationEventArgs e) 2 { 3 base.OnNavigatedTo(e); 4 if (e.NavigationMode == NavigationMode.New) 5 { 6 IDictionary<string, string> queryString = this.NavigationContext.QueryString; 7 var selectedItem = pivot.Items.First(item => 8 { 9 PivotItem temp = (PivotItem)item; 10 return temp.Tag.ToString() == queryString["type"]; 11 });//查找對應的PivotItem 12 pivot.SelectedItem = selectedItem;//設置為當前Item 13 // await GetHomeArtcle(pagedb, queryString["type"]); 14 } 15 }
當Pivot進行切換的時候,觸發SelectionChanged事件,在事件中,首先判斷當前的Item是否已經加載數據,如果尚未加載,就加載,如果已經加載了, 就不做操作了。
1 private async void Pivot_SelectionChanged(object sender, SelectionChangedEventArgs e) 2 { 3 PivotItem item = (PivotItem)pivot.SelectedItem; 4 ListBox tempList = (ListBox)item.Content; 5 if (tempList.Items.Count==0) 6 { 7 probar.Visibility = System.Windows.Visibility.Visible; 8 await GetHomeArtcle(1, item.Tag.ToString()); 9 // tempList.Tag = (page + 1).ToString(); 10 } 11 12 }
忽然覺得代碼太多了,先寫到這吧。 下班回家啦。
博客園的編輯器怎么沒找到上傳附件的呢???
把源碼上傳到了雲盤里, 下面是地址http://yunpan.cn/Qh4xxMwEHmutb 提取碼 d3b4(已失效,360雲盤分享只能被下載5次, 我無力吐槽了啊)
百度網盤分享鏈接:鏈接: http://pan.baidu.com/s/1bnjayNP 密碼: ijch
微雲分享鏈接 鏈接:http://url.cn/Pqa3vH (密碼:7Gim)
已經上傳到應用商店了,需要吐槽的點這里:http://www.windowsphone.com/zh-cn/store/app/%E5%8D%9A%E5%AE%A2%E5%9B%AD%E5%AE%A2%E6%88%B7%E7%AB%AF/4ceda3f2-bef6-4231-a6bb-b8d9a0cc7b87
未打完,也要收工。一是東西太多了, 本來打算分幾次寫的,后來覺得太麻煩了。二是,本人表達能力有限,看不明白的直接看源碼吧,源碼有注釋哦。
