M'è capitato di sentir parlare dell'entity designer di LINQ come "uno dei tanti inutili designer" di Visual Studio. Non sono un amante dei designer, e quando posso li evito. Ad esempio, per quanto ben fatto sia il designer delle WebForm mi capita di usarlo molto di rado per il semplice fatto che di solito scrivere il codice a mano mi è più rapido.

Ed è proprio questo il punto... il designer deve essere uno strumento che aiuta a risparmiare tempo e non a complicare la vita, perciò mentre un designer come quello per le WebForms ha un utilità relativamente bassa, quello di LINQ è pressochè indispensabile. Per quanto sia possibile scriversi a mano le classi di Linq To SQL chi ci abbia provato si sarà sicuramente reso conto che si tratta di un lavoro improbo e ripetitivo. Non si tratta infatti semplicemente di creare proprietà e metodi, ma anche implementare le interfaccie INotifyPropertyChanged e INotifyPropertyChanging che implicano i dover intervenire sulle proprietà una ad una. Questa implementazione è resa obbligatoria dal fatto che l'object tracking si basa su queste interfacce e senza di esse non sarà in grado di garantire il corretto funzionamento perdendo una delle caratteristiche più importanti di LINQ.

L'uso del designer consente di risparmiare moltissimo tempo e per questo motivo è sicuramente opportuno usarlo.

Technorati Tag: ,

Non tutti sanno che alcuni controlli di ASP.NET supportano l'event bubbling anche se probabilmente almeno una volta l'hanno usato senza rendersene conto. In particolare mi riferisco a Repeater, DataList, GridView e alla recentissima e splendida ListView. Si tratta di controlli molto importanti che consentono di presentare i dati con estrema flessibilità e l'uso del bubbling consente di migliorarli ulteriormente.

L'event bubbling è un meccanismo per cui gli eventi generati da un controllo vengono "trasmetti" al controllo immediatamente superiore nella gerarchia. Il padre per intenderci. La gridview ad esempio si avvale di questo meccanismo per praticamente qualunque controllo contenuto, sia esso un pulsante di "edit" piuttosto che il pulsante di paginazione o una colonna di sorting. Tutti questi che vi ho nominato sono controlli che implementano IButtonControl e di conseguenza espongono l'evento Command. E' proprio all'interno di questo comando che viene dato il via al bubbling che si concretizza nell'esecuzione del CommandName con eventuale CommandArgument da parte della grid e in seguito l'evento ItemCommand che tutti conosciamo. Perciò se inserite un bottone in qualuque posizione nella gridview e assegnate ad esso il CommandName "Sort" e CommandArgument una sortExpression valida, la gridview in risposta ordinerà la colonna prescelta.

Volendo è possibile estendere questo meccanismo anche ad altri controlli che nativamente non espongono l'evento Command e non supportano l'event bubbling.  E' il caso della DropDownList. Poniamo ad esempio di voler creare una DropDownList che consente di selezionare la colonna di ordinamento (ascendente o discendente). La normale DropDownList che conosciamo non è sufficiente e ci costringerebbe a scrivere del codice per la gestione. Ma se la estendiamo come segue possiamo piegarla alla nostra volontà:

   1: public class CommandDropDownList : DropDownList
   2: {
   3:     /// <summary>
   4:     /// Occurs when this control raise a Command.
   5:     /// </summary>
   6:     public event CommandEventHandler Command;
   7:  
   8:     /// <summary>
   9:     /// Gets or sets the name of the command.
  10:     /// </summary>
  11:     /// <value>The name of the command.</value>
  12:     public string CommandName { get; set; }
  13:  
  14:     /// <summary>
  15:     /// Raises the <see cref="E:System.Web.UI.WebControls.ListControl.SelectedIndexChanged"/> event. This allows you to provide a custom handler for the event.
  16:     /// </summary>
  17:     /// <param name="e">A <see cref="T:System.EventArgs"/> that contains the event data.</param>
  18:     protected override void OnSelectedIndexChanged(EventArgs e)
  19:     {
  20:         CommandEventHandler handler = this.Command;
  21:  
  22:         if (handler != null)
  23:         {
  24:             CommandEventArgs args = 
  25:                 new CommandEventArgs(this.CommandName, this.SelectedValue);
  26:  
  27:             handler(this, args);
  28:             this.RaiseBubbleEvent(this, args);
  29:         }
  30:  
  31:         base.OnSelectedIndexChanged(e);
  32:     }
  33: }

La CommandDropDownList espone le proprietà e l'evento che la rendono simile ad un qualsiasi pulsante e solleva il Command al SelectedIndexChanged; Tuttavia occorre l'aggiunta dell'istruzione RaiseBubbleEvent() che è quella che appunto inizia l'event bubbling notificandolo al controllo superiore.

A questo punto pssiamo definire una CommandDropDownList e specificare come CommandName "Sort" e il nome della colonna seguito da ASC o DESC per la direzione di ordinamento. Se la mettiamo nella gridview con AutoPostBack="True" ecco che magicamente al cambiare della selezione verrà cambiata la colonna di ordinamento.


In un post molto lungo, come normale per Scott Guthrie, quest'oggi è stato delineati il futuro che ci aspetta dopo il rilascio del Framework 3.5. Gli argomenti sono talmente tanti che l'unica soluzione pratica sarebbe quella di tradurlo nella sua totalità perciò vedodi fare un semplice elenco dei punti salienti:

  • A breve saranno rilasciati i sorgenti del Framework 3.5 così come promesso in precedenza.

Over the next few months we'll be delivering a series of additional products that build on top of this VS 2008 and .NET 3.5 foundation, and make .NET development even better. 

  • La prossima settimana sarà disponibile in download la preview delle ASP.NET 3.5 Extensions che aggiungeranno una mole di cambiamenti notevole, come il tanto discusso MVC Framework e gli ADO.NET Data Services (già "Astoria").

VS 2008 and .NET 3.5 include a ton of new features for ASP.NET development.  We are planning to deliver even more ASP.NET functionality next year with a "ASP.NET 3.5 Extensions" release.  The first public preview of this will be available for download next week on the web.

  • Si preannuncia il rilascio di Silverlight 2.0 in versione beta-golive nel primo quarto del 2008. Evidentemente in Microsoft si sono resi conto che il salto tecnologico dalla 1.0 era talmente ampio da meritare una major release.

Previously we've been referring to this .NET-enabled Silverlight release as "Silverlight V1.1".  After stepping back and looking at all the new features in it (the above list is only a subset - there are many more we aren't sharing yet), we've realized that calling it a point release doesn't really reflect the true nature of it.  Consequently we have decided to change the name and refer to it as "Silverlight V2.0" going forward.

  • Nella prima parte dell'anno sarà rilasciato Windows Server 2008 e con esso naturalmente Internet Information Server 7.0 che finora abbiamo potuto apprezzare solamente in Windows Vista.

Questi i punti a mio parere più importanti, per il resto vi invito a leggere il post di Scott nella sua totalità

Link:


Mi è appena giunta comunicazione che le iscrizioni al nostro One Day sono state aperte. ricordo che l'evento si terrà presso il Novotel di Mestre, lungo la tangenziale, il 17 Gennaio 2008 dalle 9:00 alle 16:00 circa.

Durante l'incontro prenderanno parte in qualità di speaker Pietro Brambati (Microsoft), Davide Venole (MVP ASP.NET), Andrea Boschin (MVP ASP.NET) , Andrea Dottor (Senior Developer) e Davide Senatore (Consultant & Contractor).

Invito tutti a iscriversi in massa il più presto possibile usando il link sottostante.

Link:

Technorati Tag: , , ,


Scrivere codice in Javascript richiede anche il sobbarcarsi l'onere di contenere le dimensioni di quanto si sta scrivendo. Sarebbe fantastico poter scrivere classi su classi, strutturare al meglio l'applicazione, seguire delle prolisse convenzioni di naming, però alla fine quello con cui sempre dobbiamo fare i conti è la banda e la quantità di dati da trasferire che se sottovalutata potrebbe rendere inutilizzabili le nostre applicazioni.

Qualche volta però basta un po' ingegnaarsi per riuscire a risparmiare un po' di codice e ottenere un risultato più che accettabile. A me ad esempio è successo esponendo eventi custom su dei controlli client derivati da Sys.UI.Control. Quando si espone un event capita spesso di dover allegare ad esso una classe Sys.EventArgs per fornire all'handler notifica di dettagli dell'evento. Nauralmente è sempre possibile estendere Sys.EventArgs per fornire delle proprietà aggiuntive e i relativi metodi get e set tuttavia questo comporta usare parecchie righe di codice la cui utilità è piuttosto limitata.

Utilizzando expando però è molto semplice fare un po' di economia. Grazie ad esso possiamo facilmente "attaccare" proprietà, metodi e quant'altro a oggetti già esistenti.

... var args = new Sys.EventArgs(); args.myCustomData = "dati..."; ...

Inutile creare una classe che estenda EventArgs. E' sufficiente appendere ad essa le proprietà che ci servono e risparmieremo un bel po' di bytes.

Technorati tags: , ,

Da quando ho iniziato a lavorare con ASP.NET e maggiormente oggi, quando ASP.NET AJAX è ormai prepotentemente entrato nelle mie applicazioni, mi sono spesso scontrato con un problema che credo che molti altri abbiano  riscontrato e affrontato nei modi più disparati. Il problema in questione deriva dalla modalità con cui i server controls di ASP.NET generano gli id degli elementi che poi vengono resi nella pagina. Questi identificatori, che tipicamente corrispondono almeno nella parte finale all'id che viene utilizzato lato server, quando arrivano al client sono spesso arricchiti di informazioni allo scopo di renderli realmente univoci all'interno del DOM html. Mi riferisco a quello che noi tutti conosciamo con il nome di ClientID, che spesso e volentieri ci costringe a generare dei piccoli pezzetti di codice javascript per riuscire ad operare su di essi come segue:

 

var myControl = document.getElementById('<%= myControl.ClientID %>');

Personalmente ho sempre trovato questa operazione stucchevole e inutilmente laboriosa e finalmente, con l'avvento di AJAX e della Microsoft AJAX Library mi sono deciso a metter insieme una serie di idee e a trovare una soluzione dignitosa. La soluzione in realtà riprende ed estende un che ho fatto poco tempo fa. Il principio che allora proposi era quello di creare una sorta di client-codebehind in Javascript che permettesse di incapsulare e in qualche modo meglio organizzare tutto il codice che siamo abituati a scrivere per attivare sul client le nostre pagine. E' stato allora che ho realizzato che tale modello con un passo ulteriore poteva diventare una valida soluzione al problema dei ClientID. Creando infatti una classe base per questo codice e iniettando al suo interno i riferimenti Javascript ai controlli, il modello di programmazione client-side diventa pressochè analogo a quello del server liberandoci così dei voli pindarici cui siamo ormai assuefatti.

Per ottenere questo modello, ho pensato di estendere sua signoria lo ScriptManager. Dato che è proprio di codice di Script che stiamo parlando mi è sembrato il punto naturale per dare vita alla mia soluzione. Inoltre, avendo comunque bisogno di un riferimento alla Microsoft AJAX Library l'uso dello ScriptManager diviene pressochè obbligatorio. Ecco quindi che ho creato un ExtendedScriptManager che cela al suo interno le seguenti cose:

  1. Una collection di ClientReference che consente di specificare quali dei controlli server-side è necessario portare sul client. Questo è dovuto innanzitutto alla scelta di indicare esplicitamente tali controlli per evitare l'inutile proliferazione del codice di script oltre alla necessità di gestire diverse modalità di referenziazione atte a gestire diverse tipologie di controlli e diverse esigenze.
  2. Una proprietà PageClass che indica quale classe istanziare, inserita in un apposito file js referenziarto per rappresentare il codebehind della pagina. Questa classe dovrà estendere la classe Javascript Elite.Page inclusa nell'assembly e referenziata automaticamente dal codice dell'ExtendedScriptManager.

Inoltre ho dovuto estendere anche lo ScriptManagerProxy per mantenere questo tipo di modello anche nei casi in cui si faccia uso di questo controllo. Lo script manager esteso in mancanza di una PageClass si comporta in modo del tutto analogo ad uno ScriptManager classico perciò è possibile usarlo in tutta tranquillità in una masterpage e impostare la PageClass solo nello ExtendedScriptManagerProxy ove ve ne sia la necessità.

L'impostazione della PageClass fa sì che lo ExtendedScriptManager produca qualche riga di codice javascript, legata all'onload dell'applicazione, che si occupa di istanziare la classe specificata e di iniettare al suo interno tutte le ClientReference esplicitate. Ecco un breve esempio di pagina che fa uso del mio modello:

 

<%@ Page Language="C#" AutoEventWireup="true" CodeBehind="Default.aspx.cs" Inherits="Elite.Library.Web.DefaultPage" %> <!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd"> <html xmlns="http://www.w3.org/1999/xhtml" > <head runat="server"> <title>Test Page</title> </head> <body> <form id="form1" runat="server"> <elite:ExtendedScriptManager ID="ScriptManager1" runat="server" PageClass="Elite.DefaultPage"> <ClientScriptReferences> <elite:ClientScriptReference ClientInstanceName="_bTest" TargetControlID="bTest" /> </ClientScriptReferences> <Scripts> <asp:ScriptReference Path="/Default.aspx.js" /> </Scripts> </elite:ExtendedScriptManager> <div> <asp:Button runat="server" ID="bTest" /> </div> </form> </body> </html>

In questa brevissimo pagina si fa uso dell'ExtendedScriptManager per creare una referenza ad un pulsante (bTest) e per istanziare la classe Elite.DefaultPage. A scopo dimostrativo ho volutamente lasciato in biano il pulsante omettendo di valorizzare la proprietà Text per settare questo valore sul client con la data corrente. Ecco il codice della classe:

 

Type.registerNamespace('Elite'); Elite.DefaultPage = function() { Elite.DefaultPage.initializeBase(this); } Elite.DefaultPage.prototype = { load : function() { this._tick$handle = setInterval( Function.createDelegate(this, this._tick), 1000); }, unload : function() { if (this._tick$handle) { clearInterval(this._tick$handle); delete this._tick$handle; } }, _tick : function() { this._bTest.value = new Date(); } } Elite.DefaultPage.registerClass('Elite.DefaultPage', Elite.Page);

In questa classe, realizzata con il Type System della Microsoft AJAX Library si fa uso della referenza introdotta dall'ExtendedScriptManager per valorizzare il testo del pulsante con la data e l'ora corrente, aggiornate ogni 1000 millisecondi. Il codice è banale, ma quello che vorrei evidenziare è la presenza dei metodi load e unload che hanno il medesimo significato degli omonimi eventi server-side. Essi sono completati dai consueti initialize e dispose che derivano da Sys.UI.Control, classe base di Elite.Page. Infine è da nostare anche che il campo membro _bTest non è mai valorizzato; esso infatti viene introdotto dal codice generato dul server:

 

<script type="text/javascript"> //<![CDATA[ Elite.registerServerControls = function(pageInstance, args) { pageInstance._bTest = $get('bTest'); }; Sys.Application.add_init( function() { Elite.Page.Current = $create( Elite.DefaultPage, { }, { beforeLoad : Elite.registerServerControls }, null, window); }); Sys.Application.initialize(); //]]> </script>

In particolare la referenza iniettata nella pageInstance è quella che poi verrà utilizzata dal nostro codice.

Personalmente sto utilizzando questo sistema da un po' di tempo, e al momento posso dirmi soddisfatto perchè finalmente ho trovato il modo di razionalizzare il lavoro e di ricondurlo ad un modello molto simile a quello che ASP.NET ci mette normalmente a disposizione. Mi interessa conoscere anche la vostra opinione perciò ho deciso di condividere con voi questo codice. Spero che qualcuno trovi utile la solzione e mi dia qualche consiglio su come mgliorare ulteriormente la piccola libreria. Il codice è stato scritto sul framework 3.5 e richiede l'uso di Visual Studio 2008.

Download: http://blog.boschin.it/download/Elite.Web.rar

Technorati tags: , , ,

Credo che nessuno mi possa dare torto se affermo che una delle più belle caratteristiche del framework ASP.NET AJAX è la possibilità di usare dei semplici ASMX per caricare dati sul client da elaborare e trasformare in informazioni visive. Il lavoro che sto realizzando in questo periodo mi sta spingendo ad usare pesantemente questa caratteristica allo scopo di migliorare notevolmente le prestazioni di una applicazione. Motivo per cui mi sono reso conto di una serie di regole "auree" che dovrebbero sempre essere seguite per minimizzare i dati in transito e quindi ottenere un boost delle prestazioni notevole soprattutto se si è in presenza di oggetti custom (non primitivi) ripetuti numerose volte. l'elenco che segue non vuole essere esaustivo, ma sono certo che possa essere un buon punto di partenza:

1) Create sempre delle classi specifiche per il trasferimento dei dati verso il client. Questo vi consente di applicare le regole con il massimo rendimento. Potrete ad esempio usare un nome come "MO" al posto di MyObject. Con una semplicissima mappatura di informazioni potreste risparmiare molto e mantenere il meglio delle naming-conventions nel codice C#.

2) Mettete sempre i tipi creati per il trasferimento nel namespace globale. Per capire il motivo proviamo ad analizzare come gli ScriptService serializzano gli oggetti in JSON

{ "__type":"Company.Namespace.DomainModel.MyObject", "Id":"8907c0cf-3e66-485d-ba6a-13fcdaf41256", "Longitude":12.690935134887695, "Latitude":45.951854705810547 }

Nella prima riga si vede che viene riportato per esteso il tipo dell'oggetto serializzato e questo avviene per ogni singola istanza. Per questo motivo inserire gli oggetti nel namespace globale perlomeno elimina tutta la porzione di nome che riguarda il namespace garantendo un notevole guadagno

3) Sempre guardando la pozione di codice si vede che il tipo di dato double viene riportato con una precisione notevole. nel mio caso tale precisione non è necessaria pertanto grazie ad una Math.Round(double, 5) risparmio ulteriori 10 byte per ogni double. Da questo si evince che la scelta dei tipi e l'analisi delle funzionalità che dobbiamo erogare possono portarci a migliorare di molto i risultati che otterremo. Un esempio analogo è il Guid riportato nello spezzone di codice che potrebbe essere trasformato in un intero, sempre che l'applicazione lo permetta.

4) Abbreviate all'estremo i nomi delle proprietà e dei tipi. Proprio per questo è opportuno usare dei tipi specifici, che ci consentiranno di usare dei nomi telegrafici allo scopo di risparmiare spazio nella trasmissione. nel mio caso il codice può tranquillamente diventare il seguente:

{ "__type":"MO", "Id":"223342739", "Lng":12.69093, "Lat":45.95185 }

5) usate estensivamente la Web Development Helper che vi consente di tracciare la trasmissione e di valutare quando la mole di dati da trasferire sta diventando eccessiva.

Può sembrare banale ma vi garantisco che su grandi quantità di istanze i risultati si vedono e sono molto corposi. Personalmente sono passato da 2250 byte a 1048 byte per solamente 10 istanze in un array. in pratica ogni 100 istanze ho un guadagno netto di ~12Kbyte che mi sembrano un numero di tutto rispetto.

Technorati Tag: , , ,

Da qualche tempo in rete proliferano gli Extension Methods. Lo stesso Scott Guthrie in un suo recente ne ha presentato uno, e qualcun altro ne ha preparato addirittura una . Queste utili estensioni che tipicamente si applicano ai tipi del Framework per estendere le funzionalità, aggiungendo magari qualcosa che chi ha progettato una classe non poteva certo prevedere.

Questo cappello serve ad introdurre il mio contributo a questa moda... :). Pochi minuti fa, mentre portavo un po' di codice verso il Framework 3.5, m'è venuto in mente di trasformare un in Extension Method. Il bello è che il "porting" ho richiesto la semplice introduzione della parola chiave "this" davanti al primo parametro, quindi non più di 30 secondi netti considerando anche il refactoring delle due posizioni in cui il metodo era usato e un misunderstanding con l'intellisense di Resharper che ancora non li supporta. Ecco il risultato:

public static class ControlUtil { /// <summary> /// Finds the control recursive. /// </summary> /// <typeparam name="T"></typeparam> /// <param name="root">The root.</param> /// <param name="id">The id.</param> /// <returns></returns> public static T FindControlRecursive<T>(this Control root, string id) where T : Control { if (root.ID == id && root is T) return root as T; if (root.HasControls()) { foreach (Control child in root.Controls) { T foundControl = FindControlRecursive<T>(child, id); if (foundControl != null) return foundControl; } } return default(T); } }

Il "this" in questione è quello situato davanti al primo parametro del metodo che conferisce ad esso la capacità di essere usato anche come Extension Method (l'uso consueto rimane sempre possibile però...).

Ora, scritto questo e compilato referenziando l'assembly System.Core.dll, diventa possibile scrivere qualcosa del genere:

... public partial class DefaultPage : System.Web.UI.Page { protected void Page_Load(object sender, EventArgs e) { TextBox txtCode = this.FindControlRecursive<TextBox>("txtCode"); } } ...

In pratica con gli Extension Methods è possibile avere il meglio della programmazione Object Oriented, ma anche la possibilità di estendere le classi create da altri senza per questo violare il principio dell'incapsulamento... Parafrasando un noto spot pubblicitario si potrebbe dire: "è come avere la moglie che dice sempre sì..."

Sto bene... Sto Benissimo!!!

Technorati tags: , ,

Stamane ho perso una buona ora per cercare di capire per quale strano motivo il ReportViewer sulla mia macchina Vista Business non ne volesse sapere di funzionare. In particolare eseguento un report per mezzo del webserver della macchina ottenevo un errore Javascrit "RSClientController non definito".

Non ci sono volute molte indagini per scoprire che l'invocazione di Reserved.ReportViewerWebControl.axd restituiva un bel errore 404 Not found, ma il problema era che un veloce controllo nel web.config mi ha rivelato che tutto era in perfetto ordine e infatti sul server di staging tutto va a dovere.

Alla fine ho scoperto che per riuscire a far funzionare tutto è necessario registrare l'HttpHandler dal "Mapping dei Gestori" di IIS7, che poi equivale a registrare l'handler nel web.config nella sezione <system.webserver> oltre che in <system.web>

<system.webServer> <handlers> <add name="ReportViewerWebControl" path="Reserved.ReportViewerWebControl.axd" verb="GET,HEAD" type="Microsoft.Reporting.WebForms.HttpHandler, Microsoft.ReportViewer.WebForms, Version=8.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a" resourceType="Unspecified" /> </handlers> </system.webServer>

Una nota a margine:  Inopinatamente mi ritrovo con il sistema operativo in lingua italiana e di tanto in tanto esce qualche chicca nelle traduzioni. "Aggiungi gestore gestito..." mi ha fatto sorridere. Per la serie: chi gestirà il gestore?

Technorati Tag: , ,

Oggi è la giornata delle segnalazione. E stavolta tocca all'evento organizzato da - una associazione per lo sviluppo informatico che opera nel friuli - che vedrà ospite e in particolare me e Andrea Dottor.

Durante la nostra permanenza avremo occasione di intrattenere il pubblico per un paio di ore a proposito di ASP.NET AJAX 1.0. Si tratta di una riedizione - riveduta e corretta - dell'evento che XeDotNet ha organizzato lo scorso giugno a Mestre. Durante la sessione io e Andrea proveremo a spiegare il framework AJAX rilasciato ormai da un po' di mesi da parte di Microsoft e a dimostrare la semplicità con cui è possibile AJAXizzare (scusate il pessimo neologismo) le proprie applicazioni. L'intervento sarà diviso in due parti, le prima in cui vedremo una serie di slide esplicative ed alcuni esempi - quick & dirty mentre nella seconda parte vedremo l'applicazione sul campo della tecnologia.

Al termine della sessione un'ampia zona Q&A e l'estrazione di alcune licenze offerte da Jetbrains tra cui e .

Per iscrizioni:

Technorati Tag: , ,

Silverlight

: Una libreria interessante per usare facilmente i downloadable fonts di Silverlight al posto del "banale" testo HTML

ASP.NET

: La risposta ad una domanda ricorrente da parte di Scott Guthrie... "Come posso gestire le configurazioni di sviluppo e produzione nello stesso progetto di Visual Studio 2005?"

ASP.NET Centric Extensions : Una serie di Extension Methods pensati per lavorare meglio con ASP.NET. Da provare!

Windows Presentation Foundation

Free Vector Images for your WPF Projects : Un paio di utili link a risorse vettoriali per chi lavora con Windows Presentation Foundation


Ci ho messo un po' a decidere come intitolare questo post, e alla fine ho optato per questo titolo alla Wertmuller perchè mi sembra che il succo del post che sto scrivendo sia proprio questo: imparare ad organizzare per bene il codice di scripting che come ho già detto più volte ha la spiacevole abitudine di infestare le pagine.

L'idea che vorrei presentare mi è venuta lavorando con la Microsoft AJAX Library, e in breve è diventata il mio modo standard di approcciare lo sviluppo in Javascript su pagine HTML perchè mi consente di lavorare con una sorta di codebehind in cui ragruppo tutto il codice di scripting. La base per poter raggiungere questo scopo è una classina che mi sono scritto in -Javascript:

Type.registerNamespace('Elite'); Elite.Page = function() { this.onload$delegate = Function.createDelegate( this, function(eventData) { this.load(); }); this.onunload$delegate = Function.createDelegate( this, function(eventData) { this.unload(); }); window.onload = this.onload$delegate; window.onunload = this.onunload$delegate; Elite.Page.initializeBase(this, [window]); } Elite.Page.prototype = { initialize : function() { Elite.Page.callBaseMethod(this, 'initialize'); }, load : function() { }, unload : function() { }, dispose : function() { if (this.onload$delegate) delete this.onload$delegate; if (this.onunload$delegate) delete this.onunload$delegate; Elite.Page.callBaseMethod(this, 'dispose'); } } } Elite.Page.registerClass('Elite.Page', Sys.UI.Control);

La classe che ho realizzato dovrà essere utilizzata come base per la classe che rappresenta la pagina. Al suo interno infatti sono stati realizzati una serie di metodi stub che ricalcano il funzionamento normale di una pagina asp.net. Troviamo ad esempio il metodo load() che viene collegato automaticamente all'evento window.onload enaturalmente anche la controparte unload().

Ecco come procedere se si vuole utilizzare questa tecnica in una propria pagina che poniamo si chiami dashboard.aspx:

1) create un file Javascript denominato dashboard.aspx.js. questo consente di tenere i file a scelta in una stessa cartella e reperirli facilmente oppure affiancarli alla pagina e vederli nel solution explorer accanto al codebehind. Al termine dello sviluppo se fate uso del Web Application Project potreste convertire tutti gli script in risorse embedded e caricali con webresource.axd.

2) includere il file javascript contenente la classe Elite.Page (ad esempio page.js). Dato che è obblgatorio fare uso della Microsoft AJAX Library è opportuno mettere la referenza all'interno dello ScriptManager che vi ricordo essere il luogo di elezione per qualsivoglia script.

3) nel file dashboard.asp.js creare una classe che rappresenti la pagina e erediti dalla classe Elite.Page. Ecco un esempio:

 

Type.registerNamespace('MyApplication'); MyApplication.DashboardPage = function() { MyApplication.DashboardPage.initializeBase(this, [window]); } MyApplication.DashboardPage.prototype = { load : function() { this.bSave = $get('bSave'); MyApplication.DashboardPage.callBaseMethod(this, 'load'); } }; MyApplication.DashboardPage.registerClass('MyApplication.DashboardPage', Elite.Page);

 

La particolarità di questa classe è di "registrare" il controllo bSave usando $get(). L'evento load() è il posto migliore per questo genere di attività dato che vi si arriva subito dopo che il download e il rendering della pagina è stato effettuato.

4) Inserire mediante ScritpManager.RegisterStartupScript() il codice che istanzia la classe:

var currentPage = $create(MyApplication.DashboardPage, {}, {}, null, null);

Questa chiamata in particolare istanzierà la classe e inizierà il ciclo di vita invocando il metodo initialize. Poi non appena caricata la pagina sarà invocato il metodo load() e di conseguenza la referenza verrà valorizzata con il controllo bSave. Naturalmente a questo punto sarà opportuno agganciare via script gli eventi dei controlli. Vi ricordo che per fare questo  è necessario l'uso di Function.createDelegate() che come ho già consente di mantenere il contesto di partenza e quindi di rimanere all'interno della classe.

Rimane solo un problema. Sappiamo tutti che i controlli di ASP.NET hanno un id generato a runtime che può essere semplice o composto. Per questo vi sarà necessario passare come argomenti della classe gli id estratti dai ClientID. In realtà ho un'altra idea in incubazione ma... questo sarà l'argomento di uno dei prossimi post.