Win8的RSS阅读器

   学习Win8开发也有一段时间,最初是看传智播客老杨的Win8视频教程,才对Win8有了一定开发基础,然后参考着微软官方的API及源码学习。在这里与大家分享我做的一个小小Demo供大家参考。

  本Demo只是现实了Rss的基本阅读功能,当然还可以扩展很多的功能,让它变得更完美些。在这里先与大家分享这基本的现实功能,我也一会继续优化。。。

  在项目中我觉得最核心的知识点:

  1、使用Win8提供的类库下载并解析Xml文件。

  2、格式化文本展示新闻内容。

  先上效果图 ,有图有真相。然后再分享实现过程,在结尾处会提供参考源代码。

 

   下面,我们一起实现步骤:

  1、创建Win8商店应用程序(不要说不会啊~~  )。

  2、随便找个RSS订阅的地方,了解了解结构,大家从贴图上也看出来了,我这里用的就是博客园的最新新闻,提供个地址:http://feed.cnblogs.com/news/rss

  3、有了第2步的基础之后,我们来创建一个类:FeedItem.cs ,属性字段看着办就行~~,开发Win8程序的实体类要记得实现一个这个接口(INotifyPropertyChanged),至于是为什么,直接不解释,不会的去看视频。贴上源代码:

View Code
 /// <summary>
    /// 新闻实体类
    /// </summary>
    public class FeedItem : INotifyPropertyChanged
    {

        private int _id;
        public int Id
        {
            get { return _id; }
            set
            {
                _id = value;
                OnPropertyChanged("Id");
            }
        }

        private string _title;
        public string Title
        {
            get { return _title; }
            set
            {
                _title = value;
                OnPropertyChanged("Title");
            }
        }

        private string _content;
        public string Content
        {
            get { return _content; }
            set
            {
                _content = value;
                OnPropertyChanged("Content");
            }
        }
        private DateTime _pubDate;
        public DateTime PubDate
        {

            get { return _pubDate; }
            set
            {
                _pubDate = value;
                OnPropertyChanged("PubDate");
            }
        }

        private string _link;
        public string Link
        {
            get { return _link; }
            set { _link = value; OnPropertyChanged("Link"); }
        }
        protected void OnPropertyChanged(string propertyName)
        {
            if (this.PropertyChanged != null)
            {
                this.PropertyChanged(this, new System.ComponentModel.PropertyChangedEventArgs(propertyName));
            }
        }

        public event PropertyChangedEventHandler PropertyChanged;
    }

  4、添加一个新的空白页:Index.xmal 这个页面是重点,用到了GridView来展示集合中的内容。

  XMAL代码:

View Code
 <Grid Background="Gray">
        <Grid.RowDefinitions>
            <RowDefinition Height="auto"></RowDefinition>
            <RowDefinition></RowDefinition>
        </Grid.RowDefinitions>
        <StackPanel Margin="20">
            <TextBlock Text="博客园 - 最新新闻" Style="{StaticResource HeaderTextStyle}"></TextBlock>
            <GridView Grid.Row="1" Margin="0 30 0 0" x:Name="ItemGridView" 
            ItemTemplate="{StaticResource StoreFrontTileTemplate}"
            ItemContainerStyle="{StaticResource StoreFrontTileStyle}"
            ItemsPanel="{StaticResource StoreFrontGridItemsPanelTemplate}"
            VerticalAlignment="Top"
            BorderThickness="1" ScrollViewer.VerticalScrollBarVisibility="Auto"
            ScrollViewer.HorizontalScrollBarVisibility="Auto" SelectionMode="Single"
               SelectionChanged="ItemGridView_SelectionChanged_1"
                  />
            <ProgressBar Name="pro1" Grid.Row="1" Background="White" IsIndeterminate="True" />
        </StackPanel>
    </Grid>

  后台代码:

  /// <summary>
    /// 可用于自身或导航至 Frame 内部的空白页。
    /// </summary>
    public sealed partial class Index : Page
    {
        //新闻集合
        ObservableCollection<FeedItem> feedData = null;

        public Index()
        {
            InitializeComponent();
        }

        /// <summary>
        /// 在此页将要在 Frame 中显示时进行调用。
        /// </summary>
        /// <param name="e">描述如何访问此页的事件数据。Parameter
        /// 属性通常用于配置页。</param>
        protected override void OnNavigatedTo(NavigationEventArgs e)
        {
            //加载
            InitItem();
            this.ItemGridView.ItemsSource = feedData;

        }
        /// <summary>
        /// 加载XML
        /// </summary>
        public async void InitItem()
        {
            if (feedData != null)
            {
                return;
            }
            SyndicationClient client = new SyndicationClient();
            Uri feedUri = new Uri("http://feed.cnblogs.com/news/rss");
            feedData = new ObservableCollection<FeedItem>();
            try
            {
                SyndicationFeed feed = await client.RetrieveFeedAsync(feedUri);
                pro1.Visibility = Windows.UI.Xaml.Visibility.Collapsed;

                foreach (SyndicationItem item in feed.Items)
                {
                    FeedItem feedItem = new FeedItem();
                    string id = item.Id.ToString();
                    Regex reg = new Regex(@".(\d+).");
                    Match m = reg.Match(id);
                    string s = m.Groups[1].ToString();
                    feedItem.Id = Convert.ToInt32(s);
                    feedItem.Link = item.Id.ToString();
                    feedItem.Title = item.Title.Text;
                    feedItem.PubDate = item.PublishedDate.DateTime;
                    if (feed.SourceFormat == SyndicationFormat.Atom10)
                    {
                        feedItem.Content = item.Content.Text;
                    }
                    else if (feed.SourceFormat == SyndicationFormat.Rss20)
                    {
                        feedItem.Content = item.Summary.Text;
                    }
                    feedData.Add(feedItem);
                }

            }
            catch (Exception)
            {

            }
        }

        private void ItemGridView_SelectionChanged_1(object sender, SelectionChangedEventArgs e)
        {
            FeedItem feedItem = e.AddedItems[0] as FeedItem;
            //页面跳转
            Frame.Navigate(typeof(Details), feedItem);
        }

    }

  哈哈,完成这四步就可以展示列表信息了。当然要注意的几点:

    (1)、xmal中的GridView几个模版样式我是写在app.xmal中,你必须要在app.xmal中写上模版样式,要不然肯定会报错滴。因为我也不知道要用啥颜色效果,所以整个的页面风格我全部设置为灰色了。自己改改喜欢的样式呗。

  app.xmal中的代码:

View Code
 <Application.Resources>
        <ResourceDictionary>
            <ResourceDictionary.MergedDictionaries>

                <!-- 
                    用于定义平台外观的共同方面的样式
                    Visual Studio 项目和项模板所必需的
                 -->
                
                <ResourceDictionary Source="Common/StandardStyles.xaml"/>
            </ResourceDictionary.MergedDictionaries>
            <Style x:Key="ItemTitleStyle" TargetType="TextBlock">
                <Setter Property="FontFamily" Value="Segoe UI Light"/>
                <Setter Property="FontSize" Value="34"/>
            </Style>

            <Style x:Key="ItemSubtitleStyle" TargetType="TextBlock">
                <Setter Property="FontFamily" Value="Segoe UI Light"/>
                <Setter Property="FontSize" Value="24"/>
            </Style>

            <DataTemplate x:Key="StoreFrontTileTemplate">
                <Grid HorizontalAlignment="Left" Background="DarkGray" >
                    <StackPanel Orientation="Horizontal" Margin="5,5,0,0" >
                        <StackPanel Margin="0,0,0,0" Orientation="Vertical">
                            <TextBlock TextWrapping="Wrap" Foreground="{StaticResource ApplicationForegroundThemeBrush}" Style="{StaticResource ItemTitleStyle}" Width="400" Height="200" VerticalAlignment="Center" Text="{Binding Title}" HorizontalAlignment="Left" FontFamily="Segoe UI" />
                            <TextBlock TextWrapping="Wrap" Foreground="{StaticResource ApplicationForegroundThemeBrush}" Style="{StaticResource ItemSubtitleStyle}" Width="400" Height="100" VerticalAlignment="Center" Text="{Binding PubDate}" HorizontalAlignment="Left"/>
                        </StackPanel>
                    </StackPanel>
                </Grid>
            </DataTemplate>

            <Style x:Key="StoreFrontTileStyle"  TargetType="GridViewItem">
                <Setter Property="FontFamily" Value="Segoe UI" />
                <Setter Property="Height" Value="300" />
                <Setter Property="Width" Value="400" />
                <Setter Property="Padding" Value="0" />
                <Setter Property="Margin" Value="0,0,8,8" />
                <Setter Property="HorizontalContentAlignment" Value="Left" />
                <Setter Property="VerticalContentAlignment" Value="Top" />
                <Setter Property="BorderThickness" Value="0"/>
                <Setter Property="TabNavigation" Value="Local" />
            </Style>

            <Style x:Key="StoreFrontLVTileStyle"  TargetType="ListViewItem">
                <Setter Property="FontFamily" Value="Segoe UI" />
                <Setter Property="Height" Value="80" />
                <Setter Property="Width" Value="292" />
                <Setter Property="Padding" Value="0" />
                <Setter Property="Margin" Value="0,0,8,8" />
                <Setter Property="HorizontalContentAlignment" Value="Left" />
                <Setter Property="VerticalContentAlignment" Value="Top" />
                <Setter Property="BorderThickness" Value="0"/>
                <Setter Property="TabNavigation" Value="Local" />
            </Style>

            <ItemsPanelTemplate x:Key="StoreFrontGridItemsPanelTemplate">
                <WrapGrid MaximumRowsOrColumns="2" VerticalChildrenAlignment="Center"
                      HorizontalChildrenAlignment="Left"/>
            </ItemsPanelTemplate>

        </ResourceDictionary>
    </Application.Resources>

  

    (2)、得使用ObservableCollection集合类型,比List之类的要更先进此。

    (3)、InitItem方法 加载并解析xml方法。要记得引用几个命名空间。

    (4)、ItemGridView_SelectionChanged_1这个当点击之后要执行的事件,因为我们还没有创建详情页,但是你又想看看效果,可以先这里面的代码注释一下,跑跑看,如果能跑起来,那就说明离成功不远了。先休息一会儿,坐久了可不好,顺便喝个水。

  当然如果跑起来有错误,不要着急,看错误在哪,有问题解决就行。。。

  过了N久之后。。。。

  5、好,我们接着来创建一个新的页面:Details.xaml

  先直接贴上代码:

View Code
<Grid Background="Gray">
        <Grid Margin="20">
            <Grid.RowDefinitions>
                <RowDefinition Height="auto"></RowDefinition>
                <RowDefinition></RowDefinition>
            </Grid.RowDefinitions>
            <Grid.ColumnDefinitions>
                <ColumnDefinition Width="auto"></ColumnDefinition>
                <ColumnDefinition></ColumnDefinition>
            </Grid.ColumnDefinitions>

            <Button Grid.Row="0" Grid.Column="0" Content="返回" Style="{StaticResource BackButtonStyle}" Click="Button_Click_1" HorizontalAlignment="Left" Margin="44,35,0,0" VerticalAlignment="Top"/>
            <TextBlock Name="txtTitle" HorizontalAlignment="Center" VerticalAlignment="Bottom" Style="{StaticResource HeaderTextStyle}" Grid.Row="0" Grid.Column="1"></TextBlock>
            <ScrollViewer Grid.Row="1" Grid.ColumnSpan="2" HorizontalScrollBarVisibility="Auto" VerticalScrollBarVisibility="Hidden">
                <StackPanel Name="stPanel" Orientation="Horizontal"></StackPanel>
            </ScrollViewer>
        </Grid>
    </Grid>

  后台代码:

/// <summary>
    /// 可用于自身或导航至 Frame 内部的空白页。
    /// </summary>
    public sealed partial class Details : Page
    {
        public Details()
        {
            this.InitializeComponent();
        }

        /// <summary>
        /// 在此页将要在 Frame 中显示时进行调用。
        /// </summary>
        /// <param name="e">描述如何访问此页的事件数据。Parameter
        /// 属性通常用于配置页。</param>
        protected override void OnNavigatedTo(NavigationEventArgs e)
        {
            if (e.NavigationMode == NavigationMode.New)
            {
                FeedItem f = e.Parameter as FeedItem;
                txtTitle.Text = f.Title;
                //对新闻内容字符串处理   
                string content = HtmlUtilities.ConvertToText(f.Content + " --" + f.PubDate).Replace("本文链接", "");
                ShowTxt(content);
            }
        }

        private void Button_Click_1(object sender, RoutedEventArgs e)
        {
            //返回
            Frame.GoBack();
        }

        const double CT_WIDTH = 400d; //文本块的宽度
        const double CT_HEIGHT = 600d; //文本块的高度
        const double CT_MARGIN = 20d; //文本块的边距

        /// <summary>
        /// 显示新闻 
        /// </summary>
        /// <param name="msg"></param>
        private void ShowTxt(string msg)
        {
            stPanel.Children.Clear();

            // 去掉特殊的换行符
            msg = msg.Replace(" ", "").Replace("\n", "").Replace("\r", "\n").Replace("\t", "");
            msg = Regex.Replace(msg, @"\r\n{1,}", "\r\n");
            msg = Regex.Replace(msg, @"\n{1,}", "\n");
            msg = Regex.Replace(msg, @"^\n{1,}", "");
           
            // 为了支持文本分块,使用RichTextBlock
            RichTextBlock tbContent = new RichTextBlock();
            tbContent.Width = CT_WIDTH;
            tbContent.Height = CT_HEIGHT;
            tbContent.TextWrapping = TextWrapping.Wrap;
            tbContent.Margin = new Thickness(CT_MARGIN);

            Paragraph ph = new Paragraph();
            ph.TextIndent = 33;

            Run txtRun = new Run();
            txtRun.Text = msg;
            ph.Inlines.Add(txtRun);

            tbContent.Blocks.Add(ph);
            tbContent.FontSize = 24;

            stPanel.Children.Add(tbContent);
            // 更新一下状态,方便获取是否有溢出的文本
            tbContent.UpdateLayout();
            bool isflow = tbContent.HasOverflowContent;
            // 因为除了第一个文本块是RichTextBlock,
            // 后面的都是RichTextBlockOverflow一个一个接起来的
            // 所以我们需要两个变量
            RichTextBlockOverflow oldFlow = null, newFlow = null;
            if (isflow)
            {
                oldFlow = new RichTextBlockOverflow();
                oldFlow.Width = CT_WIDTH;
                oldFlow.Height = CT_HEIGHT;
                oldFlow.Margin = new Thickness(CT_MARGIN);
                tbContent.OverflowContentTarget = oldFlow;
                stPanel.Children.Add(oldFlow);
                oldFlow.UpdateLayout();
                // 继续判断是否还有溢出
                isflow = oldFlow.HasOverflowContent;
            }
            while (isflow)
            {
                newFlow = new RichTextBlockOverflow();
                newFlow.Height = CT_HEIGHT;
                newFlow.Width = CT_WIDTH;
                newFlow.Margin = new Thickness(CT_MARGIN);

                oldFlow.OverflowContentTarget = newFlow;
                stPanel.Children.Add(newFlow);
                newFlow.UpdateLayout();
                // 继续判断是否还有溢出的文本
                isflow = newFlow.HasOverflowContent;
                // 当第一个变量填充了文本后,
                // 把第一个变量的引用指向当前RichTextBlockOverflow
                // 确保OverflowContentTarget属性可以前后相接
                oldFlow = newFlow;
            }
        }
    }

  关于这个展示文本的方法:ShowTxt(),算个重点,  用RichTextBlock控件来展示新闻内容。当然这里面的代码不了解的,先贴上去看看效果,哈哈,我也不太了解,只是写了很多次,把这个格式背下来了,反正以后要用到类似的地方,直接把这个代码贴上去就可以。

  F5 跑起来~~~~

  先按照步骤一步步做下来,如果还是有错呢,可以下载源代码,对比一下。

  终于给写完了,如果你看到了这里,真是谢谢啊!

  此项目参考"传智播客.net培训Windows 8开发视频教程"  、 Microsoft官方提供API帮助。 

   源码下载

 

posted @ 2012-11-25 20:16  lhfly  阅读(1161)  评论(0编辑  收藏  举报