Опубликован: 11.01.2013 | Доступ: свободный | Студентов: 623 / 124 | Длительность: 12:06:00
Лекция 4:

Лабораторный практикум 3

< Лекция 3 || Лекция 4: 12345 || Лекция 5 >
Аннотация: Лабораторные работы: RSS-новости, телефонная книжка и локальная база,контакты, учет затрат, сохранение состояния.

Лабораторная работа №11. RSS-новости

Задание

Создать приложение для Windows Phone 7, загружающее из Интернета RSS-новости по разным тематикам (воспользоваться функциональностью класса WebClient). Предусмотреть возможность сохранения загруженных лент в изолированном хранилище.

Освоение

  • работа с HTTP посредством WebClient
  • взаимодействие Linq и XML
  • связывание (Binding)
  • задача запуска (WebBrowserTask)
  • изолированное хранилище

Описание

Создадим новый проект Silverlight for Windows PhoneWindows Phone Application.

Откроем файл разметки главной страницы MainPage.xaml. Будем создавать приложение на основе элемента Panorama. Для этого подключим библиотеку Microsoft.Phone.Controls и добавим пространство имен:

xmlns:ctrl="clr-namespace:Microsoft.Phone.Controls;assembly=Microsoft.Phone.Controls"

Наша Panorama будет состоять из 5 разделов (PanoramaItem): политика, экономика, спорт, музыка и новости MSDN. Каждый PanoramaItem будет включать список ListBox. Списки будем заполнять динамически с использованием шаблонов (Template) и связывания (Binding). Каждая новость будет иметь дату (pubDate) и заголовок (title). В коде для каждого списка будем задавать источник привязки. Содержимое текстовых блоков списков привяжем к источнику привязки с помощью ключевого слова Binding. Следующая структура:

<TextBlock Text="{Binding pubDate}" />

означает, что свойству Text текстового блока автоматически будет присваиваться значение поля pubDate источника привязки.

Также в файле разметки определим стили для текстовых блоков с помощью тега:

<phone:PhoneApplicationPage.Resources>

Добавим две кнопки меню в разметку: "Обновить" и "Перейти". При нажатии на кнопку "Обновить" будем загружать ленту из Интернета и разбивать ее на отдельные новости. При этом соответствующий список будет заполняться автоматически. При нажатии на кнопку "Перейти" будем открывать браузер Windows Phone 7 со ссылкой на выделенную новость.

Итак, файл разметки примет следующий вид:

      <phone:PhoneApplicationPage 
    x:Class="Wp7IUSLab14.MainPage"
    xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
    xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
    xmlns:phone="clr-namespace:Microsoft.Phone.Controls;assembly=Microsoft.Phone"
    xmlns:ctrl="clr-namespace:Microsoft.Phone.Controls;assembly=Microsoft.Phone.Controls"
    xmlns:shell="clr-namespace:Microsoft.Phone.Shell;assembly=Microsoft.Phone"
    xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
    xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
    mc:Ignorable="d" d:DesignWidth="480" d:DesignHeight="696"
    FontFamily="{StaticResource PhoneFontFamilyNormal}"
    FontSize="{StaticResource PhoneFontSizeNormal}"
    Foreground="{StaticResource PhoneForegroundBrush}"
    SupportedOrientations="Portrait" Orientation="Portrait"
    shell:SystemTray.IsVisible="True">

    <phone:PhoneApplicationPage.Resources>
        <Style x:Key="lblDate" TargetType="TextBlock">
            <Setter Property="Foreground" Value="LightBlue" />
            <Setter Property="FontSize" Value="20" />
            <Setter Property="FontStyle" Value="Italic" />
        </Style>

        <Style x:Key="lblTitle" TargetType="TextBlock">
            <Setter Property="FontSize" Value="22" />
            <Setter Property="TextWrapping" Value="Wrap" />
        </Style>
    </phone:PhoneApplicationPage.Resources>

    <!--LayoutRoot is the root grid where all page content is placed-->
    <Grid x:Name="LayoutRoot" Background="Transparent">
        <Grid.RowDefinitions>
            <RowDefinition Height="Auto"/>
            <RowDefinition Height="*"/>
        </Grid.RowDefinitions>

        <!--TitlePanel contains the name of the application and page title-->
        <!--StackPanel x:Name="TitlePanel" Grid.Row="0" Margin="12,17,0,28">
            <TextBlock x:Name="ApplicationTitle" Text="MY APPLICATION" 
            Style="{StaticResource PhoneTextNormalStyle}"/>
            <TextBlock x:Name="PageTitle" Text="page name" Margin="9,-7,0,0" 
            Style="{StaticResource PhoneTextTitle1Style}"/>
        </StackPanel-->

        <!--ContentPanel - place additional content here-->
        <Grid x:Name="ContentPanel" Grid.Row="1" Margin="12,0,12,0">
            <ctrl:Panorama Name="pnrmRSS" Title="новости">
                <ctrl:PanoramaItem Header="политика">
                    <ListBox Name="lstRssPolit">
                        <ListBox.ItemTemplate>
                            <DataTemplate>
                                <StackPanel>
                                    <TextBlock Text="{Binding pubDate}" Style="{StaticResource lblDate}" />
                                    <TextBlock Text="{Binding title}" Style="{StaticResource lblTitle}" />
                                </StackPanel>
                            </DataTemplate>
                        </ListBox.ItemTemplate>
                    </ListBox>
                </ctrl:PanoramaItem>

                <ctrl:PanoramaItem Header="экономика">
                    <ListBox Name="lstRssEcon">
                        <ListBox.ItemTemplate>
                            <DataTemplate>
                                <StackPanel>
                                    <TextBlock Text="{Binding pubDate}" Style="{StaticResource lblDate}" />
                                    <TextBlock Text="{Binding title}" Style="{StaticResource lblTitle}" />
                                </StackPanel>
                            </DataTemplate>
                        </ListBox.ItemTemplate>
                    </ListBox>
                </ctrl:PanoramaItem>

                <ctrl:PanoramaItem Header="спорт">
                    <ListBox Name="lstRssSport">
                        <ListBox.ItemTemplate>
                            <DataTemplate>
                                <StackPanel>
                                    <TextBlock Text="{Binding pubDate}" Style="{StaticResource lblDate}" />
                                    <TextBlock Text="{Binding title}" Style="{StaticResource lblTitle}" />
                                </StackPanel>
                            </DataTemplate>
                        </ListBox.ItemTemplate>
                    </ListBox>
                </ctrl:PanoramaItem>

                <ctrl:PanoramaItem Header="музыка">
                    <ListBox Name="lstRssMusic">
                        <ListBox.ItemTemplate>
                            <DataTemplate>
                                <StackPanel>
                                    <TextBlock Text="{Binding pubDate}" Style="{StaticResource lblDate}" />
                                    <TextBlock Text="{Binding title}" Style="{StaticResource lblTitle}" />
                                </StackPanel>
                            </DataTemplate>
                        </ListBox.ItemTemplate>
                    </ListBox>
                </ctrl:PanoramaItem>

                <ctrl:PanoramaItem Header="MSDN">
                    <ListBox Name="lstRssMSDN">
                        <ListBox.ItemTemplate>
                            <DataTemplate>
                                <StackPanel>
                                    <TextBlock Text="{Binding pubDate}" Style="{StaticResource lblDate}" />
                                    <TextBlock Text="{Binding title}" Style="{StaticResource lblTitle}" />
                                </StackPanel>
                            </DataTemplate>
                        </ListBox.ItemTemplate>
                    </ListBox>
                </ctrl:PanoramaItem>
            </ctrl:Panorama>
        </Grid>
    </Grid>
 
    <!--Sample code showing usage of ApplicationBar-->
    <phone:PhoneApplicationPage.ApplicationBar>
        <shell:ApplicationBar IsVisible="True" IsMenuEnabled="True">
            <shell:ApplicationBarIconButton IconUri="/Images/appbar.refresh.rest.png" 
            Text="Обновить" Click="MenuRefresh_Click" />
            <shell:ApplicationBarIconButton IconUri="/Images/appbar.next.rest.png" 
            Text="Перейти" Click="MenuGo_Click" />
            
            <!--shell:ApplicationBar.MenuItems>
                <shell:ApplicationBarMenuItem Text="MenuItem 1"/>
                <shell:ApplicationBarMenuItem Text="MenuItem 2"/>
            </shell:ApplicationBar.MenuItems-->
        </shell:ApplicationBar>
    </phone:PhoneApplicationPage.ApplicationBar>

</phone:PhoneApplicationPage>

Перейдем к написанию кода.

Создадим класс для новостей (к полям данного класса и будем привязывать содержимое текстовых полей разметки):

      public class PostMessage
    {
        public string pubDate { get; set; }
        public string title { get; set; }
        public string link { get; set; }
    }

Для того чтобы загружать RSS-ленту добавим в класс 2 переменные и 5 констант со ссылками на источники:

        private const string RSS_POLIT = "http://news.yandex.ru/politics.rss";
        private const string RSS_ECNMK = "http://news.yandex.ru/business.rss";
        private const string RSS_SPORT = "http://news.yandex.ru/sport.rss";
        private const string RSS_MUSIC = "http://news.yandex.ru/music.rss";
        private const string RSS_MSDN = "http://blogs.msdn.com/b/rudevnews/rss.aspx";

        private WebClient client;
        private int m_nRssLoadIndex = -1;

Добавим в конструктор инициализацию веб-клиента:

client = new WebClient();
  client.DownloadStringCompleted += new DownloadStringCompletedEventHandler(WebClient_DownloadStringCompleted);

При нажатии на кнопку "Обновить" будем загружать веб-содержимое по ссылкам:

            private void MenuRefresh_Click(object sender, EventArgs e)
        {
            m_nRssLoadIndex = pnrmRSS.SelectedIndex;

            switch (m_nRssLoadIndex)
            {
                case 0:
                    LoadRSS(new Uri(RSS_POLIT));
                    break;
                case 1:
                    LoadRSS(new Uri(RSS_ECNMK));
                    break;
                case 2:
                    LoadRSS(new Uri(RSS_SPORT));
                    break;
                case 3:
                    LoadRSS(new Uri(RSS_MUSIC));
                    break;
                case 4:
                    LoadRSS(new Uri(RSS_MSDN));
                    break;
                default:
                    MessageBox.Show("Ошибка при загрузке новостей.");
                    break;
            }
        }

        private void LoadRSS(Uri url)
        {
            client.DownloadStringAsync(url);
        }

Напишем обработчик окончания загрузки (будем отправлять загруженную строку e.Result на обработку):

  // Окончание загрузки строки
        private void WebClient_DownloadStringCompleted(object sender, DownloadStringCompletedEventArgs e)
        {
            if (e.Error == null)
            {
                ParseAndDislayRSS(e.Result);
            }
        }

При обработке будем использовать встроенные средства XML-парсера и Linq. Для этого добавим в код директиву:

 using System.Xml.Linq;

Затем с помощью LInq запроса получим коллекцию новостей (типа PostMessage). Данную коллекцию будем задавать как источник для списков (свойство ItemsSource).

  private void ParseAndDislayRSS(string strRSS, bool isSaveToStorage = true)
        {
            //получаем новости, парсим их и присваиваем списку
            XElement twitterElements = XElement.Parse(strRSS);

            var postList =
                from tweet in twitterElements.Descendants("item")
                select new PostMessage
                {
                    title = tweet.Element("title").Value,
                    pubDate = tweet.Element("pubDate").Value,
                    link = tweet.Element("link").Value
                };

            switch (m_nRssLoadIndex)
            {
                case 0:
                    lstRssPolit.ItemsSource = postList;
                    if (isSaveToStorage) SaveToIsolatedStorage(strIStoragePolit, strRSS);
                    break;
                case 1:
                    lstRssEcon.ItemsSource = postList;
                    if (isSaveToStorage) SaveToIsolatedStorage(strIStorageEconom, strRSS);
                    break;
                case 2:
                    lstRssSport.ItemsSource = postList;
                    if (isSaveToStorage) SaveToIsolatedStorage(strIStorageSport, strRSS);
                    break;
                case 3:
                    lstRssMusic.ItemsSource = postList;
                    if (isSaveToStorage) SaveToIsolatedStorage(strIStorageMusic, strRSS);
                    break;
                case 4:
                    lstRssMSDN.ItemsSource = postList;
                    if (isSaveToStorage) SaveToIsolatedStorage(strIStorageMsdn, strRSS);
                    break;
                default:
                    MessageBox.Show("Ошибка при обработке новостей.");
                    break;
            }
        }

Загруженную ленту сразу же будем сохранять в изолированном файловом хранилище. Для этого в классе определим глобальные константы с именами файлов:

  private const string strIStoragePolit = "Lab14Polit.xml";
        private const string strIStorageEconom = "Lab14Econom.xml";
        private const string strIStorageSport = "Lab14Sport.xml";
        private const string strIStorageMusic = "Lab14Music.xml";
        private const string strIStorageMsdn = "Lab14Msdn.xml";

Для того чтобы воспользоваться изолированным хранилищем определим директивы:

  using System.IO.IsolatedStorage;
  using System.IO;

В конструкторе класса будем загружать новость из изолированного хранилища (аналогичные строки для всех тем новостей):

  if (IsIsolatedStorageExist(strIStoragePolit))
        {
            m_nRssLoadIndex = 0;
            ParseAndDislayRSS(LoadFromIsolatedStorage(strIStoragePolit), false);
        }

Работу с изолированным хранилищем обеспечивают следующие четыре функции:

  private void SaveToIsolatedStorage(string histFileName, string histText)
        {
            IsolatedStorageFile fileStorage = IsolatedStorageFile.GetUserStoreForApplication();
            IsolatedStorageFileStream fileStream = fileStorage.CreateFile(histFileName);

            StreamWriter sw = new StreamWriter(fileStream);
            sw.Write(histText);
            sw.Close();

            fileStream.Close();
        }

        private string LoadFromIsolatedStorage(string histFileName)
        {
            IsolatedStorageFile fileStorage = IsolatedStorageFile.GetUserStoreForApplication();
            IsolatedStorageFileStream fileStream = fileStorage.OpenFile(histFileName, System.IO.FileMode.Open);

            StreamReader sr = new StreamReader(fileStream);
            string strRes = sr.ReadToEnd();
            sr.Close();
            fileStream.Close();

            return strRes;
        }

        private bool IsIsolatedStorageExist(string histFileName)
        {
            IsolatedStorageFile sileStorage = IsolatedStorageFile.GetUserStoreForApplication();
            return sileStorage.FileExists(histFileName);
        }

        private void RemoveIsolatedStorage(string histFileName)
        {
            if (IsIsolatedStorageExist(histFileName))
            {
                IsolatedStorageFile fileStorage = IsolatedStorageFile.GetUserStoreForApplication();
                fileStorage.DeleteFile(histFileName);
            }
        }

При нажатии на кнопку "Перейти" будем создавать экземпляр класса WebBrowserTask (задача запуска) с указанием прямой ссылки на новость (в начале файла необходимо определить директиву "using Microsoft.Phone.Tasks"):

 WebBrowserTask webTask = new WebBrowserTask();
        webTask.Uri = new Uri(((PostMessage)(lstRssPolit.SelectedItem)).link);
        webTask.Show();

Теперь можно скомпилировать приложение, запустить на эмуляторе или телефоне и проверить его функциональность.

< Лекция 3 || Лекция 4: 12345 || Лекция 5 >