• 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("
    ", ""),
    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

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

  • 相关阅读:
    Java代码检测工具
    java编程工具
    100-days: The one day
    前端书籍整理
    vue 学习笔记(二)
    从零开始写一个npm包及上传
    Vue Baidu Map 插件的使用
    对数组对象进行过滤
    使用css方法使footer保持在页面的最底部
    判断是第一次打开界面?还是刷新
  • 原文地址:https://www.cnblogs.com/zskbll/p/3836500.html
Copyright © 2020-2023  润新知