wp新人打造专属windows phone博客园客户端

 

自从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
获取ListBox的滚动条,实现下拉自动刷新

只需在页面加载的时候注册下事件就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     }
View Code

这些就是首页栏目的主要代码了,下面会把源码贴出来,所以就不一一贴在这里了。

绑定后列表后,就要绑定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         }
Tap事件

 

其次说热门分类

热门分类没有做抓取,只是手工将博客园中的一级分类写出来,把单击事件里传递对应的地址,到列表页面。

列表页面是采用的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         }
选择对应的Item

当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         }
SelectionChanged事件

 

忽然觉得代码太多了,先写到这吧。 下班回家啦。

 

博客园的编辑器怎么没找到上传附件的呢???

 

把源码上传到了云盘里, 下面是地址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

 

未打完,也要收工。一是东西太多了, 本来打算分几次写的,后来觉得太麻烦了。二是,本人表达能力有限,看不明白的直接看源码吧,源码有注释哦。

posted @ 2014-07-10 19:47  billsking  阅读(4017)  评论(25编辑  收藏  举报