Centre de développement Sharepoint 2010
Sharepoint 2010 - MSDN
Tutoriels Sharepoint 2010 sur areaprog
Developper Top 10 Resource Center | Sharepoint
Sharepoint 2010 fournit une API REST (WCF Data Services aka ADO.Net Data Services aka Astoria :) ) permettant d’accéder et de requêter directement les données d’un site Sharepoint.
Si par-exemple vous tentez d’accéder à une de vos listes: http://<votresite>/_vti_bin/ListData.svc/<votreliste> vous risquez de rencontrer cette erreur :
Impossible de charger le type System.Data.Services.Providers.IDataServiceUpdateProvider …
Pour résoudre le problème, installez le patch suivant selon votre OS:
ADO.NET Data Services Update for .NET Framework 3.5 SP1 for Windows 7 and Windows Server 2008 R2
ou
ADO.NET Data Services Update for .NET Framework 3.5 SP1 for Windows 2000, Windows Server 2003, Windows XP, Windows Vista and Windows Server 2008
N’oubliez pas de rebooter…
Voici la seconde partie de l’article. Vous trouverez la première partie ici : Développer un client Silverlight pour Sharepoint 2010 en appliquant le pattern MVVM (1/2)
Ainsi que les sources complètes:
Nous avons créé une ListBox classique Silverlight, mis en place tous les éléments de l’architecture MVVM ainsi que l’accès aux données dans Sharepoint. Malgré tout, l’application ne fonctionne pas encore tout à fait correctement.
Dans cette seconde partie, nous verrons: les opérations à effectuer sur les classes pour que le binding fonctionne correctement l’écueil classique lors d’un développement Silverlight/WPF dans un contexte multithread comment passer d’un rendu habituel de liste à quelque chose de plus…original :) grâce à Silverlight et xaml
Pour que le binding fonctionne, la classe cible du binding – la couche ViewModel en MVVM - doit implémenter un mécanisme de notification à savoir l’interface INotifyPropertyChanged. Cette interface contient un événement qui doit être levé chaque fois que le binding d’une propriété de la classe doit être réévalué. Notre classe WinesListVM va donc implémenter INotifyPropertyChanged et l’événement sera levé chaque fois que l’on affectera les propriétés SelectedItem et WineItems de cette classe. Voilà comment cela se présente:
Dans le fichier WinesListVM.cs, ajoutez la référence au namespace suivante :
using System.ComponentModel;
Implémentez INotifyPropertyChanged pour la classe WinesListVM:
public class WinesListVM : INotifyPropertyChanged
Cliquez droit sur INotifyPropertyChanged et sélectionnez “Implement Interface”. Visual Studio ajoute pour vous les membres de l’interface à implémenter, à savoir ici un événement PropertyChanged.
public event PropertyChangedEventHandler PropertyChanged;
Ajoutez la méthode suivante qui permet de déclencher l’événement dont le paramètre est le nom de la propriété modifiée (dans notre cas ce sera “SelectedItem” ou “WineItems”). Le moteur de binding actualisera tous les éléments de l’UI qui sont bindés à cette propriété.
protected void OnPropertyChanged(string name)
{
PropertyChangedEventHandler handler = PropertyChanged;
if (handler != null)
handler(this, new PropertyChangedEventArgs(name));
}
Il reste ensuite à lever l’événement, chaque fois que l’on affecte la propriéte “SelectedItem” et “WineItems”:
IEnumerable<Wine> _wineItems;
public IEnumerable<Wine> WineItems
get
return _wineItems;
set
if (_wineItems != value)
_wineItems = value;
OnPropertyChanged("WineItems");
Wine _selectedItem;
public Wine SelectedItem
return _selectedItem;
if (_selectedItem != value)
_selectedItem = value;
OnPropertyChanged("SelectedItem");
Voici le code complet de la classe WinesListVM :
using System;
using Wines.SL.DAL;
using System.Collections.Generic;
using Wines.SL.Model;
using System.Threading;
namespace Wines.SL.ViewModel
public WinesListVM(IWinesDAL winesDal)
winesDal.WineItemsUpdated += ((s, e) => WineItems = e.WineItems);
A présent, chaque fois que l’on modifie la propriété “SelectedItem” du ViewModel, l’élément sélectionné dans la vue sera synchronisé avec cette propriété. De la même manière, chaque fois que l’on rechargera la liste des vins et que l’on réaffectera la propriété WineItems, la contenu de liste xaml sera rafraichie à l’écran avec les nouvelles données.
Relançons l’application : F5….boum une exception UnauthorizedAccessException : Accès inter-threads non valide : mais courage c’est la dernière étape….
Souvenez-vous, dans la DAL, nous accédons aux données de manière asynchrone. Les données provenant de Sharepoint arrivent donc dans une callback par un thread dédié. Que ce soit en Winform, en WPF, en Silverlight…en C# ou même en VB, seul le thread qui a créé le contrôle graphique peut en modifier ses propriétés. Or dans notre cas, si l’on déroule le chemin d’exécution c’est le thread qui exécute le code de la callback dans la DAL qui au final va modifier la propriété ItemsSource du contrôle List de l’UI Silverlight.
Il faut donc passer la main au thread principal à un moment ou un autre dans la chaine pour que ce soit lui qui exécute le code de réévaluation du binding. Puisque c’est la DAL qui effectue un appel asynchrone, c’est elle qui va prendre la responsabilité de se remettre dans le thread principal. Et voici comment procéder en utilisant le SynchronizationContext (la même opération peut être effectuée avec Dispatcher.BeginInvoke):
Déclarons un objet de type SynchronizationContext dans la classe WinesFromSP, que l’on initialize de la manière suivante (sans oublier de référencer le namespace System.Threading):
SynchronizationContext _syncCtxt = SynchronizationContext.Current;
Puis on encapsule la levée de l’évenement par la méthode Post du SynchronizationContext. Le code situé dans le corps de la clause Post sera exécuté dans le thread principal dès que celui-ci en aura la possibilité, ce qui règle notre problème.
_syncCtxt.Post(unused => WineItemsUpdated(this, new WineItemsUpdatedEventArgs(WineItems)), null);
voici le code complété:
using Wines.SL.Model; // Pour utilisation de la couche Model
using Microsoft.SharePoint.Client; // Référence au ClientObjectModel
using System.Collections.Generic; // Pour les Generics
using System.Linq;
using System.Threading; // Pour Linq
namespace Wines.SL.DAL
public class WinesFromSP : IWinesDAL
// Contexte de connexion au serveur
ClientContext _ctx = new ClientContext(@"http://stephe:2828/MySite");
// La liste des vins résultante
ListItemCollection _wines;
// Pour notifier l'update des éléments
public event EventHandler<WineItemsUpdatedEventArgs> WineItemsUpdated;
public void RequestFromServer()
// Récupère la liste "Wines"
var wineList = _ctx.Web.Lists.GetByTitle("Wines");
// Récupère les éléments de la liste
_wines = wineList.GetItems(new CamlQuery());
// Charge les éléments de la liste
_ctx.Load(_wines);
// Exécute la requête asynchrone
_ctx.ExecuteQueryAsync(onWineElemsLoaded, onWineElemsError);
public IEnumerable<Wine> WineItems { get; set; }
private void onWineElemsLoaded(object sender, ClientRequestSucceededEventArgs args)
// Succes
// _wines.AsEnumerable() pour repasser en Linq To Object [using System.Linq]
WineItems = _wines.AsEnumerable().Select(w => new Wine()
Name = w["Name"].ToString(),
Count = Convert.ToUInt16(w["Count"])
});
if (WineItemsUpdated != null)
private void onWineElemsError(object sender, ClientRequestFailedEventArgs args)
// Traitement sur erreur
On relance l’application et cette fois-ci on peut voir apparaître notre liste de vins.
Remarquez que grâce au binding du SelectedItem, si vous mettez un point d’arrêt sur le setter de la propriété SelectedItem du ViewModel, on s’y arrête chaque fois que l’on change d’élément sélectionné dans la liste.
En Silverlight comme en WPF, le choix d’un contrôle s’effectue par-rapport au rôle fonctionnel que l’on souhaite lui attribuer. Le rôle fonctionnel d’une ListBox est de contenir un ensemble d’éléments ainsi qu’un élément courant sélectionné.
Ainsi nous allons déclarer un contrôle de type “ListBox” dans notre vue, non pas parce qu’elle afficherait une liste au sens où les éléments sont placés les uns en dessous des autres, mais parce que le ViewModel qui lui est associé comprend les 2 fonctionnalités “ensemble d’éléments” (WineItems) et “élément sélectionné” (SelectedItem).
Pour avoir le rendu ci-dessous, il faudra effectuer les opérations suivantes: modifier la position d’affichage des éléments de la liste : plutôt que de les placer les uns au-dessus des autres, on les positionne de manière aléatoire à l’écran modifier l’aspect visuel d’un élément de la liste : au lieu du titre de l’élément, on affiche un cercle de diamètre = à la quantité de bouteilles disponibles + son titre
Le résultat attendu peut être codé de différentes manières en xaml (utilisation de styles, de templates, …). Pour cet exemple j’ai préféré utiliser un code qui fonctionne aussi bien en Silverlight qu’en WPF.
Par défaut, les éléments d’une ListBox sont affichés dans un conteneur (ListBox.ItemsPanel) de type StackPanel ce qui est parfait si l’on veut disposer les éléments horizontalement ou verticalement les uns par-rapport aux autres. Dans notre cas, on souhaite positionner chaque élément à des coordonnées qui lui sont propres. C’est ce que permet le conteneur de type Canvas.
Dans le fichier WinesListView.xaml, complétez le code de la ListBox de la manière suivante :
<Grid x:Name="LayoutRoot">
<ListBox ItemsSource="{Binding WineItems}" SelectedItem="{Binding SelectedItem, Mode=TwoWay}">
<ListBox.ItemsPanel>
<ItemsPanelTemplate>
<Canvas Background="DarkSeaGreen" >
</Canvas>
</ItemsPanelTemplate>
</ListBox.ItemsPanel>
</ListBox>
</Grid>
Relancez l’application (F5) : vous verrez à présent tous les éléments se positionner sur le bord gauche du Canvas. En effet, aucune directive de placement n’a été effectuée pour l’instant:
Redéfinissons à présent l’ItemTemplate de la ListBox pour pouvoir modifier les coordonnées et le visuel d’un élément de la liste. Pour l’instant, nous y mettons simplement une zone de texte.
<ListBox.ItemTemplate>
<DataTemplate>
<TextBlock Text="{Binding}"
FontSize="9" FontWeight="Bold" FontStyle="Italic"
Foreground="Brown"
HorizontalAlignment="Center" VerticalAlignment="Center"
Opacity="0.6">
<TextBlock.RenderTransform>
<TranslateTransform X="{Binding Converter={StaticResource ElemToPositionConverter}}" Y="{Binding Converter={StaticResource ElemToPositionConverter}}" />
</TextBlock.RenderTransform>
</TextBlock>
</DataTemplate>
</ListBox.ItemTemplate>
<!-- Modification du type de conteneur de l'ensemble des éléments de la ListBox -->
Nous effectuerons une translation du TextBlock dans le Canvas en bindant x (Canvas.Left) et y (Canvas.Top) avec l’élément de la liste en appliquant un converter. Le converter nous permet d’effectuer les ajustements entre ce que nous propose le ViewModel et le rendu que l’on souhaite obtenir dans la View. Le converter sera appelé chaque fois que le binding de l’élément concerné sera réévalué (ici 1 seule fois lors du chargement). Dans notre cas, nous allons récupérer un élément de type Wine en entrée du converter et nous souhaitons renvoyer un nombre aléatoire qui représentera un nombre de pixels. Le paramètre d’entrée du converter ne nous sert à rien. Mais on aurait très bien pu décider de positionner les éléments en fonction de la nom ou de leur quantité.
Créez un nouveau répertoire “Converters” dans le répertoire “View” du projet. Créez-y une nouvelle classe “ElemToPositionConverter".cs”. Copiez-y le code suivant:
using System.Windows.Data;
namespace Wines.SL.View.Converters
public class ElemToPositionConverter : IValueConverter
static Random _rand = new Random();
public object Convert(object value, Type targetType, object parameter, System.Globalization.CultureInfo culture)
return _rand.Next(30, 250);
public object ConvertBack(object value, Type targetType, object parameter, System.Globalization.CultureInfo culture)
throw new NotImplementedException();
Un converter doit implémenter l’interface IValueConverter et dans notre cas, seul le sens de conversion Source vers UI sera à définir. Nous renvoyons simplement un nombre aléatoire entre 30 et 250 qui correspondra au positionnement x et y en pixels de chacun de nos éléments sur l’écran. Dans du code xaml, l’utilisation d’un converter s’effectue en l’instanciant dans les resources de la View. On peut ensuite l’utiliser dans les contrôles en le référençant comme une StaticResource.
Voici le résultat, remarquez qu’à chaque exécution, les éléments se placent différemment.
Ajoutons à présent les cercles dont le diamètre représente la quantité de bouteilles. Chaque élément de la liste ne sera plus uniquement représenté par un TextBlock, mais par un TextBlock surmonté d’une Ellipse. On a vu tout à l’heure, que le contrôle qui permet contenir des éléments les uns en dessous des autres est un StackPanel. Nous allons donc placer le TextBlock existant ainsi qu’une Ellipse dans un contrôle StackPanel:
<navigation:Page x:Class="Wines.SL.View.WinesListView"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
mc:Ignorable="d"
xmlns:navigation="clr-namespace:System.Windows.Controls;assembly=System.Windows.Controls.Navigation"
xmlns:cnv="clr-namespace:Wines.SL.View.Converters"
d:DesignWidth="640" d:DesignHeight="480"
Title="MainView Page">
<navigation:Page.Resources>
<cnv:ElemToPositionConverter x:Key="ElemToPositionConverter" />
</navigation:Page.Resources>
<StackPanel>
<StackPanel.RenderTransform>
</StackPanel.RenderTransform>
<Ellipse x:Name="Circle"
Width="{Binding Count}"
Height="{Binding Count}">
<Ellipse.Fill>
<SolidColorBrush
Color="Azure"
Opacity="0.6"/>
</Ellipse.Fill>
</Ellipse>
<TextBlock
Text="{Binding}"
</StackPanel>
</navigation:Page>
Remarquez que la translation Canvas.Left et Canvas.Top a été déplacé dans le StackPanel pour que l’ellipse ainsi que le texte profitent du placement aléatoire.
Nous avons terminé !! Cet article est assez long car très détaillé, mais si vous téléchargez les sources de la solution, vous constaterez qu’elle contient finalement très peu de lignes de code.
Cet article décrit comment réaliser une application Silverlight qui présente la liste externe “Wines” (Sharepoint 2010 Dev Partie 1 : Création d’une liste externe basée sur des données stockées dans SqlServer) dans une forme plus ludique, et en appliquant le pattern Model-View-ViewModel. Nous verrons notamment un moyen d’intégrer les requêtes asynchrones du Client Object Model dans un modèle en couche, grâce aux événements C#. Vous pouvez suivre cet article en utilisant n’importe quelle liste Sharepoint, du moment qu’elle contient une colonne de type texte et une colonne de type numérique Je précise que le pattern MVVM n’a aucun rapport avec le rendu visuel de la liste…vous pouvez donc sauter directement à la deuxième partie de l’article pour la partie xaml
Téléchargez les sources complètes :
Ce premier article, abordera les notions suivantes: Création d’une application Silverlight Mise en place de l’architecture MVVM Utilisation du Client Object Model de Sharepoint 2010 Gestion de notifications asynchrones dans une architecture MVVM
Dans le deuxième article ici, nous verrons: l’implémentation de INotifyPropertyChanged par les classes du ViewModel pour que le binding fonctionne correctement l’écueil classique lors d’un développement Silverlight/WPF dans un contexte multithread comment passer d’un rendu habituel de liste à quelque chose de plus…original :) grâce à Silverlight et xaml
Pour rappel, voici notre liste “Wines” dans Sharepoint:
Et voici maintenant le rendu de notre liste Silverlight à la fin de l’article. Chaque vin est représenté par un cercle dont le diamètre est fonction de la quantité de bouteilles. Je conviens que ce n’est pas forcément très joli et je suis sûre que vous ferez beaucoup mieux…mais ça a le mérite d’être très différent de la version “Liste” de Sharepoint :)
Grâce à un client Silverlight pour Sharepoint 2010, on pourra facilement : obtenir un rendu graphique élaboré accéder à des ressources externes pour intégrer d’autres sources d‘information accéder aux données de Sharepoint grâce au Client Object Model choisir d’héberger l’application Silverlight comme une WebPart dans Sharepoint, ou l’héberger directement dans IIS pour l’utiliser directement
Pour plus d’information sur le développement et la technologie Silverlight : Introduction à Silverlight
Grâce à MVVM on pourra facilement : changer la provenance et les moyens d’accès à la source de données sans remettre en question le code métier ou de présentation ajouter de nouvelles fonctionnalités facilement changer la partie visuelle sans toucher au code métier faciliter la mise en oeuvre des tests
Pour plus d’informations sur MVVM :WPF Apps With The Model-View-ViewModel Design Pattern et aussi http://japf.developpez.com/tutoriels/dotnet/mvvm-pour-des-applications-wpf-bien-architecturees-et-testables/
Lancez Visual Studio 2010 en mode Administrateur. Créer un nouveau projet de type Application Silverlight et appelez-le Wines.SL
Laissez les valeurs par défaut dans l’écran suivant :
La solution contient 2 projets :
Wines.SL : qui contient le code métier et les pages Silverlight Wines.SL.Web : qui contient les éléments nécessaires au chargement et au déploiement de l’application
Pour pouvoir exécuter les applications Silverlight durant la phase de codage et de débuggage, Visual Studio utilise un hébergement custom. Nous pourrons donc voir directement le résultat dans un navigateur, comme si l’application était hébergée dans IIS. On pourra ensuite uploader le .xap dans Sharepoint, pour pouvoir l’afficher dans une WebPart ou la déployer dans IIS. Ce point fera l’objet d’un article ultérieur.
Quand l’application Silverlight s’exécute dans une WebPart de Sharepoint, il n’y a pas de problématique Cross-Domain car l’hébergement est identique. Dans notre cas par-contre, il faut autoriser l’application Silverlight à utiliser les services Web du serveur Sharepoint en déployant le fichier “clientaccesspolicy.xml” suivant à la racine du serveur Sharepoint (C:\inetpub\wwwroot\wss\VirtualDirectories\2828) :
<?xml version="1.0" encoding="utf-8" ?>
<!-- This file must be put on the Server from where a Silverlight client gets infomration (e.g. SharePoint Site) -->
<access-policy>
<cross-domain-access>
<policy>
<allow-from http-request-headers="*">
<domain uri="*"/>
</allow-from>
<grant-to>
<resource include-subpaths="true" path="/"/>
</grant-to>
</policy>
</cross-domain-access>
</access-policy>
Plus d’informations : Communications inter-domaines
MVVM (Model-View-ViewModel) est une architecture multi-tiers qui permet de découpler la couche Vue (View) de la couche Métier (Model) à travers la couche Cas d’utilisation (ViewModel).
Le ViewModel remplit 2 rôles :
il présente des éléments provenant du model et d’autres ViewModel, pour présenter un ensemble d’informations pertinentes pour un cas d’utilisation fonctionnel de l’application – et donc pour une ou plusieurs Views. il implémente les interfaces et propriétés adaptées à l’usage d’une technologie de binding comme Silverlight ou WPF
[La dénomination “Cas d’utilisation” pour le View Model est un terme qui m’est propre et n’engage donc que moi, mais je trouve qu’il correspond bien à la fonction de cette couche].
En pratique, la couche View accède à la couche ViewModel par l’intermédiaire du binding.
Un des intérêts de ce modèle est de localiser tout le code d’accès à la logique métier par la vue dans les classes du ViewModel, et ainsi de ne pas avoir de code behind dans les fichiers .cs des vues. Ainsi, toute la logique d’accès aux métier est située dans le ViewModel et seul le XAML (et les converters) perdurent côté View. Un autre avantage indéniable est de faciliter les tests qui sont alors pratiqués sur la couche ViewModel. Et bien sûr, comme toutes les architectures multi-tiers, il permet de découpler la couche d’accès aux données, le modèle, les cas d’utilisation et l’UI.
Je ne vais pas m’étendre davantage sur ce pattern dans cet article. Pour plus d’informations sur l’architecture MVVM, je vous recommande l’article du MSDN magazine suivant, avec les sources d’un cas pratique :WPF Apps With The Model-View-ViewModel Design Pattern et aussi http://japf.developpez.com/tutoriels/dotnet/mvvm-pour-des-applications-wpf-bien-architecturees-et-testables/ (en français :) )
En règle générale, on prépare un répertoire pour chaque couche dans la solution. Créez 3 répertoires View, Model et ViewModel dans le projet Wines.SL (click droit sur le projet –> Ajouter –> Nouveau Répertoire).
Nous allons définir la classe qui représente 1 élément de la liste des vins. Elle proposera autant de propriétés que de colonnes de la liste Sharepoint. Ajoutez une classe “Wine.cs” dans le répertoire “Model”et définissez les attributs suivants: Name, Count et ToString()
using System.Net;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Documents;
using System.Windows.Ink;
using System.Windows.Input;
using System.Windows.Media;
using System.Windows.Media.Animation;
using System.Windows.Shapes;
namespace Wines.SL.Model
public class Wine
// = la colonne Name de la liste SP
public string Name { get; set; }
// = la colonne Count dans la liste SP
public UInt16 Count { get; set; }
// Affichage par défaut de l'élément
public override string ToString()
return Name;
L’accès aux données dans Sharepoint fait l’objet d’une nouvelle couche “Accès aux données” (Data Access Layer) dans notre solution. En effet, le fait d’accéder aux informations à travers Sharepoint n’est en aucun cas une contrainte du modèle lui-même (qui n’est finalement qu’un IEnumerable<Wine>), mais de la manière dont on a décidé de stocker les données. On trouvera dans la répertoire DAL, les classes qui constituent les différentes manières d’accéder aux informations métier (une seule dans cet article : une liste SharePoint via le Client Object Model).
Créez un répertoire DAL dans le projet Wines.SL. Ajoutez une classe “WinesFromSP.cs” dans le répertoire “DAL” et ajoutez les références suivantes dans le fichier .cs :
using System.Linq; // Pour Linq
Ajoutez une méthode “RequestFromServer” qui se chargera de récupérer les données depuis Sharepoint et les présentera comme un ensemble d’éléments de type Wine dans une propriété WineItems.
public class WinesFromSP
Le Client Object Model (CSOM) est une nouveauté de Sharepoint 2010. C’est une librairie de classes qui permet facilement d’accéder aux objets Sharepoint côté client et évite ainsi d’avoir directement recours aux Web Services de Sharepoint. Le contexte du CSOM va retenir vos opérations de requêtage pour les envoyer en une seule fois au serveur et ainsi optimiser la communication entre le client Silverlight et le serveur Sharepoint. Par défaut, le CSOM fonctionne dans le contexte de l’utilisateur de la session Windows. Assurez-vous donc que les droits sur la liste soient attribués à l’utilisateur avec lequel vous avez ouvert la session Windows pour faire fonctionner l’application.
Pour pouvoir utiliser le CSOM, il faut référencer les assemblies suivantes qui se trouvent dans "C:\Program Files\Common Files\Microsoft Shared\Web Server Extensions\14\TEMPLATE\LAYOUTS\ClientBin":
Ce code permet de récupérer la liste des vins. Complétez la classe “WinesFromSP” comme ci-dessous:
On commence par instancier le contexte client en spécifiant le site concerné. Ce contexte sera enrichi par nos requêtes et demandes de chargement jusqu’à ce que l’on envoie effectivement la demande au serveur.
ClientContext _ctx = new ClientContext(@http://stephe:2828/MySite);
On spécifie dans le contexte que l’on souhaite utiliser la liste “Wines”.
var wineList = ctx.Web.Lists.GetByTitle("Wines");
On demande ensuite à récupérer tous les éléments de la liste wines. SP Linq (Linq To Sharepoint) ne fonctionne que côté serveur, il restera donc des instructions CAML dans le code client.
Il faut ensuite charger les éléments que vous souhaitez obtenir en réponse du serveur par Load.
La requête est ensuite envoyée et exécutée côté serveur de manière asynchrone par l'appel de ExecuteQueryAsync. Ceci implique que les éléments récupérés dans _wines seront exploitables dans la callback onWineElemsLoaded. Il n’y a donc pas de communication avec le serveur avant l’appel de ExecuteQueryAsync.
Je vous recommande la lecture de l’article Introduction au Client Object Model qui détaille les modalités de requêtage et l’utilisation du Client Object Model ainsi que Sharepoint Guidance 2010 qui contient tout un chapitre très détaillé sur le CSOM.
Le schéma suivant qui synthétise les étapes du requêtage est extrait de SP2010 Guidance:
A présent, nous ajoutons un mécanisme de notification accessible facilement de l’extérieur de la classe : un événement WineItemsUpdated qui renverra la liste des vins et auquel le ViewModel pourra s’abonner pour être notifié du rafraichissement des données une propriété WineItems qui représente la dernière liste des vins chargée
Nous verrons tout à l’heure comment l’événement sera utilisé par la couche ViewModel pour être notifiée et permettre ainsi à la vue d’être actualisée via le binding.
Pour rester dans les règles de l’art concernant l’événement WineItemsUpdated : la signature doit être de type EventHandler<T> le 1er paramètre du delegate est l’émetteur de l’événement le second paramètre est l’information <T> à faire passer, qui doit hériter de la classe système EventArgs
Ajoutez le fichier “WineItemsUpdatedEventArgs.cs” dans le répertoire DAL:
public class WineItemsUpdatedEventArgs : EventArgs
public WineItemsUpdatedEventArgs(IEnumerable<Wine> wineItems)
WineItems = wineItems;
get;
private set;
Intéressons-nous au code de la callback onWineElemsLoaded :
// wines.AsEnumerable() pour repasser en Linq To Object [using System.Linq]
if(WineItemsUpdated != null)
WineItemsUpdated(this, new WineItemsUpdatedEventArgs(WineItems));
On récupère la liste d’éléments renvoyés par le serveur. Puis pour chaque élément w de “wines”, on projette une instance de la classe métier “Wine”. La clause Select renvoit un type IEnumerable<Wine> (car dans notre cas, le corps du Select renvoit un type “Wine”) que l’on affecte à la variable WineItems.
Plutôt que d’expliquer longuement la projection à l’aide de Select, je vais l’écrire sans la syntaxe Linq, et tout sera dit ou presque :
WineItems = new List<Wine>();
foreach (var w in wines)
WineItems.Add(new Wine()
{ Name = w["Name"].ToString(),
Count = Convert.ToUInt16(w["Count"]) });
Le ViewModel va s’abonner à l’événement WineItemsUpdated du modèle et va présenter la liste des vins à la couche vue.
Pour cela, créons une classe WinesListVM.cs dans le répertoire ViewModel du projet. Pour s’abonner à l’évenement du modèle, cette classe doit connaître son type. Or, quand on y pense, tout ce qui intéresse le ViewModel, c’est d’être notifié chaque fois que le liste des vins est réactualisée et de récupérer cette liste à jour comme un IEnumerable<Wine>. Mais le fait que les données proviennent de Sharepoint, cela ne la concerne pas : pour être plus précis, WinesListVM n’a pas besoin de connaître le type concrêt WinesFromSP défini dans notre DAL. Elle pourrait très bien s’abonner à une classe qui récupère la liste des vins en provenance de SqlServer, ou d’un fichier : le code du ViewModel serait le même.
Pour apporter plus de souplesse, nous allons définir la signature de ce qu’est un modèle, au regard du ViewModel : une interface qui présente un événement dont le delegate a pour argument un WineItemsUpdatedEventArgs.
Nous retournons donc dans le répertoire DAL et y ajoutons un fichier IWinesDAL.cs contenant une interface qui comporte un événement : le même que celui défini dans WinesFromSP
public interface IWinesDAL
event EventHandler<WineItemsUpdatedEventArgs> WineItemsUpdated;
Notre classe WinesFromSP respecte cette signature et elle implémente donc cette interface comme ceci:
{ …
A présent, nous pouvons retourner dans le code du ViewModel, dans la classe WinesListVM. Le constructeur de cette classe prendra en paramètre un type IWinesDAL, c’est à dire n’importe quelle classe qui implémente cette interface, et donc qui respecte la signature de l’événement WinesItemsUpdated. Le viewModel peut ensuite s’abonner à cet événement pour être notifié des changements et récupérer la liste à jour.
Dans le ViewModel, on ajoute également la propriété WineItems ainsi que SelectedItems pour récupérer l’élément sélectionné dans la vue.
public class WinesListVM
set;
Le code d’abonnement à l’événement
est comparable au code ci-dessous. Il a été simplifié à l’aide d’une expression lambda pour le delegate:
winesDal.WineItemsUpdated += new EventHandler<WineItemsUpdatedEventArgs>(winesDal_WineItemsUpdated);
void winesDal_WineItemsUpdated(object sender, WineItemsUpdatedEventArgs e)
WineItems = e.WineItems;
Nous allons présenter les vins sous forme de cercles dont la taille dépend du nombre de bouteilles. Les cercles seront placés aléatoirement dans la fenêtre. Dans le répertoire “View”, ajoutez une nouvelle Page Silverlight et appelez-le WinesListView.xaml. Nous allons commencer par un rendu “normal de liste” et nous changerons l’aspect visuel par la suite. Copiez le code suivant dans votre fichier WinesListView.xaml :
Remarquez que l’on binde la propriété WineItems du ViewModel à l’ItemsSource de la ListBox et le SelectedItem du ViewModel à la propriété SelectedItem de la ListBox…sauf que pour l’instant, rien ne relie le ViewModel à la vue et c’est ce à quoi nous allons remédier maintenant.
Ouvrez le fichier App.xaml.cs, et placez-vous sur Application_Startup(). Voici le code qui a été auto-généré pour ce fichier, lors de la création du projet.
public partial class App : Application
public App()
this.Startup += this.Application_Startup;
this.Exit += this.Application_Exit;
this.UnhandledException += this.Application_UnhandledException;
InitializeComponent();
private void Application_Startup(object sender, StartupEventArgs e)
this.RootVisual = new MainPage();
private void Application_Exit(object sender, EventArgs e)
Ajoutez les références aux namespaces :
using Wines.SL.View;
using Wines.SL.ViewModel;
Modifiez le code de Application_Startup de la manière suivante, pour instancier notre vue WinesListView plutôt que la page par défaut qui est créée lors de la création d’un projet Silverlight:
WinesDALFromSP dal = new WinesFromSP();
WinesListVM vm = new WinesListVM(dal);
dal.RequestFromServer();
this.RootVisual = new WinesListView() { DataContext = vm };
Reprenons le code de Application_Startup ligne par ligne. C’est dans cette méthode que l’on va lier les différentes couches de notre architecture: La première ligne permet d’instancier la classe qui communique avec SP côté serveur L’instance dal est ensuite passée en paramètre du ViewModel dans la 2ème ligne (relie le ViewModel au Model) La 3ème ligne permet de déclencher le chargement de la liste des vins. La 4ème ligne permet d’instancier la View et de binder le DataContext de cette vue au ViewModel. Nous verrons dans l’étape suivante comment le DataContext se comportera dans la vue. (relie la View au ViewModel)
Il est intéressant de connecter nos couches de manière “externe” (dans du code qui ne fait partie d’aucune des couches, comme App.xaml.cs) c’est à dire qu’aucune couche n’instancie explicitement des classes appartenant à d’autres couches. Ainsi, non seulement nous avons l’avantage de centraliser le code de rattachement entre les couches, mais les couches sont découplées au maximum (on pourrait aller plus loin et faire “sauter” ce code de liaison entre les couches en utilisant un mécanisme d’injection de dépendance comme MEF ou Unity…mais c’est un autre sujet). Il existe également des frameworks MVVM qui effectueront ces opérations pour vous : instanciation des ViewModels et liaison entre les couches.
Revenons sur le code de la View:
Plus haut, nous avons initialisé la propriété DataContext de la Page avec notre ViewModel par le code:
this.RootVisual = new MainView() { DataContext = vm };
En Silverlight (et en WPF), les DataContext des contrôles héritent de celui de leur parent. Ainsi, notre Grid et notre ListBox utilisent le même DataContext que celui qui a été affecté à MainView, à savoir “vm”. Lorsque l’on binde une propriété, le binding s’effectue dans le contexte du DataContext. En clair : dans notre cas lorsque l’on binde la propriété “ItemsSource” de la ListBox sur “WineItems”, cela revient à binder “ListBox.ItemsSource” sur “vm.WineItems”. Lorsque l’on binde , la propriété “SelectedItem” de la ListBox sur “SelectedItem” cela revient à binder “ListBox.SelectedItem” sur “vm.SelectedItem”.
Le binding permettra le rafraichissement automatique des propriétés des contrôles, chaque fois que les éléments bindés sont modifiés, et inversement pour les binding 2 ways. Le but de cet article n’étant pas de réexpliquer le fonctionnement du binding et la syntaxe xaml, je vous encourage à suivre les tutoriaux du MSDN ou de voir des Webcasts sur le sujet si cela vous semble flou.
Pour récapituler, voici un schéma simpliste de la communication entre les différents éléments, pour pouvoir afficher la liste des vins:
Les différentes couches sont reliées, pourquoi ne pourrait-on faire fonctionner l’application dès maintenant ? Tapez F5 pour lancer l’application et vous obtiendrez…une jolie page blanche. Pas de panique c’est normal…il reste quelques éléments à mettre en place…
Deuxième partie de l’article ici….
Une mine d’informations détaillées pour bien comprendre les nouveautés de Sharepoint 2010 côté Développeurs (et une version Pdf disponible).
http://spg.codeplex.com/