29/07/2013

[WindowsPhone] Lecteur de flux : Couche View

Bonjour à toutes et à tous,
On se retrouve pour la troisième partie de la création de notre lecteur de flux RSS avec la création de la couche View.

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

Mise en route

Bon, ça fait deux semaines qu'on est sur ce lecteur, ce serai pas mal qu'on puisse tester si ce qu'on a fait fonctionne. Donc on va faire le strict minimum pour vérifier visuellement qu'on récupère les données et les afficher. Donc c'est parti !
On démarre avec le MainPage.xaml.cs:

using System;
using System.Collections.Generic;
using System.Linq;
using System.Net;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Navigation;
using Microsoft.Phone.Controls;
using Microsoft.Phone.Shell;
using SampleRSSReader.Resources;
using SampleRSSReader.ViewModel;
using SampleRSSReader.Model.POCO;

namespace SampleRSSReader
{
    public partial class MainPage : PhoneApplicationPage
    {

        ViewModelLocator vml;

        public MainPage()
        {
            InitializeComponent();
            vml = new ViewModelLocator();
            this.DataContext = vml;

        }

        protected override void OnNavigatedTo(NavigationEventArgs e)
        {
            base.OnNavigatedTo(e);
            vml.OnNavigatedTo();
        }
    }
}

Donc on instancie notre ViewModelLocator et on définie le DataContext.
Enfin on va surcharger la méthode OnNavigatedTo et déclencher le chargement de nos ViewModel.
Maintenant, on va s'occuper d'un affichage minimaliste des ces données :

<phone:PhoneApplicationPage
    x:Class="SampleRSSReader.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: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"
    FontFamily="{StaticResource PhoneFontFamilyNormal}"
    FontSize="{StaticResource PhoneFontSizeNormal}"
    Foreground="{StaticResource PhoneForegroundBrush}"
    SupportedOrientations="Portrait" Orientation="Portrait"
    shell:SystemTray.IsVisible="True">
    <!--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 Text="MY APPLICATION" Style="{StaticResource PhoneTextNormalStyle}" Margin="12,0"/>
            <TextBlock 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">
            <phone:LongListSelector ItemsSource="{Binding PostsViewModel.MyBlogPosts}">
                <phone:LongListSelector.ItemTemplate>
                    <DataTemplate>
                        <TextBlock Text="{Binding Title}"/>
                    </DataTemplate>
                </phone:LongListSelector.ItemTemplate>
            </phone:LongListSelector>
        </Grid>
    </Grid>

</phone:PhoneApplicationPage>

Donc on rajoute un LongListSelector, on effectue notre Binding sur l'ObservableCollection de notre ViewModel. On édite l'ItemTemplate et on lui affecte un template avec un TextBlock qui se sera bindé sur la propriété Title.

On compile et on envoie ça sur notre émulateur.

Avec des Images c'est mieux !

Maintenant que tout fonctionne, on va "embellir" notre application en affichant des images.
Donc en fait les images sont contenues dans le contenu de l'article, il va donc falloir les isoler pour alimenter nos Image. Pour cela on va utiliser un Converter que j'ai déjà publié sur mon blog et que vous pouvez retrouver ici.

Donc on va ensuite déclarer notre Converter dans le fichier App.xaml :

<Application
    x:Class="SampleRSSReader.App"
    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:shell="clr-namespace:Microsoft.Phone.Shell;assembly=Microsoft.Phone"

    xmlns:conv="clr-namespace:SampleRSSReader.View.Converter">

    <!--Application Resources-->
    <Application.Resources>
        <local:LocalizedStrings xmlns:local="clr-namespace:SampleRSSReader" x:Key="LocalizedStrings"/>

        <conv:RSSImageConverter x:Key="ImagePicker" />

    </Application.Resources>

    <Application.ApplicationLifetimeObjects>
        <!--Required object that handles lifetime events for the application-->
        <shell:PhoneApplicationService
            Launching="Application_Launching" Closing="Application_Closing"
            Activated="Application_Activated" Deactivated="Application_Deactivated"/>
    </Application.ApplicationLifetimeObjects>

</Application>

Et enfin, on modifie notre DataTemplate sur MainPage.xaml :

<phone:PhoneApplicationPage
    x:Class="SampleRSSReader.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: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"
    FontFamily="{StaticResource PhoneFontFamilyNormal}"
    FontSize="{StaticResource PhoneFontSizeNormal}"
    Foreground="{StaticResource PhoneForegroundBrush}"
    SupportedOrientations="Portrait" Orientation="Portrait"
    shell:SystemTray.IsVisible="True">

    <!--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 Text="PASCALPEREZNET.BLOGSPOT.COM" Style="{StaticResource PhoneTextNormalStyle}" Margin="12,0"/>
            <TextBlock Text="Blog Posts" 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">
            <phone:LongListSelector ItemsSource="{Binding PostsViewModel.MyBlogPosts}">
                <phone:LongListSelector.ItemTemplate>
                    <DataTemplate>
                        <StackPanel Orientation="Horizontal" Margin="12">
                            <Image Source="{Binding Path=Content, Converter={StaticResource ImagePicker}}" Height="30" Width="30" Margin="0,0,12,0"/>
                            <TextBlock Text="{Binding Title}"/>
                        </StackPanel>
                    </DataTemplate>
                </phone:LongListSelector.ItemTemplate>
            </phone:LongListSelector>
        </Grid>
    </Grid>

</phone:PhoneApplicationPage>

On compile et on envoie au compilateur, je veux simplement attirer votre attention sur la valeur du Margin que j'ai utilisé dans mon StackPanel. C'est un 12, et ce n'est pas un 12 par hasard c'est que c'est le chiffre d'or à utiliser en Windows Phone pour la bonne et simple raison que c'est l'intervalle entre deux éléments pour arriver à sélectionner sur du tactile.

Accéder aux articles

Maintenant que nous avons notre liste, il serait intéressant de naviguer jusqu'à l'article. On va donc s'abonner au changement de sélection du LongListSelector pour récupérer l'url de l'article et utiliser le navigateur natif du téléphone :

MainPage.xaml
<phone:LongListSelector ItemsSource="{Binding PostsViewModel.MyBlogPosts}" SelectionChanged="LongListSelector_SelectionChanged">
                <phone:LongListSelector.ItemTemplate>
                    <DataTemplate>
                        <StackPanel Orientation="Horizontal" Margin="12">
                            <Image Source="{Binding Path=Content, Converter={StaticResource ImagePicker}}" Height="30" Width="30" Margin="0,0,10,0"/>
                            <TextBlock Text="{Binding Title}"/>
                        </StackPanel>
                    </DataTemplate>
                </phone:LongListSelector.ItemTemplate>
            </phone:LongListSelector>
MainPage.xaml.cs
        private void LongListSelector_SelectionChanged(object sender, SelectionChangedEventArgs e)
        {
            var myItem = (PostItem)(sender as LongListSelector).SelectedItem;
            WebBrowserTask myWebBrowser = new WebBrowserTask();
            myWebBrowser.Uri = new Uri(myItem.Link);
            myWebBrowser.Show();
        }

On compile et on envoie sur l'émulateur. On teste ça marche mais... Si vous vous choisi un élément qui se trouve à la fin de la liste, vous vous êtes rendu compte que vous êtes automatique rebasculé au le début de la liste.

La navigation

Le phénomène constaté plus haut est du au fait que lorsque nous avons effectué notre retour avec la touche back de notre téléphone pour revenir en arrière, la méthode OnNavigatedTo a été exécutée ce qui est logique puisque nous naviguions vers elle. Pour éviter d'avoir à nouveau ce phénomène :

        protected override void OnNavigatedTo(NavigationEventArgs e)
        {
            if (e.NavigationMode != NavigationMode.Back)
            {
                base.OnNavigatedTo(e);
                vml.OnNavigatedTo();
            }
        }

Conclusion

Voilà ce sera le mot de la fin.
Comme d'habitude, vous pouvez retrouver les sources ici
La vidéo arrive dans les prochains jours !
A bientôt !

26/07/2013

[XAML] INotifyPropertyChanged

Bonjour à toutes et à tous,
aujourd'hui je vais vous présenter l'interface INotifyPropertyChanged. Donc pour celles et ceux qui ne la connaîtrait pas c'est par ici.

Ca sert à quoi ?

Très utilisée dans les technologies XAML (Windows Phone, Silverlight, WPF), cette interface va nous permettre de signaler qu'une propriété d'un objet a été modifiée par le biais d'un événement et cela va permettre de notifier à votre View(MVVM) ce changement et permettre le rafraîchissement d'informations dans le cadre de Binding.
Du coup c'est plutôt utile. ^^

Comment on fait ?

Premièrement en l'implémentant l'interface.

public class User : System.ComponentModel.INotifyPropertyChanged
{

}
Puis vous faites un clic droit sur INotifyPropertyChanged et "Implémenter l'interface". Vous allez récupérer l'évént handler automatiquement dans votre classe :

public event PropertyChangedEventHandler PropertyChanged;

Et c'est tout ce que vous allez récupérer ! Libre à vous de composer la méthode qui va lever l'événement !

Les accesseurs

Pour petit rappel, pour coder nos accesseurs avant C# 3 :

private string _name;
public string Name
{
   get
   {
      return _name;
   }
   set
   {
      _name = value;
   }
}

Pour coder nos accesseurs après C# 3 :

public string Name { get; set; }

Nos accesseurs avec INotifyPropertyChanged :

private string _name;
public string Name
{
   get
   {
      return _name;
   }
   set
   {
      if(_name != value)
      {
         _name = value;
         // Ici on notifie
      }
   }
}

Pourquoi vérifier si la valeur est changé, simplement dans un souci de performances. "Trop de notifications, tue la notification". De plus et toujours pour les mêmes raisons, ne coder vos accesseurs de cette manière que si les propriétés ont besoins de signaler qu'elles ont été modifiées.

Méthodes de notification

Bon pour les méthodes je vous en propose aujourd'hui 2.
La première est celle proposée dans la MSDN :

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

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

La seconde est celle que j'ai trouvé dans le livre écrit par Jonathan ANTOINE et Thomas LEBRUN intitulé "MVVM - De la découverte à la maîtrise" que vous pouvez retrouver ici


protected void RaisePropertyChanged<T>(Expression<Func<T>> exp)
{
   var memberExpression = exp.Body as MemberExpression;

   if (memberExpression != null)
   {
      string propertyName = memberExpression.Member.Name;
      PropertyChangedEventHandler handler = PropertyChanged;
      if (handler != null)
      {
         handler(this, new PropertyChangedEventArgs(propertyName));
      }
   }
}

private string _name;
public string Name
{
   get
   {
      return _name;
   }
   set
   {
      if(_name != value)
      {
         _name = value;
         RaisePropertyChanged<string>(() => Name);
      }
   }
}

C'est par exemple cette deuxième implémentation que j'ai utilisé pour mon application "Gâteaux de Laetitia".

Conclusion

Pour plus d'information sur l'interface, c'est par ici
Les deux méthodes présentées ici, ont le mérite de pouvoir continuer à fonctionner même si vous renommez vos variables. Voilà c'est tout pour aujourd'hui.
A bientôt

22/07/2013

[WindowsPhone] Lecteur de flux : Couche ViewModel

Bonjour à toutes et à tous
Nous allons reprendre notre lecteur de flux rss où nous l'avions laissé et allons nous occuper aujourd'hui de la couche ViewModel.

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

Modèle du ViewModel

Donc on va créer notre dossier ViewModel.
Cette fois-ci nous allons créer un modèle qui sera repris par l'ensemble de nos ViewModel. Ce modèle gère la propriété IsBusy de notre ViewModel, l'interface INotifyPropertyChanged et l'événement pour la fin du traitement :

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

using System.Runtime.CompilerServices;

namespace MyApp.ViewModel
{
    public class ViewModelBase : INotifyPropertyChanged
    {
        public event PropertyChangedEventHandler PropertyChanged;

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

        private bool _isBusy;
        public bool IsBusy
        {
            get { return _isBusy; }
            set
            {
                if (_isBusy != value)
                {
                    _isBusy = value;
                    NotifyPropertyChanged();
                }
            }
        }

        public event EventHandler OnTreatmentCompleted;

        protected void TreatmentCompleted()
        {
            onTreatmentCompleted(this, null);
        }
    }
}

ViewModel des Posts

Ensuite nous créons notre ViewModel qui va gérer une collection d'items, la propriété IsBusy pour indiquer si notre ViewModel est toujours en cours de traitement. Notre méthode d'entrée est bien évidemment la méthode OnNavigatedTo et on s'abonne à l'événement de notre BloggerService. Lorsque le traitement est terminé, nous récupérons les data et on signale que le traitement est terminé :

using MyApp.Model.ModeleClient;
using MyApp.Model.Services;
using System;
using System.Collections.Generic;
using System.Collections.ObjectModel;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Windows;

namespace MyApp.ViewModel
{
    public class PostsViewModel : ViewModelBase
    {

        private ObservableCollection _myBlogPosts = new ObservableCollection();
        public ObservableCollection MyBlogPosts
        {
            get
            {
                return _myBlogPosts;
            }
            set
            {
                if (_myBlogPosts != value)
                {
                    _myBlogPosts = value;
                    NotifyPropertyChanged();
                }
            }

        }

        BloggerService BloggerSvc;

        public void OnNavigatedTo()
        {
            IsBusy = true;
            BloggerSvc = new BloggerService();
            BloggerSvc.LoadAsyncComplete += BloggerSvc_LoadAsyncComplete;
            BloggerSvc.LoadData();
        }

        void BloggerSvc_LoadAsyncComplete(object sender, EventArgs e)
        {
            Deployment.Current.Dispatcher.BeginInvoke(() =>
            {
                MyBlogPosts = BloggerSvc.PostData;
                IsBusy = false;
                TreatmentCompleted();
            });
        }
    }
}

BONUS : ViewModelLocator

Dans le cadre de notre application, il n'est pas forcément nécessaire de créer un ViewModelLocator car nous n'avons qu'un seul ViewModel. Mais cela deviendra utile dès que nous en aurons plusieurs. Notre ViewModelLocator va nous permettre d'accéder à l'ensemble de nos ViewModel côté XAML sans changer de DataContext

using System;
using System.Collections.ObjectModel;
using System.ComponentModel;
using MyApp.Resources;
using MyApp.Model;
using System.Net;
using System.Windows;
using System.IO;
using System.Xml;
using System.Linq;
using System.Xml.Linq;
using System.IO.IsolatedStorage;
using LGDL.WP8.Model.ModeleClient;
using Microsoft.Phone.Net.NetworkInformation;

namespace MyApp.ViewModel
{
    public class ViewModelLocator : ViewModelBase
    {
        public PostsViewModel PostsViewModel { get; set; }

        public ViewModelLocator()
        {
            PostsViewModel = new PostsViewModel();

            PostsViewModel.OnTreatmentCompleted += ViewModel_OnTreatmentCompleted;
        }

        void ViewModel_OnTreatmentCompleted(object sender, EventArgs e)
        {
            IsBusy = false;
        }

        public void OnNavigatedTo()
        {
            IsBusy = true;
            PostsViewModel.OnNavigatedTo();
        }
    }
}

Conclusion

Bon voilà, notre couche, vous pouvez retrouver les sources ici
La vidéo :


A vendredi pour un prochain article et à lundi prochain pour la suite.

19/07/2013

[WindowsPhone] WebClient et async await pattern

Bonjour à toutes et à tous
aujourd'hui je fais une suite à l'article ici sur la mise place du pattern async await (Nouveauté C# 5) avec un WebClient.

WebClientExtensions

Comme je l'avais annoncé dans l'article précédent, il n'est pas possible en l'état d'utiliser un WebClient avec le pattern async await. Du coup, on va voir aujourd'hui comment contourner cela.

Alors le premier point est de comprendre pourquoi cela ne marche pas. Tout simplement car la méthode DownloadStringAsync ne renvois pas de Task nécessaire au await.

Donc pour contourner cette limitation nous allons, dans la partie liée au services, créer un classe supplémentaire qui va venir surchager notre WebClient et lui faire renvoyer une Task.

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

namespace System.Threading.Tasks
{
    public static class WebClientExtensions
    {

        public static Task<string> DownloadStringTaskAsync(this WebClient webClient, Uri address)
        {
            var taskCSource = new TaskCompletionSource<string>();

            webClient.DownloadStringCompleted += (s, e) =>
            {
                if (e.Error != null)
                {
                    taskCSource.SetException(e.Error);
                }
                else
                {
                    taskCSource.SetResult(e.Result);
                }
            };

            webClient.DownloadStringAsync(address);

            return taskCSource.Task;
        }

        public static Task<string> DownloadStringTaskAsync(this WebClient webClient, string address)
        { 
            return DownloadStringTaskAsync(webClient, new Uri(address));
        }
    }
}

Si vous recréez cette classe, prenez garde au namespace, il est important ! Donc dans la première méthode, on instancie notre TaskCompletionSource on s'abonne à l'événement DownloadStringCompleted et on utilise un delagate pour assignes Exception et Result à notre tâche. Enfin on lance de téléchargement et on retourne la Task.
La seconde méthode est simplement une surcharge au cas où vous souhaiteriez lancer la même méthode avec un string plutôt qu'une Uri.

Conclusion

Voilà c'est tout pour aujourd'hui, comme d'habitude vous pouvez retrouver les sources ici

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 !

12/07/2013

[Converter] BoolToVisibility

Allez c'est sans conteste, le converter dont vous vous servirez le plus. Convertir un Boolean en Visibility

Bon je pense qu'il n'y a pas besoin d'explication supplémentaire sur l'intérêt de ce converter donc :

public class BoolToVisibilityConverter : IValueConverter
    {

        public object Convert(object value, Type targetType, object parameter, System.Globalization.CultureInfo culture)
        {
            // Vérification de la valeur
            if (value == null)
            {
                return null;
            }
            else
            {
                var myBoolean = (bool)value;

                // Vérification du paramètre
                if (parameter != null)
                {
                    // Autant de paramètre qu'on veut
                    switch (parameter.ToString())
                    {
                        case "invert":
                            return myBoolean ? Visibility.Collapsed : Visibility.Visible;
                        default:
                            return myBoolean ? Visibility.Visible : Visibility.Collapsed;
                    }
                }
                else
                {
                    return myBoolean ? Visibility.Visible : Visibility.Collapsed;
                }
            }
        }

        public object ConvertBack(object value, Type targetType, object parameter, System.Globalization.CultureInfo culture)
        {
            throw new NotImplementedException();
        }
    }

Pour en savoir plus sur les converter c'est par ici

Une notion abordée ici qui n'est pas abordée dans mon précédent article est le ConverterParameter qui sert tout simplement à passer un paramètre à mon converter. Dans ma classe, le paramètre invert permet d'inverser le résultat.


    
        
    


Il est à noter qu'en WPF le framework embarque directement le converter donc, il reste plus qu'à l'instancier dans notre code côté XAML :


      

A Lundi pour une nouvelle vidéo.

08/07/2013

[WindowsPhone] SystemTray et ProgressIndicator

Bonjour à toutes et à tous,
aujourd'hui ce sera le tour du SystemTray et du ProgressIndicator. Vraiment une fonctionnalité indispensable pour vos utilisateurs afin qu'ils puissent vérifier leur réseau avant d'incriminer votre application. ^^

SystemTray

Alors pour le SystemTray, il faut que celui ci soit bien défini comme visible dans la balise PhoneApplicationPage :

    shell:SystemTray.IsVisible="True"

Pour jouer sur l'opacité du SystemTray, ça se joue toujours au même endroit :

    shell:SystemTray.Opacity="0.5"

ProgressIndicator

Nous le retrouvons dans le namespace shell en tant que propriété de la SystemTray et on lui passe la classe ProgressIndicator disponible dans le shell :


   
      
      
   

Alors un petit conseil d'utilisation qui a été pas mal relayé: lorsque vous effectuer votre Binding ou que vous assignez la valeur IsVisible pensez à systématiquement affecter la même valeur à la propriété IsIndeterminate. Tout simplement parce que même si votre ProgressIndicator n'est plus visible, il continu de consommer des ressources pour l'aspect indéterminé.

Maintenant, nous avons également la possibilité de personnaliser le texte de ce ProgressIndicator qui devra être bien évidemment globalisé.

On modifie le fichier de ressources :

Et il ne nous reste plus qu'à effectuer notre Binding.


   
      
      
   

Le résultat sur l'émulateur:

Conclusion

Voilà c'est tout. Vous pouvez retrouver les sources ici.
Et la vidéo :

A bientôt.

05/07/2013

[Converter] ImagePicker

On inaugure aujourd'hui la catégorie des Converter en démarrant avec un sélecteur d'image.

Pour finir cette semaine tranquillement, je vous propose un sélecteur d'image :

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Text.RegularExpressions;
using System.Threading.Tasks;
using System.Windows.Data;

namespace MyApp.WP8.View.Converter
{
    public class ImagePicker : IValueConverter
    {
        public object Convert(object value, Type targetType, object parameter, System.Globalization.CultureInfo culture)
        {
            string matchString = Regex.Match(value.ToString(), @"(?<=<img.*?src=\"")[^\""]*(?=\"".*?((/>)|(>.*</img)))").Value;
            if (matchString == "")
            {
                return "https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEgPgjvN03Mt9fpkGDo4AnQtOImddidJGYXbT18Yz7EyjBpt2NNrxZ1IvJXnCMNQEY-lNftZb66KYRznXWTCZSyH5HAkiRtD-JrMfbRaZQUzWUUz4PqOQlvlVFgu2zjV5YYlM3M0FMkyRUMN/s220/AvatarFB.png";
            }
            else
            {
                return matchString;
            }
        }

        public object ConvertBack(object value, Type targetType, object parameter, System.Globalization.CultureInfo culture)
        {
            throw new NotImplementedException();
        }
    }
}

Très utile notamment pour les flux RSS, il va nous permettre de renvoyer une image par défaut si notre item ne contient pas d'image.

Bon WE. A lundi pour un nouveau Tuto vidéo.

01/07/2013

[WindowsPhone] Application Bar

Bonjour à toutes et à tous,
je souhaitais commencer par quelque chose de simple pour ce premier article parce qu'en même temps je souhaite tenter un expérience nouvelle en proposant, en plus des sources mise à disposition en fin d'article, de réaliser une vidéo explicative consultable depuis le blog. Surtout n'hésitez pas à commenter pour si ça vaut le coup de continuer. Allez c'est parti...

Donc on démarre par le contrôle Application Bar. C'est pas le contrôle le compliqué du monde mais il faut bien connaître certaines spécificités notamment si l'on recherche à globaliser son application.

Application Bar



        
            
        
    

Comme vous le constatez, on va chercher la propriété ApplicationBar dans notre classe PhoneApplication qui se trouve dans le namespace Phone. Et Ensuite va chercher l'interface IApplicationBar qui elle se trouve dans le namespace shell (pour plus d'infos sur IApplicationBar).

Ensuite on dispose de deux collections d'éléments pour travailler :
  • Buttons (gére la collection d'objets de type ApplicationBarIconButton)
  • MenuItems (gére la collection d'objets de type ApplicationBarMenuItem)

Collection d'ApplicationBarIconButton

Alors ces ApplicationsBarIconButton vont fonctionner de la manière suivante :


        
            
        
    

  • le cercle autour des éléments est créé automatiquement
  • quelque soit la casse qui est utilisée, le texte sera affiché en minuscule
  • le mot représentant la commande ne devra pas dépasser environ 10 caractères sinon le reste ne sera pas affiché
  • Jusqu'à 4 éléments

Donc vous vous demandez maintenant comment je vais trouver de beaux boutons en flat design (modern UI). En fait vous les avez déjà et ils sont installés avec le SDK sur votre poste (C:\Program Files(x86)\Microsoft SDK\Windows Phone\[Version]\Icons\[Theme]\). Alors les principaux seulement mais de nombreux sites en propose.

Collection d'ApplicationBarMenuItem

Les ApplicationBarMenuItem doivent être défini dans la collection MenuItems de l'ApplicationBar:


        
            
            
                
            
        
    

Globalisation

Il est vrai qu'en développant à destination de "marchés" (App Store, Store, Marketplace, etc...) on souhaite toucher le plus grand nombre d'utilisateurs et bien souvent il faudra penser à globaliser son application. Lorsqu'on génère un projet, toute la mécanique est déjà en place.

Une des particularités de l'ApplicationBar est qu'on ne va pas pouvoir effectuer de Binding sur les propriétés "Text" par conséquent, on gère tout cela côté code behind:

        private void GeolocalizeApplicationBar()
        {
            var myResearchButton = (ApplicationBarIconButton)ApplicationBar.Buttons[0];
            myResearchButton.Text = AppResources.ApplicationBarResearch;

            var myResearchMenuItem = (ApplicationBarMenuItem)ApplicationBar.MenuItems[0];
            myResearchMenuItem.Text = AppResources.ApplicationBarResearch;         
        }
On fait donc un cast notre item que l'on va chercher dans les collections de la propriété ApplicationBar de notre PhoneApplicationPage et on lui affecte la valeur qu'on aura au préalable définie dans le fichier de ressources (cf. vidéo).

Conclusion

Bon voilà pour ce point vous pouvez retrouvez les sources du projets ici

Et voici la démo vidéo :


A bientôt

[WindowsPhone] Every saga has a beginning...

Bonjour à toutes et à tous,
comme j'en ai parlé dans mon précédent article, je commence aujourd'hui une série d'articles visant à montrer, dans le détail, comment intégrer au mieux vos projets d'application sur la plateforme Windows Phone. Que vous soyez intermédiaire ou débutant, vous devriez tous, je l'espère, trouver des informations intéressantes. (Désolé pour les ninjas du code, vous n'apprendrez rien ici puisque vous êtes déjà au top ! ^^). Et une nouvelle catégorie verra le jour à savoir "Converter" ou je ressemblerai les converter les plus utilisés.

Design Pattern - MVVM

De suite les mots qui fâchent, oui on y comprend rien, c'est compliqué ! Et pour les débutants quand on s'attaque un peu la théorie, on a l'impression que c'est une "usine à gaz". Pour celles et ceux qui ne l'on pas encore fait, je vous conseille de lire un livre sur le sujet en français, ici. D'autre part, pour garder tout le monde dans le rang je vais vous proposer une implémentation simplifié de ce design pattern, le temps de bien se familiariser avec lui.

Dans la mesure ou un dessin vaut parfois mieux qu'un long discours, voici comment s'articule une solution :

Donc dans notre implémentation MVVM (Model View ViewModel), Le Model lui va englober d'une part nos entités d'objets (POCO - Plain Old CLR Objects) et d'autre part l'accès au données (Repository). La vue se trouve directement à la racine de notre projet et contient tout les éléments graphiques (XAML) et enfin notre couche ViewModel va servir à rassembler les informations nécessaires obtenues de la couche Model et à le mettre à disposition de la vue.

Voilà, j'ai volontairement essayé d'être le plus concis possible. Vous pouvez retrouver les sources ici. Et je vous dis à très vite un premier article.