I. Introduction▲
Avant toute chose, les pré-requis pour pouvoir reproduire ce qui est décrit dans le tutoriel sont les suivants :
- Le framework 3.5 doit être installé sur le serveur
- Vous disposez de MOSS ou WSS avec le SP1 installé
- Vous disposez de Visual Studio 2005 ou 2008
La plate-forme dotnet évoluant sans cesse et SharePoint étant entièrement basé sur celle-ci, il est important de comprendre comment intégrer les dernières évolutions technologiques. Une des évolutions apportées par le framework 3.5 est l'intégration native d'ASP.NET AJAX dans les DLL du framework.
Avant, vous deviez télécharger les extensions AJAX et les installer en plus du framework .NET. Nous allons voir comment configurer et utiliser ces librairies 3.5 au sein de composants SharePoint
II. Configuration de SharePoint▲
Pour commencer, il faut rendre SharePoint compatible avec le framework 3.5. Etant donné que Tobias Zimmergren a déjà entièrement décrit comment s'y prendre, je vous renvoie vers son blog qui explique en détail ce qui doit être ajouté dans le web.config de l'application SharePoint pour laquelle vous souhaitez utiliser 3.5.
Vous pouvez également télécharger la fonctionnalité développée par Renaud Comte permettant d'effectuer les modifications requises dans le web.config automatiquement. Elle s'intitule 3.5 Web.Config Feature (separate download) et permet d'ajouter les sections nécessaires dans le fichier web.config de l'application SharePoint que vous souhaitez rendre compatible.
Enfin, à toutes fins utiles, je vous ai joint un exemple de web.config fonctionnel et compatible avec 3.5. Vous le trouverez dans la section téléchargement.
III. Le ScriptManager et les pages maîtres▲
L'objectif du tutoriel n'est pas de vous apprendre comment utiliser l'AJAX en ASP.NET mais bien d'aborder tant que faire se peu les spécificités liées à SharePoint. Une de ces spécificités réside notamment dans l'utilisation des ScriptManager.
Comme vous le savez sans doute, une instance de l'objet ScriptManager est impérative pour pouvoir travailler en AJAX. Généralement, c'est d'ailleurs le premier contrôle qui doit être ajouté à une page. Dans SharePoint, les composants web sont toujours exécutés dans un contexte où une page maître est présente. En effet, qu'il s'agisse de WebParts, colonnes personnelles, web controls etc...tous ces composants sont encapsulés dans l'architecture SharePoint et bénéficient automatiquement de la présence de la page maître associée au site où ils sont exécutés.
Grâce ou à cause de cela, la déclaration du ScriptManager se fait généralement au sein de la page maître elle-même, comme ceci :
<
asp
:
ScriptManager
id
=
"ScriptManager1"
EnablePartialRendering
=
"TRUE"
runat
=
"server"
>
Au cas où vous ne bénéficiez pas d'une page maître avec un ScriptManager intégré (ce qui est le cas avec les pages maîtres standard), vous pouvez en déployer une vous-même. C'est ce que j'ai fait pour cet exemple, vous retrouverez une page maître qui sera ajoutée dans la galerie de pages maîtres lors de l'activation de la fonctionnalité liée au projet.
Vous pourrez donc vous inspirer de cela pour développer vos propres pages maîtres ou en modifier une existante avec SharePoint Designer par exemple.
IV. Les techniques d'intégration de l'AJAX dans les composants SharePoint▲
IV-A. Dans les WebParts, les contrôles web, les colonnes personnelles ▲
Il existe globalement deux techniques majeures pour intégrer de l'AJAX dans les WebParts :
- Dans le code-behind du contrôle lui même : cette technique a le désavantage de "mélanger" le code métier avec l'interface graphique si on n'y prend garde
- Dans un contrôle utilisateur ASP.NET : cette technique isole l'aspect graphique dans le contrôle utilisateur. Il faut ensuite charger celui-ci dans notre WebPart
Une déclinaison de la deuxième technique consiste à utiliser le composant de Jan Tielens, j'ai nommé le Return of SmartPart qui a été récemment mis à jour par son auteur.
IV-B. Dans les pages applicatives▲
Les pages applicatives étant liées à la page maître Application.master, l'insertion du ScriptManager peut-être rendue plus complexe car il n'est pas évident de personnaliser cette page maître. Ceci dit, trois choix s'offrent néanmoins à vous :
- Vous personnalisez Application.master : en ajoutant la déclaration du script manager mais en acceptant toutes les contraintes liées à la personnalisation de cette page (voir google).
- Vous délivrez votre propre page maître applicative : vous liez explicitement vos pages applicatives à votre page maître applicative personnelle
- Vous générez le code AJAX "à la main" :en fonction de l'environnement et de la complexité de la page, c'est une option envisageable. L'avantage d'ailleurs de cette technique est qu'elle ne nécessite aucune configuration spécifique au niveau du serveur.
IV-C. Dans les workflows▲
Les seuls composants nécessitant éventuellement de recourir à l'AJAX sont les formulaires d'association/initiation/modification des workflows. Si vous utilisez Forms Services, bonne nouvelle : l'AJAX y est intégré nativement sans que vous n'ayez rien à faire. Si vous utilisez des pages web classiques, vous pouvez soit utiliser le mode déclaratif d'ASP.NET, soit générer dynamiquement les contrôles dans le code-behind associé à la page.
V. Création d'un SPGridView avec de l'AJAX - Tout en Code-Behind▲
Dans la lignée de mon dernier article sur le SPGridView, voici un WebPart qui utilise un SPGridView avec de la pagination bénéficiant de l'AJAX pour éviter un full page postback lorsque l'on passe d'une page à l'autre.
Voici pas à pas comment il faut s'y prendre avec Visual Studio 2008, notez que vous pouvez exécuter les mêmes étapes avec Visual Studio 2005
Créer un nouveau projet de type Class Library
Ajoutez ensuite les références suivantes :
- System.Web => onglet dotnet
- System.Web.Extensions => onglet dotnet
- Microsoft.SharePoint => 12\ISAPI de SharePoint
Signez votre projet : Cliquez sur votre Projet => Propriétés => Onglet Signature => Signez le
Créez à présent une structure de répertoires et de fichiers comme illustré ci-dessous :
et renommez class1.cs en SPGridViewAjax.cs.
Voici à présent le contenu du fichier feature.xml :
<?xml version="1.0" encoding="utf-8"?>
<Feature
Id
=
"EE4B04A2-71A0-46E3-BA2A-783B0729DB3C"
Title
=
"SPGridView avec de l'AJAX"
Description
=
"SPGridView avec de l'AJAX"
Version
=
"1.0.0.0"
Hidden
=
"FALSE"
Scope
=
"Site"
xmlns
=
"http://schemas.microsoft.com/sharepoint/"
>
<ElementManifests>
<ElementManifest
Location
=
"webpart.xml"
/>
</ElementManifests>
</Feature>
Fichier classique définissant une fonctionnalité de collection et pointant vers le fichier webpart.xml
Contenu du fichier webpart.xml :
<?xml version="1.0" encoding="utf-8" ?>
<Elements
xmlns
=
"http://schemas.microsoft.com/sharepoint/"
>
<Module
Name
=
"WebParts"
List
=
"113"
Url
=
"_catalogs/wp"
RootWebOnly
=
"TRUE"
>
<File
Url
=
"DVP.SPGridViewAJAX.webpart"
Type
=
"GhostableInLibrary"
>
<Property
Name
=
"Group"
Value
=
"DVP - SPGridView"
></Property>
</File>
</Module>
</Elements>
Ce fichier indique qu'il faut déployer le WebPart dans la galerie de WebParts de la collection où la fonctionnalité est activée.
Contenu du fichier DVP.SPGridViewAJAX.webpart :
<?xml version="1.0" encoding="utf-8"?>
<webParts>
<webPart
xmlns
=
"http://schemas.microsoft.com/WebPart/v3"
>
<metaData>
<type
name
=
"signature"
/>
<importErrorMessage>
Cannot import this Web Part.</importErrorMessage>
</metaData>
<data>
<properties>
<property
name
=
"Title"
type
=
"string"
>
DVP.SPGridViewAJAX.webpart</property>
</properties>
</data>
</webPart>
</webParts>
Veillez à remplacer signature par votre signature que vous pouvez obtenir avec Reflector. Ce fichier indique simplement au système que le WebPart est lié au contrôle spécifié par la signature.
Reprenez à présent votre classe principale que vous avez nommé SPGridViewAjax.cs et faites-là dériver de System.Web.UI.WebControls.WebParts.WebPart. Ensuite, déclarez deux membres de classe comme suit :
SPGridView SimpleGrid =
null
;
UpdatePanel ControlPanel =
null
;
réécrivez à présent la méthode CreateChildControls()
protected
override
void
CreateChildControls
(
)
{
ScriptManager ScriptMgr =
ScriptManager.
GetCurrent
(
Page);
if
(
ScriptMgr ==
null
)
{
throw
new
ApplicationException
(
"Pas de script manager!"
);
}
ControlPanel =
new
UpdatePanel
(
);
ControlPanel.
UpdateMode =
UpdatePanelUpdateMode.
Conditional;
ControlPanel.
ID =
"ControlPanel"
;
Controls.
Add
(
ControlPanel);
SimpleGrid =
new
SPGridView
(
);
SPDataSource Donnees =
new
SPDataSource
(
);
Donnees.
List =
SPContext.
Current.
Web.
Lists[
"Documents"
];
Donnees.
Scope =
SPViewScope.
Recursive;
SimpleGrid.
AutoGenerateColumns =
false
;
BoundField ColonneID =
new
BoundField
(
);
ColonneID.
DataField =
"ID"
;
ColonneID.
HeaderText =
"ID"
;
SimpleGrid.
Columns.
Add
(
ColonneID);
BoundField ColonneTitre =
new
BoundField
(
);
ColonneTitre.
DataField =
"Title"
;
ColonneTitre.
HeaderText =
"Title"
;
SimpleGrid.
Columns.
Add
(
ColonneTitre);
SimpleGrid.
DataSource =
Donnees;
SimpleGrid.
AllowPaging =
true
;
SimpleGrid.
PageSize =
1
;
SimpleGrid.
PageIndexChanging +=
new
GridViewPageEventHandler
(
SimpleGrid_PageIndexChanging);
ControlPanel.
ContentTemplateContainer.
Controls.
Add
(
SimpleGrid);
SimpleGrid.
TemplateControl =
null
;
SimpleGrid.
PagerTemplate =
null
;
ScriptMgr.
RegisterAsyncPostBackControl
(
SimpleGrid);
base
.
CreateChildControls
(
);
}
Ce SPGridView est lié à la liste Documents du site courant et aux colonnes ID et Title. Vous devez éventuellement changer le nom de la liste pour que ça fonctionne chez vous et si vous êtes en français, remplacez Title par Titre...Tout ceci peut bien sûr être fait dynamiquement dans le code mais je préfère simplifier au maximum la lisibilité, c'est pourquoi il n'y a pas de gestion d'erreurs non plus.
Au niveau de l'AJAX, le code détecte d'abord si un ScriptManager est présent ou non dans le contexte courant et lance une exception si aucun ScriptManager n'est trouvé. Ensuite, un UpdatePanel est créé et le SPGridView lui est ajouté en tant que contrôle.
Le SPGridView étant un contrôle très sensible, il faut lui spécifier que nous ne développons pas de modèle de pagination via la ligne de code :
SimpleGrid.
PagerTemplate =
null
;
sauf si bien sûr vous souhaitez en créer un :). Complétons à présent avec les deux méthodes manquantes :
void
SimpleGrid_PageIndexChanging
(
object
sender,
GridViewPageEventArgs e)
{
SimpleGrid.
PageIndex =
e.
NewPageIndex;
SimpleGrid.
DataBind
(
);
ControlPanel.
Update
(
);
}
protected
override
void
OnPreRender
(
EventArgs e)
{
SimpleGrid.
DataBind
(
);
base
.
OnPreRender
(
e);
}
La méthode SimpleGrid_PageIndexChanging est appelée lors du click sur un numéro de page. Elle spécifie le nouvel index de page et raffraîchit l'UpdatePanel.
Le code complet est donc le suivant :
public
class
SPGridViewAjax :
System.
Web.
UI.
WebControls.
WebParts.
WebPart
{
SPGridView SimpleGrid =
null
;
UpdatePanel ControlPanel =
null
;
protected
override
void
CreateChildControls
(
)
{
ScriptManager ScriptMgr =
ScriptManager.
GetCurrent
(
Page);
if
(
ScriptMgr ==
null
)
{
throw
new
ApplicationException
(
"Pas de script manager!"
);
}
ControlPanel =
new
UpdatePanel
(
);
ControlPanel.
UpdateMode =
UpdatePanelUpdateMode.
Conditional;
ControlPanel.
ID =
"ControlPanel"
;
Controls.
Add
(
ControlPanel);
SimpleGrid =
new
SPGridView
(
);
SPDataSource Donnees =
new
SPDataSource
(
);
Donnees.
List =
SPContext.
Current.
Web.
Lists[
"Documents"
];
Donnees.
Scope =
SPViewScope.
Recursive;
SimpleGrid.
AutoGenerateColumns =
false
;
BoundField ColonneID =
new
BoundField
(
);
ColonneID.
DataField =
"ID"
;
ColonneID.
HeaderText =
"ID"
;
SimpleGrid.
Columns.
Add
(
ColonneID);
BoundField ColonneTitre =
new
BoundField
(
);
ColonneTitre.
DataField =
"Title"
;
ColonneTitre.
HeaderText =
"Title"
;
SimpleGrid.
Columns.
Add
(
ColonneTitre);
SimpleGrid.
DataSource =
Donnees;
SimpleGrid.
AllowPaging =
true
;
SimpleGrid.
PageSize =
1
;
SimpleGrid.
PageIndexChanging +=
new
GridViewPageEventHandler
(
SimpleGrid_PageIndexChanging);
ControlPanel.
ContentTemplateContainer.
Controls.
Add
(
SimpleGrid);
SimpleGrid.
TemplateControl =
null
;
SimpleGrid.
PagerTemplate =
null
;
ScriptMgr.
RegisterAsyncPostBackControl
(
SimpleGrid);
base
.
CreateChildControls
(
);
}
void
SimpleGrid_PageIndexChanging
(
object
sender,
GridViewPageEventArgs e)
{
SimpleGrid.
PageIndex =
e.
NewPageIndex;
SimpleGrid.
DataBind
(
);
ControlPanel.
Update
(
);
}
protected
override
void
OnPreRender
(
EventArgs e)
{
SimpleGrid.
DataBind
(
);
base
.
OnPreRender
(
e);
}
}
Le résultat ressemble à ceci :
avec une pagination en AJAX. Vous pouvez retrouver ce projet et une solution .wsp dans la section téléchargement.
Pour déployer le projet, je vous conseille d'utiliser l'add-in WSPBuilder pour Visual Studio
VI. Utilisation de l'AjaxControlToolkit dans un contrôle utilisateur▲
Pour illustrer l'utilisation de l'AjaxControlToolkit, j'ai décidé de reproduire l'exemple de Microsoft disponible ici. La seule différence est que cet exemple est dans un contexte web classique, je l'ai simplement remis dans le contexte de SharePoint.
Ajoutez ensuite les références suivantes :
- System.Web => onglet dotnet
- System.Web.Extensions => onglet dotnet
- AjaxControlToolkit
La DLL AjaxControlToolkit est disponible sur le site de l'éditeur.
Comme pour l'exemple précédent, créez un projet de type Class Library et nommez-le DVP.AjaxToolkit.Exemples. Créez ensuite la structure de répertoire et de fichier suivante :
Excluez toutefois les fichiers dvp.master et master.xml qui servent à déployer une page maître. Je les ai inclus dans le projet afin que vous puissiez plus facilement tester les WebParts. Ceci dit, je vous laisserai découvrir par vous même comment cela a été fait.
Les fichiers feature.xml, webpart.xml et AjaxToolkitDvp.webpart sont très similaires à ceux de l'exemple précédent. Il est donc inutile d'en réafficher le contenu. Concentrons nous plutôt sur ce qui change réellement, à savoir la création d'un contrôle utilisateur.
Dans la structure du projet et plus précisémment dans le répertoire CONTROLTEMPLATES/DvpAjaxToolkit, il y a le contrôle utilisateur dvpajaxtoolkit.ascx. Celui-ci contient principalement le code suivant :
<%
@ Control Language=
"C#"
Inherits=
"DVP.AjaxToolKit35.Exemples.AjaxToolkitExe....."
<
%@ Register Tagprefix=
"cc1"
Namespace=
"AjaxControlToolkit"
Assembly=
"AjaxControlToolkit...."
A savoir les directives déclarant l'utilisation du toolkit et le fait que la page dérive de AjaxToolkitExempleCodeBehind (que nous implémenterons plus tard). J'ai volontairement abrégé la déclaration des directives, vous pourrez les retrouver au complet en téléchargeant l'exemple.
En plus des directives, le contrôle contient une déclaration de style contenant toutes les classes CSS utilisées par le contrôle de rating.
<style type
=
"text/css"
>
.accordionHeader
{
border:
1
px solid
#2F4F4F
;
color:
white
;
background-color:
#2E4d7B
;
font-family:
Arial,
Sans-Serif
;
font-size:
12
px;
font-weight:
bold
;
padding:
5
px;
margin-top:
5
px;
cursor:
pointer
;
}
.accordionContent
{
background-color:
#D3DEEF
;
border:
1
px dashed
#2F4F4F
;
border-top:
none
;
padding:
5
px;
padding-top:
10
px;
}
.ratingStar
{
font-size:
0
pt;
width:
13
px;
height:
12
px;
margin:
0
px;
padding:
0
px;
cursor:
pointer
;
display:
block
;
background-repeat:
no-repeat
;
}
.filledRatingStar
{
background-image:
url(
PublishingImages/FilledStar.png
)
;
}
.emptyRatingStar
{
background-image:
url(
PublishingImages/EmptyStar.png
)
;
}
.savedRatingStar
{
background-image:
url(
PublishingImages/SavedStar.png
)
;
}
</style>
Notez qu'il est préférable d'inclure les styles dans un fichier CSS à part que l'on déploiera ensuite dans une style library.
Ensuite vient le plus important, à savoir la déclaration du contrôle de rating :
<asp:UpdatePanel ID
=
"UpdatePanel1"
runat
=
"server"
>
<ContentTemplate>
<div style
=
"float: left; width:230;"
>
How much do you like ASP.NET AJAX ?</div>
<cc1:Rating ID
=
"LikeRating"
runat
=
"server"
CurrentRating
=
"3"
MaxRating
=
"5"
StarCssClass
=
"ratingStar"
WaitingStarCssClass
=
"savedRatingStar"
FilledStarCssClass
=
"filledRatingStar"
EmptyStarCssClass
=
"emptyRatingStar"
OnChanged
=
"LikeRating_Changed"
style
=
"float: left;"
>
</cc1:Rating>
<br />
<div style
=
"clear:left;"
>
<br />
<asp:Button ID
=
"ButtonSubmit"
runat
=
"server"
Text
=
"Submit"
onclick
=
"ButtonSubmit_Click"
/><br /><br />
<asp:Label ID
=
"LabelResponse"
runat
=
"server"
Text
=
"[ No response provioded yet.]"
></asp:Label>
</div>
</ContentTemplate>
</asp:UpdatePanel>
Ici on a simplement la déclaration de l'UpdatePanel et du contrôle de Rating. C'est un copier-coller pur et simple de l'exemple proposé par Microsoft. Ce contrôle fait appel à la méthode LikeRating_Changed et le bouton de soumission appelle quant à lui la méthode ButtonSubmit_Click. Ces deux méthodes sont implémentées dans le code-behind du contrôle utilisateur. C'est la classe AjaxToolkitExempleCodeBehind dont voici le contenu :
public
class
AjaxToolkitExempleCodeBehind :
System.
Web.
UI.
UserControl
{
protected
Rating LikeRating;
protected
Label LabelResponse;
protected
void
LikeRating_Changed
(
object
sender,
AjaxControlToolkit.
RatingEventArgs e)
{
e.
CallbackResult =
"Upate done. Value = "
+
e.
Value +
" Tag = "
+
e.
Tag;
}
protected
void
ButtonSubmit_Click
(
object
sender,
EventArgs e)
{
string
howMuch =
"[unknown]"
;
switch
(
LikeRating.
CurrentRating)
{
case
1
:
howMuch =
"a bit."
;
break
;
case
2
:
howMuch =
"some."
;
break
;
case
3
:
howMuch =
"a fair bit."
;
break
;
case
4
:
howMuch =
"a lot."
;
break
;
case
5
:
howMuch =
"more than any thing."
;
break
;
}
LabelResponse.
Text =
"You like ASP.NET AJAX "
+
howMuch +
"."
;
}
}
Ici, la différence par rapport à l'exemple de Microsoft est que ce code est inclut dans une classe dérivant de UserControl. LikeRating et LabelResponse sont déclarés en protected et correspondent aux contrôles déclarés de manière déclarative...
Enfin, il ne reste plus à notre WebPart SharePoint qu'à charger ce contrôle utilisateur. C'est la classe WebPartPrincipal.cs qui se charge de cela :
public
class
WebPartPrincipal :
System.
Web.
UI.
WebControls.
WebParts.
WebPart
{
const
string
ControlFile =
@"/_controltemplates/dvpajaxtoolkit/dvpajaxtoolkit.ascx"
;
protected
override
void
CreateChildControls
(
)
{
ScriptManager ScriptMgr =
ScriptManager.
GetCurrent
(
Page);
if
(
ScriptMgr ==
null
)
{
throw
new
ApplicationException
(
"Pas de script manager"
);
}
else
if
(!
ScriptMgr.
EnablePartialRendering)
{
ScriptMgr.
EnablePartialRendering =
true
;
}
UserControl Ctrl =
Page.
LoadControl
(
ControlFile) as
UserControl;
Controls.
Add
(
Ctrl);
base
.
CreateChildControls
(
);
}
}
Le WebPart exécute le même test que pour l'exemple précédent pour détecter si un ScriptManager est bien présent ou non. Il fait également en sorte que la propriété EnablePartialRendering soit bien mise à TRUE sans quoi les changements ne seraient pas reflétés au sein du contrôle utilisateur.
Ensuite, le WebPart charge simplement le contrôle pointé par la constante ControlFile. Tout ceci donne le résultat suivant :
Pour déployer le projet, je vous conseille d'utiliser l'add-in WSPBuilder pour Visual Studio
VII. Tester les projets présents en téléchargement▲
Pour tester les projets proposés en téléchargement, commencez par déployer la solution DVP.AjaxToolKit35.Exemples.wsp via stsadm. Ensuite, après activation de la fonctionnalité de collection :
Vous devriez retrouver une nouvelle page maître que vous pouvez associer à votre site :
Ensuite, il ne vous reste plus qu'à ajouter le WebPart à une page :
Ensuite, vous pouvez déployer l'autre solution DVP.Ajax35.Exemples.wsp. Après activation de la fonctionnalité :
et ajouter le WebPart contenant le SPGridView avec pagination.
Veillez à le déployer dans un site où il existe une bibliothèque de documents nommées Documents avec une colonne Title. Sinon changez simplement le code et mettez le nom de la bibliothèque que vous voulez.
VIII. Téléchargement▲
Retrouvez les deux projets présentés dans le tutoriel ainsi que les solutions .wsp :
L'exemple avec le toolkit qui contient également la page maître
L'exemple qui contient le SPGridView avec de l'AJAX