Introduction
J'ai trouvé interessant l'idée du control UpdateProgress mais parfois c'est interessant de bloquer l'interaction de l'utilisateur. J'ai donc décider d'étendre les fonctioinalités du control pour créer un ModalUpdateProgress.
Prérequis
-
Visual studio 2008 beta 2 et le .NET framework 3.5 mais ca doit être réalisable avec .NET 2, Atlas et Visual Studio 2005
-
Connaissances en c#
-
Connaissance de base du Framework AJAX de Microsoft
Premièrement : Création de la solution
Il faut créer une solution et y inclure 2 projets, un site web (pour tester notre control) et une class library.
Cet article ne portera que sur la création du control, je supose que vous savez comment ajouter un control dans une page web...
Dans votre projet Class Library supprimer le fichier class1.cs et ajouter un fichier nommé ModalUpdateProgress.cs. À l'intérieur vous devriez y trouver le code suivant :
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
namespace ogulrok.tutoriel
{
class ModalUpdateProgress
{
}
}
Deuxièmement : Héritage
Faison hériter notre control ModalUpdateProgress de la class UpdateProgress présente dans le namespace System.Web.UI
Premièrement il faut ajouter une référence à System.Web et à System.Web.Extensions à notre projet.
1. Right-click sur le dossier référence et choisir Add Reference...
2. choisir System.Web et cliquer ok
3. Recommencer pour System.Web.Extensions
Ensuite ajoutons l'instruction suivante :
using System.Web.UI;
Finalement il faut ajouter l'héritage, nous ne voulons pas réinventer la roue, redéfinisons ToolboxData() et rendons notre class public, nous voulons pouvoir l'utiliser...
[ToolboxData("<{0}:ModalUpdateProgress runat=server>
</{0}:ModalUpdateProgress>")]
public class ModalUpdateProgress : UpdateProgress
Troixièmement : Le constructeur
Ici, je veux modifier la valeur par défaut de la propriété DisplayAfter du control de base pour ne pas avoir à attendre 500ms avant de voir mon control apparaitre, je vais la modifier dans le constructeur ainsi :
public ModalUpdateProgress() : base()
{
base.DisplayAfter = 0;
}
Comme vous le voyez, je fait appelle au constructeur de la superclass et je modifie la propriété DisplayAfter de la superclass, ainsi dans notre onglet propriété nous allons voir 0 d'affiché plutot que 500...
Quatrièmement : Le ProgressTemplate
Dans le control de base s'il n'y a pas de ProgressTemplate de définit, une exception est lancé. Je ne veux pas obliger l'utilisateur du control a créer un ProgressTemplate puisqu'il peut définir l'affichage dans une class CSS (voir plus loin).
Dans ce cas, nous allons créer une class EmptyProgressTemplate qui implémentera l'interface ITemplate ainsi :
public class EmptyProgressTemplate : ITemplate
{
#region ITemplate Members
public void InstantiateIn(System.Web.UI.Control container)
{
Literal l = new Literal();
l.Text = " ";
container.Controls.Add(l);
}
#endregion
}
Nous devons préalablement utiliser System.Web.UI.WebControls pour le Literal
using System.Web.UI.WebControls;
Comme vous le voyez, cette class ne fait qu'ajouter un control Literal " " au container, dans le cas présent, notre control.
Maintenant surchargeons la méthode OnPreRender pour y ajouter notre EmptyProgressTemplate si aucun ProgressTemplate n'a été définit par l'utilisateur.
protected override void OnPreRender(EventArgs e)
{
if (base.ProgressTemplate == null)
base.ProgressTemplate = new EmptyProgressTemplate();
base.OnPreRender(e);
}
L'instruction suivante : base.OnPreRender(e);, appelle la méthode de la superclass, elle fait la job à notre place :)
Cinquièmement : Les propriétés
J'ai décidé que mon control avait besoin de quelques propriétés pour que le control soit complet.
Utilisons le namespace System.ComponentModel pour pouvoir ajouter quelques attribut intéressant à notre control.
using System.ComponentModel;
Passons maintenant aux choses sérieuse, les propriétés :
PS : je n'utiliserai que le ViewState pour mes propriétés, vous pouvez modifier le code pour utiliser une autre méthode si nésséssaire.
PS2 : Les descriptions et les exceptions sont en anglais. L'anglais est plus universel que le français...
La première propriété est ClassName. Je veux que les utilisateurs de mon control puissent y ajouter une class CSS (qui n'est rien d'autre qu'un div en fait).
[Bindable(true)]
[Category("Appearance")]
[DefaultValue("blockScreen")]
[Localizable(true)]
[Description("Class name used to style this ModalUpdateProgress object.")]
public String ClassName
{
get
{
String _ClassName = (String)ViewState["ClassName"];
return ((_ClassName == null) ? "blockScreen" : _ClassName);
}
set
{
ViewState["ClassName"] = value;
}
}
La deuxième propriété sera OverrideStyles, car les styles pour créer le modal sont ajouter au div dans la méthode Render (voir plus loin). Je veux que les utilisateurs de mon control puissent changer mes styles s'il le désire...
[Bindable(true)]
[Category("Appearance")]
[DefaultValue(false)]
[Localizable(true)]
[Description("If true, no style will be added to the ModalUpdateProgress
object. You will need to define them into the class passed to the
ClassName property of the ModalUpdateProgress object.")]
public Boolean OverrideStyles
{
get
{
return (Boolean)(ViewState["OverrideStyles"] ?? false);
}
set
{
ViewState["OverrideStyles"] = value;
}
}
Ici l'opérateur "??" sert si ViewState["OverrideStyles"] est null, voir mon post s'intitulant
L'opérateur "??"
La dernière propriété est Opacity, je veux permettre au utilisateur de mon control de modifier l'opacité s'il le désire sans pour autant avoir à refaire tous les styles CSS du modal...
[Bindable(true)]
[Category("Appearance")]
[DefaultValue(90)]
[Localizable(true)]
[Description("Opacity of the ModalUpdateProgress object.")]
public int Opacity
{
get
{
return (int)(ViewState["Opacity"] ?? 90);
}
set
{
if (value >= 0 && value <= 100)
ViewState["Opacity"] = value;
else
throw new ArgumentOutOfRangeException("Opacity value must be
between 0 and 100.");
}
}
Cette propriété est également assez simple, l'opacité se calcule en poucentage donc doit être entre 0 et 100. Si ce n'est pas le cas, une exception est lancé.
Dernièrement : Redéfinition de Render
La méthode Render de la class de base ne fait pas ce que nous désirons, elle ne fait qu'afficher le texte du ProgressTemplate là où le control est placé...
Redéfinissons la méthode Render :
protected override void Render(HtmlTextWriter writer)
Pour que l'action soit exécuter lors d'une opération AJAX, nous devons register les scripts déja créer dans le Framework de Microsoft.
ScriptManager scriptManager = ScriptManager.GetCurrent(this.Page);
if (!this.DesignMode)
scriptManager.RegisterScriptDescriptors(this);
Premièrement nous créons une référence sur le control ScriptManager, qui est le pilier du framework AJAX de Microsoft. Ensuite si nous ne somme pas en mode design, nous utilisons la méthode RegisterScriptDescriptors du ScriptManager.
La superclass possède 2 méthodes qui seront utilisé par le ScriptManager : GetScriptDescriptors() et GetScriptReferences(), 2 itérateurs présent dans l'interface IScriptControl (ce qui ne fait pas partie du présent tutoriel).
Ensuite nous allons utiliser le HtmlTextWriter pour créer notre control.
Ajoutons les attributs "id" et "class" :
writer.AddAttribute("id", this.UniqueID);
if (this.ClassName != String.Empty)
writer.AddAttribute("class", this.ClassName);
Ajoutons maintenant les styles du modal si la propriété OverrideStyles n'est pas activé :
if (!this.OverrideStyles)
{
writer.AddStyleAttribute("top", "0px");
writer.AddStyleAttribute("left", "0px");
writer.AddStyleAttribute("width", "100%");
writer.AddStyleAttribute("height", "100%");
writer.AddStyleAttribute("position", "fixed");
}
Il reste l'opacité qui sera ajouté seulement si elle n'est pas à 100%, car par défaut elle est à 100% :)
if (this.Opacity < 100)
{
writer.AddStyleAttribute("filter",
String.Format("alpha(opacity={0})", this.Opacity));
writer.AddStyleAttribute("-moz-opacity",
String.Format(".{0}", this.Opacity));
writer.AddStyleAttribute("opacity",
String.Format("{0}", this.Opacity));
}
Remarque : on peut changer "opacity" par :
writer.AddStyleAttribute("opacity", this.Opacity.ToString());
Mais bon j'ai fait du copier/coller...
Ajoutons maintenant le dernier style, qui servira à controler l'affichage de notre modal : display, qui sera à none lors de son initialisation.
writer.AddStyleAttribute("display", "none");
Tous ceci est presque fini, il ne reste qu'à créer le div et les controls contenu dans notre fameux ProgressTemplate :
writer.RenderBeginTag("div");
base.RenderChildren(writer);
writer.RenderEndTag();
Et voilà, nous avons un control ModalUpdateProgress...
Il reste qu'a tester le tout...
CSS
Voici la class CSS que j'ai utilisé. Pour un rendu plus dynamique j'ai utilisé comme image de fond un gif animé, une sorte de progress bar :
div.blockScreen
{
background-image: url('/WebSiteTest1/images/loading.gif');
background-color: #D9DDE6;
background-repeat: no-repeat;
background-attachment: fixed;
background-position: 50% 50%;
}
Je ne suis pas un "crack" en CSS alors j'aurais probablement pu faire mieux, mais bon...
Conclusion
Je ne sais plus trop quoi dire autre que : voici un control, facile a utilisé et flexible, créé en seulement quelque minutes...
J'espère que ce tutoriel c'est avéré interessant et instructif.