16/07/2013

[WindowsPhone] Lecteur de flux : Couche Model

Bonjour à toutes et à tous,
aujourd'hui, je vous propose sur une série de différents articles de créer le lecteur d'un flux RSS. Ca va nous permettre de décortiquer la plomberie dans une architecte MVVM simplifiée.

Nous allons donc démarrer avec la partie liée au Model.

Dossier complet

  1. [WindowsPhone] Lecteur de flux : Couche Model
  2. [WindowsPhone] Lecteur de flux : Couche ViewModel
  3. [WindowsPhone] Lecteur de flux : Couche View
  4. [WindowsPhone] Lecteur de flux : Le détail du post
  5. [WindowsPhone] Lecteur de flux : LiveTiles, Isolated Storage & BackgroundAgent

On va pour le moment repartir de la solution "vide" que je vous ai présenté ici. Pour un petit rappel :

Donc on va commencer par créer un classe qui va représenter les objets (de type POCO) que nous allons manipuler, à savoir des articles de blog. Comme ceci :

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;

using System.ComponentModel;

namespace SampleRSSReader.Model.POCO
{
    public class PostItem : INotifyPropertyChanged
    {
        #region Accesseurs

        private string _bloggerid;
        public string BloggerId
        {
            get
            {
                return _bloggerid;
            }
            set
            {
                if (_bloggerid != value)
                {
                    _bloggerid = value;
                    NotifyPropertyChanged();;
                }
            }

        }

        private string _title;
        public string Title
        {
            get
            {
                return _title;
            }
            set 
            {
                if (_title != value)
                {
                    _title = value;
                    NotifyPropertyChanged(););
                }
            }

        }

        private DateTime _publishdate;
        public DateTime PublishDate
        {
            get
            {
                return _publishdate;
            }
            set
            {
                if (_publishdate != value)
                {
                    _publishdate = value;
                    NotifyPropertyChanged();;
                }
            }

        }

        private string _content;
        public string Content
        {
            get
            {
                return _summary;
            }
            set
            {
                if (_summary != value)
                {
                    _summary = value;
                    NotifyPropertyChanged();;
                }
            }
        }

        private string _link;
        public string Link
        {
            get
            {
                return _link;
            }
            set
            {
                if (_link != value)
                {
                    _link = value;
                    NotifyPropertyChanged();;
                }
            }
        }


        #endregion



        public event PropertyChangedEventHandler PropertyChanged;

        private void NotifyPropertyChanged([CallerMemberName] String propertyName = "")
        {
            if (PropertyChanged != null)
            {
                PropertyChanged(this, new PropertyChangedEventArgs(propertyName));
            }
        }
    }
}

Pour plus d'informations sur l'interface INotifyPropertyChanged, voyez ici

Maintenant on va créer notre accès à la donnée :

using SampleRSSReader.Model.POCO;
using SampleRSSReader.Resources;
using System;
using System.Collections.Generic;
using System.Collections.ObjectModel;
using System.Globalization;
using System.Linq;
using System.Net;
using System.Text;
using System.Threading.Tasks;
using System.Windows;
using System.Xml.Linq;

namespace SampleRSSReader.Model.Repository
{
    public class BloggerService
    {
        string RequestPosts = "http://pascalpereznet.blogspot.com/feeds/posts/default?alt=rss&redirect=false&max-results=5";

        private readonly ObservableCollection _postdata = new ObservableCollection();
        public ObservableCollection PostData { get { return _postdata; } }

        public void LoadData()
        {
            var myWebClient = new WebClient();
            myWebClient.DownloadStringCompleted += new DownloadStringCompletedEventHandler(myWebClient_DownloadStringCompleted);
            myWebClient.DownloadStringAsync(new Uri(RequestPosts));
        }

        void myWebClient_DownloadStringCompleted(object sender, DownloadStringCompletedEventArgs e)
        {

            if (e.Error != null)
            {
                Deployment.Current.Dispatcher.BeginInvoke(() =>
                {
                    MessageBox.Show(AppResources.BloggerServiceError);
                });
            }
            else
            {
                PostData.Clear();
                UpdatePostsList(e.Result);

            }
            OnLoadAsyncComplete();
        }


        private void UpdatePostsList(string feedXML)
        {
            var flow = XDocument.Parse(feedXML);
            var syncitems = from si in flow.Descendants("item")
                            select new PostItem
                            {
                                BloggerId = si.Element("guid").Value.Substring(si.Element("guid").Value.LastIndexOf("-") + 1),
                                Title = si.Element("title").Value,
                                PublishDate = Convert.ToDateTime(si.Element("pubDate").Value, CultureInfo.InvariantCulture),
                                Content = si.Element("description").Value,
                                Link = si.Element("link").Value
                            };

            foreach (var item in syncitems)
            {
                PostData.Add(item);
            }
        }

        public event EventHandler LoadAsyncComplete;

        protected void OnLoadAsyncComplete()
        {
            LoadAsyncComplete(this, null);
        }

    }
}

Ici je vais prendre un peu de temps pour expliquer. Donc nous avons notre classe avec l'ObservableCollection qui va contenir nos data.
Ensuite le point d'entrée sera la méthode LoadData(). Pour un premier exercice sur Windows Phone nous utilisons un WebClient (1 - C'est plus simple, 2 - Y en a assez pour l'exercice et surtout si c'est votre première application. Promis la prochaine fois, on fera des HttpWebRequest ^^). On s'abonne à l'événement DownloadStringCompleted qui va nous permettre de récupérer l'information de manière asynchrone. (Donc en vulgarisant, nous allons retrouver dans un flux de travail différent de celui du flux principal de celui qui gère l'interface de l'application. Par conséquent, cela n'impacte pas notre utilisateur durant ce traitement). Et on lance notre requête.
En appelant la methode BeginInvoke de notre Dispatcher nous nous rattachons à nouveau au flux principal. Ce qui est le cas si nous obtenons un retour contenant une erreur.
On parse ensuite notre resultat et on le requête (LinQ) pour identifier nos éléments à ajouter dans notre ObservableCollection.
Afin on lève un événement pour signaler à notre ViewModel que nous avons fini notre traitement.

Et là vous vous dites, "moi j'ai vu des mecs qui utilise le pattern async await et s'est bien plus simple !".
Oui c'est vrai mais deux choses : la première est que le pattern async await est une nouveauté de C# 5 donc la méthode que je vous ai présenté à le mérite de fonctionner sur l'ensemble de la plateforme Windows Phone 7.x. Et deuxièmement, le pattern async await ne fonctionne pas un WebClient. Du moins pas en l'état et je ferai prochainement un article sur la manière de contourner cela !

Conclusion

Du coup c'est fini pour aujourd'hui car nous en avons fini avec la couche Model.
Comme d'habitude vous pouvez retrouver les sources ici
La vidéo :


A bientôt !

Aucun commentaire:

Enregistrer un commentaire