Realizzare un custom extender AJAX con ASP.NET 3.5

di Marco De Sanctis, in ASP.NET 3.5,

Con il rilascio del .NET Framework 3.5, ASP.NET AJAX ha raggiunto la piena maturità, divenendo parte integrante della tecnologia ASP.NET. Certamente nei circa due anni trascorsi dal rilascio delle prime CTP il numero di utilizzatori è aumentato esponenzialmente e con ogni probabilità una buona fetta di questo successo può essere attribuita alla semplicità di utilizzo e di migrazione di applicazioni preesistenti verso AJAX. Spesso basta infatti avvalersi di qualche UpdatePanel per vedere il nostro sito web liberarsi dal flicker tipico dei postback e iniziare a dialogare con il Web Server a colpi di chiamate XmlHttp, aggiornando poi automaticamente la propria interfaccia utente.

ASP.NET AJAX non è però solo UpdatePanel; in particolare una caratteristica estremamente interessante è costituita dalla possibilità di estendere i WebControls di ASP.NET, dotandoli di funzionalità client tramite l'utilizzo degli ExtenderControl. Si tratta a tutti gli effetti di controlli server che però, invece di produrre markup HTML in maniera autonoma, emettono codice javascript in grado di aggiungere comportamenti (behavior) ad un elemento target.

Chi ha utilizzato AjaxControlToolkit nelle proprie applicazioni, ha sicuramente avuto modo di sperimentare la versatilità di questo tipo di soluzione architetturale: da un lato, infatti, uno stesso Extender può essere associato a controlli eterogenei, rendendolo di fatto un'astrazione di un comportamento lato client; dall'altro è possibile collegare diversi Extender allo stesso oggetto, raggiungendo risultati complessi semplicemente combinandone gli effetti. Il tutto è ovviamente incapsulato all'interno di un tradizionale server control, con il vantaggio di poter essere utilizzato anche da uno sviluppatore con poca o nessuna esperienza in ambito di Javascript; basta infatti scrivere qualcosa di simile a:

Data: <asp:TextBox runat="server" ID="txtDateTime" />
<img src="calendar.png" id="btnCalendar" />
<ajax:MaskedEditExtender runat="server" id="mskDateTime" TargetControlId="txtDateTime" MaskType="Date" Mask="99/99/9999" />
<ajax:CalendarExtender runat="server" ID="calendar" PopupButtonID="btnCalendar" TargetControlID="txtDateTime" />

per avere un controllo DateBox a tutti gli effetti, dotato di calendario a scomparsa e digitazione controllata dei caratteri (vedi figura 1).

Figura 1
Figura 1 - L'effetto del CalendarExtender associato ad un controllo TextBox

Il progetto sulla carta

Supponiamo allora di voler dotare le nostre pagine di RichTooltip, in grado di visualizzare markup HTML (e quindi immagini, testo formattato, tabelle e quant'altro) in luogo del classico testo piano: un utilizzo tipico potrebbe essere, ad esempio, in una pagina di catalogo di un sito e-commerce, in cui l'utente possa visualizzare una foto e una descrizione estesa del prodotto semplicemente spostando il puntatore del mouse sopra alla sua voce nell'elenco. D'altro canto un simile controllo potrebbe anche essere utile per visualizzare un breve help in linea sulle funzionalità associate ad un determinato pulsante. Si tratta insomma di un tipico caso in cui optare per controlli ad-hoc non è una scelta saggia, perchè ci costringerebbe a realizzarne uno per ogni tipologia di oggetto da supportare (RichTooltipButton, RichTooltipLabel, ecc.). E' molto meglio invece costruire un RichTooltipExtender, che sarà nativamente associabile ad un insieme eterogeneo di controlli target.

La tooltip standard del controllo target sarà sostituita da un DIV (presente in pagina), che dovrà essere reso visibile o meno in base agli spostamenti del mouse e il cui posizionamento dipenderà dalle coordinate del puntatore. Da un punto di vista funzionale, la nostra tooltip può essere modellata come una macchina a stati, il cui comportamento si articola secondo le tre fasi sintetizzate dal diagramma riportato nella figura 2.

Figura 2
Figura 2 - Diagramma degli stati di RichTooltip

1) Inizialmente la tooltip è in stato Out e non risulta graficamente visibile. Questo può essere insomma inteso come una sorta di stato di stand-by, che può evolvere nello stato Showing quando il puntatore del mouse viene posizionato al di sopra dell'elemento target.

2) In fase di Showing la tooltip è ancora invisibile e l'evoluzione verso lo stato successivo è regolata da un timer che viene resettato ad ogni movimento del mouse sopra l'area del controllo. Nel caso invece il puntatore fuoriesca da quest'ultima, la macchina a stati deve riposizionarsi su Out.

3) In fase di Showed la tooltip può essere finalmente visualizzata, almeno per un periodo di tempo scandito da un secondo timer. Alla scadenza di questo o nel caso in cui l'utente muova il puntatore all'esterno dell'elemento target, la tooltip viene di nuovo resa invisibile e lo stato resettato ad Out.

Primi passi nel mondo della programmazione OO in Javascript

Realizzare un CustomExtender richiede sostanzialmente due ingredienti:

  • la scrittura di una o più classi Javascript, dette behavior, che incapsulino tutta la logica lato client;
  • la realizzazione di un Server Control, che potrà essere inserito nelle pagine aspx e avrà il compito di iniettare questi script nel browser, impostandone opportunamente tutti i parametri necessari per il corretto funzionamento.

ASP.NET AJAX reca con sé una libreria Javascript (la Client Script Library) così potente e versatile da poter essere considerata un vero e proprio framework lato client, introducendo fra l'altro concetti come namespace, interfacce, ereditarietà o enumerati; essa espone la classe Sys.UI.Behavior da cui tutti i behavior dovranno ereditare. Nel nostro caso, lo scheletro del codice di TooltipBehavior sarà grossomodo simile al seguente:

// questa direttiva serve a Visual Studio 2008
// per fornire funzionalità di intellisense
// durante la scrittura del codice
/// <reference name="MicrosoftAjax.js"/>

// registrazione del namespace
Type.registerNamespace('AspitaliaExtender');

// costruttore
AspitaliaExtender.TooltipBehavior= function(element) {
  AspitaliaExtender.TooltipBehavior.initializeBase(this, [element]);

  // codice custom qui
}

// prototipo
AspitaliaExtender.TooltipBehavior.prototype = {
   initialize: function() {
    AspitaliaExtender.TooltipBehavior.callBaseMethod(this, 'initialize');

    // codice custom qui
  },

  dispose: function() {
    // codice custom qui

    AspitaliaExtender.TooltipBehavior.callBaseMethod(this, 'dispose');
  }
}

AspitaliaExtender.TooltipBehavior.registerClass('AspitaliaExtender.TooltipBehavior', Sys.UI.Behavior);

if (typeof(Sys) !== 'undefined')
  Sys.Application.notifyScriptLoaded();

Sebbene privo di logica funzionale, lo snippet precedente offre comunque già diversi spunti di riflessione, data anche la sintassi sicuramente un po' particolare. Subito a valle della dichiarazione del namespace, si nota come il concetto di classe in Javascript corrisponde alla definizione del suo costruttore. Nel caso di un behavior, esso deve accettare come argomento l'istanza dell'elemento target del DOM, passandola poi al costruttore della classe base. Il passo successivo è poi quello di definirne il prototipo, che consiste nell'insieme di funzioni che saranno invocabili in ogni istanza di TooltipBehavior. Nel codice precedente, in particolare, trovano posto gli override dei due metodi initialize e dispose che, per il momento, si limitano a richiamare le rispettive implementazioni della classe base. Una volta completato il prototipo, è necessario registrare il nuovo tipo creato, specificandone anche l'ereditarietà da Sys.UI.Behavior, e successivamente comunicare all'infrastruttura che l'intero script è stato completamente caricato.

4 pagine in totale: 1 2 3 4

Attenzione: Questo articolo contiene un allegato.

Contenuti dell'articolo

Commenti

Visualizza/aggiungi commenti

| Condividi su: Twitter, Facebook, LinkedIn

Per inserire un commento, devi avere un account.

Fai il login e torna a questa pagina, oppure registrati alla nostra community.

Approfondimenti