Sébastien Pertus
Développement d'un Extender Ajax
L'optique de cet article est de prendre connaissance du framework ASP Ajax Extensions 1.0 ainsi que celui fourni par l'ATLAS Toolkit afin de voir les possibilités de customisation de contrôles qui vous sont alors offertes.
Par Sébastien Pertus publié le 02/04/2007 à 10:19, lu 4907 fois, 10 pages
 8 | Code Extender côté client
Le fichier ReadWriteTextBoxBehavior.js est déjà bien rempli de base. Grace à l'apport du Framework ASP.Net Ajax 1.0 et une mise en avant des concepts objet, on arrive à avoir un squelette de ce qui pourrait représenter une classe.
Le modèle adopté ici est la présentation de la classe sous forme de déclaration d'un prototype.
Si vous avez déjà fait du JavaScript mais jamais avec le Framework ASP.Net Ajax 1.0, ne soyez pas étonné de ne pas reconnaitre certaines fonctions.
Tout d'abord, le Framework ASP.Net Ajax 1.0, dans le souci de cohérence et de rapprochement avec ce que l'on connait en .Net, fournit un moyen de créer des Namespaces en JavaScript.
Certes l'écriture du code diffère mais le résultat se rapproche de ce à quoi peut servir un namespace :

Type.registerNamespace('ReadWriteTextBox');

Sans rentrer dans les détails, toute class JavaScript est définie comme une fonction et est déclarée comme tel. Si en l'on rajoute le concept de Namespace la ligne suivante ne devrait pas (trop) vous surprendre :

    ReadWriteTextBox.ReadWriteTextBoxBehavior = function(element) {

 

    ReadWriteTextBox.ReadWriteTextBoxBehavior.initializeBase(this, [element]);

 

    // TODO : (Step 1) Add your property variables here

    //

    this._myPropertyValue = null;

 

}

Nous avons là un premier élément de notre class. C'est ici que nous allons déclarer nos propriétés. Enfin, du moins les propriétés privées.En JavaScript, il n'existe pas de typage fort des données. On déclare directement la propriété et on y affecte une valeur par défaut.
Vous reconnaissez sans doute dans _myPropertyValue, quelque chose qui ressemble fort à ce que nous avions au départ dans notre class ReadWriteTextBoxExtender.cs (la propriété MyPropertyValue)
Nous allons la remplacer par notre booléen.
Note : Le nom de cette propriété n'est pas très importante, mais par convention, nous allons utiliser un « _ » pour signifier que nous avons à faire à une propriété privée, et du camelCasing pour le reste (JavaScript est basé entièrement sur du camelCasing, n'en déplaise ;) )
Nous allons donc avoir :

this._disableOnDblClick = null;

Note : la nomenclature de cette propriété privée n'est pas très importante. Il en sera autrement de l'accesseur (ou du moins son homologue JavaScript)
Dans notre Extender, nous allons créer à la volée une DIV qui recouvrira notre TextBox, nous avons donc besoin de déclarer cette DIV pour pouvoir travailler dessus.

// DOM Element de type DIV

this._divReadOnly = null;

De plus, nous allons interagir sur des évènements particuliers :
  1. Le Clic sur le Div qui masquera celui-ci pour faire apparaitre le TexBox
  2. Le LostFocus du Texbox qui produira l'effet inverse
  3. Le Double clic sur la Texbox qui désactivera le comportement global
Les développeurs JavaScript qui ont une certaine expérience sur le sujet savent que la gestion des évènements est très complexe, surtout si on veut que cela fonctionne sur tous les navigateurs.
C'est là, encore une fois, que le Framework ASP.Net Ajax 1.0 intervient en encapsulant cette complexité et en nous offrant un modèle homogène et très proche d'un modèle classique objet.
Un évènement c'est quoi ? C'est tout simplement un Handler et un Délégué.
Et bien soit, nous pouvons faire maintenant la même chose en JavaScript. Déclarons donc les Handlers :

// Handlers

this._textBoxBlurHandler = null;

this._textBoxDblClickHandler = null;

this._divClickHandler = null;

Nous avons terminé la déclaration de nos propriétés et nous pouvons nous attaquer au prototype : C'est ici que nous allons coder les fonctions de notre class JavaScript :
Il n'existe pas d'accesseurs au sens .Net comme on l'entend. Pour palier à ce problème, nous allons créer deux fonctions qui rempliront le rôle de Get et de Set d'un accesseur classique.
Ces deux fonctions doivent être nommées EXACTEMENT de la façon suivante, sinon la propriété DisableOnDblClick de votre Extender ne sera pas passée lors du rendu du contrôle :
  1. Pour le GET : « get_ » + « nomdelapropriete »
  2. Pour le SET : « set_ » + « nomdelapropriete »
Dans notre exemple, nous aurions donc :

get_ DisableOnDblClick : function() {

    return this._disableOnDblClick;

 },

 

 set_ DisableOnDblClick : function(value) {

    this._disableOnDblClick = value;

 },

Si vous en restez là, tout fonctionnera, mais, nous avons dit que la casse utilisée en JavaScript est basée sur le camelCasing.Nous voudrions donc quelque chose comme ceci :

 get_disableOnDblClick: function() {

    return this._disableOnDblClick;

},

 

set_disableOnDblClick: function(value) {

    this._disableOnDblClick = value;

},

 

Le problème c'est que nous ne respectons pas la nomenclature correcte pour le passage de la propriété de notre Extender.
Heureusement, il existe un attribut à déclarer sur la propriété de notre Extender C# pour palier à ce problème et respecter de chaque coté les conventions de nommage : L'attribut ClientPropertyName :

[ExtenderControlProperty]

[DefaultValue(true)]

[ClientPropertyName("disableOnDblClick")]

public Boolean DisableOnDblClick

{

    get

    {

        return GetPropertyValue("DisableOnDblClick", true);

    }

    set

    {

        SetPropertyValue("DisableOnDblClick", value);

    }

}

Nous devons créer notre DIV et la placer correctement :
La DIV est construite dans la fonction _initiateDiv(). :
Je vous laisse regarder le code superflu (notamment la récupération des styles par exemple) dans le projet source, pour m'attarder sur certains points :

   _initializeDiv : function(){

 

      var element = this.get_element();

 

      this._divReadOnly = document.createElement('div');

      this._divReadOnly.id = element.id + "_div";

      this._divReadOnly.innerHTML = element.value;

      this._divReadOnly.style.cursor = "pointer";

 

      // Récupération de la position et de la taille du TextBox

      var elementBounds = CommonToolkitScripts.getBounds(element);

 

      Sys.Debug.trace("Bounds : X = " + elementBounds.x + " Y = " + elementBounds.y + " Width = " +

elementBounds.width + " Height = " + elementBounds.height);          

 

      // Affectation des éléments taille et position au div

      CommonToolkitScripts.setBounds(this._divReadOnly, elementBounds);

 

      document.body.appendChild(this._divReadOnly);

 

  • Pour placer notre DIV, aussi faut il savoir où est placée notre TextBox. D'ailleurs comment faire pour récupérer un pointeur sur cette TextBox ???
    Tout simplement, grâce encore une fois au Framework ASP.Net Ajax 1.0 et l'adjonction de fonctionnalités de la Toolkit, vous avez une fonction toute simple qui récupère le contrôle étendu par votre Extender en JavaScript : this.get_element() ;
    Le retour de cette fonction est un objet DOM classique.
  • Plus compliqué : Récupérer la taille de notre TextBox et sa position ? Là on peut vite s'arracher les cheveux si on veut en plus être sûr que cela fonctionne sur tous les navigateurs. Mais encore une fois la ToolKit vous simplifie la vie : l'appel de la fonction CommonToolkitScripts.getBounds(element) vous récupère non seulement la dimension mais aussi la taille de votre élément DOM.
  • Évidemment si on est capable de récupérer la taille et la position d'un objet DOM, on est forcément également capable de lui affecter une taille et une position : C'est ce que fait la fonction CommonToolkitScripts.setBounds(element, elementBounds);
Bien, quelques remarques en vrac :
Où trouver de la documentation sur les diverses fonctions et possibilités de l'ATLAS Toolkit en terme de script JavaScript ? Pour le moment nulle-part.Le mieux reste encore l'exploration de la Toolkit elle-même. Je vous conseille une exploration approfondie du répertoire Common du projet AjaxControlToolkit.csproj pour vous donner une idée des possibilités offerte par le Framework de la Toolkit. Vous allez vite vous rendre compte que cela devient indispensable !
Vous aurez surement remarqué une ligne dans le code qui ressemble à du débuggage :

Sys.Debug.trace("Bounds : X = " + elementBounds.x + " Y = " + elementBounds.y +

    " Width = " + elementBounds.width + " Height = " + elementBounds.height);

Je vous conseille la lecture des tutoriaux d' ASP.Net Ajax 1.0 pour l'utilisation du mode débug pour le développement de vos contrôles :http://ajax.asp.net/docs/overview/ASPNETAJAXDebuggingAndTracingOverview.aspx
Pour chaque Handler déclaré, nous allons créer un délégué qui va pointer sur la bonne fonction.Un exemple de création de délégué grâce au Framework Ajax.Net Extensions :

this._divClickHandler = Function.createDelegate(this, this._onDivClick);

$addHandler(this._divReadOnly, "click", this._divClickHandler);   

Function.createDelegate : Crée le délégué qui pointe sur la fonction _onDivClick et l'affecte au Handler déclaré
$addHandler : Ajoute le Handler sur l'évènement clic de la DIV
Note : $addHandler est un raccourci vers Sys.UI.DomEvent.addHandler. D'une manière générale, une fonction notée avec un $ est la plupart du temps un raccourci
Il ne reste plus qu'à créer la fonction _onDivClick qui représente le coeur du métier

 _onDivClick : function(e) {

    /// <summary>

    /// Clic sur le Div

    /// </summary>

    /// <param name="e" type="Sys.UI.DomEvent">

    /// </param>

 

    Sys.Debug.trace("Click sur la Div");

 

    if (this._disableOnDblClick){

        return;

    }

 

 

    Sys.UI.DomElement.setVisible(this._divReadOnly, false);

    Sys.UI.DomElement.setVisible(this.get_element(), true);

 

    // Focus sur la zone de texte

    this.get_element().focus();

},

Nous avons vu jusqu'à présent qu'il est facile de faire transiter la valeur d'une propriété depuis votre Contrôle Serveur vers le Contrôle généré et sa class JavaScript.
Par contre nous ne savons pas comment faire pour refaire transiter cette valeur dans l'autre sens, lors d'un PostBack.
Encore une fois, et grâce à l'héritage de la classe Extender, vous avez accés à une propriété qui est persitante et qui permet de stocker des valeurs à récupérer dans un sens comme dans l'autre.
Il s'agit du ClientState.
Vous y accédez via le code Javascript de la manière suivante :

this._setClientState(value) ;

var __value = this._getClientState() ;

Dans notre exemple nous avons besoin de stocker la valeur de la propriété _disableOnDblClick
Il suffit pour cela de modifier par exemple l'accesseur de la propriété. Notre code devient donc :

set_disableOnDblClick : function(value) {

    this._disableOnDblClick = value;

 

    // Affectation de la valeur dans le ClientState, pour récupération lors du PostBack

    this.set_ClientState(value);

},

Il suffit maintenant de récupérer la valeur coté serveur. Il existe un évènement levé sur l'Extender lors du chargement du ClientState. Il suffit de s'y abonner et récupérer la donnée chargée. Attention toutefois, l'évènement n'est levé que si vous avez explicitement autorisé la gestion du ClientState dans votre contrôle.
Notre code évolue et devient donc :

public ReadWriteTextBoxExtender()

   {

    this.ClientStateValuesLoaded += ReadWriteTextBoxExtender_ClientStateValuesLoaded ;

    this.EnableClientState = true;

   }

 

   void ReadWriteTextBoxExtender_ClientStateValuesLoaded(object sender, EventArgs e)

   {

       string clientState = base.ClientState;

 

       if (clientState != null)

       {

           bool value = bool.Parse(clientState);

           this.DisableOnDblClick = value ;

       }

 

   }

 

Voilà nous avons pu récupérer la valeur de la propriété qui a évolué depuis le rendu du contrôle client !
Votre class JavaScript contient une fonction appelée dispose(). C'est ici que vous allez pouvoir libérer vos ressources afin d'optimiser le fonctionnement de votre contrôle :Important ici, veillez à supprimer les Handlers que vous avez créés lors de l'initialisation :

var element = this.get_element();

 

if(element) {

    $removeHandler(element, "blur", this._textBoxBlurHandler);

    $removeHandler(element, "dblclick", this._textBoxDblClickHandler);

}

 

if (this._divReadOnly)

{

    $removeHandler(this._divReadOnly, "click", this._divClickHandler);

}

 

// Handlers

this._textBoxBlurHandler = null;

this._textBoxDblClickHandler = null;

this._divClickHandler = null;

 
» Démarrer une discussion