Articolo da me pubblicato su Internet.Pro Dicembre 2004.
Per anni abbiamo vissuto la lotta fra Internet Explorer e Netscape e a prescindere da quale possa essere il migliore, il più standard, il più carino, il più veloce, le differenze fra i due hanno costretto spesso gli sviluppatori di applicazioni web a produrre due diverse pagine per ogni funzionalità. Queste differenze rallentano i tempi di sviluppo e di manutenzione di un’applicazione e costringe ogni bravo sviluppatore a seguire l’andamento delle versioni di entrambi i prodotti. Se facciamo un passo indietro nel tempo ci dovremmo ricordare come il "bello di internet" dovesse essere scrivere il codice in un unica location per renderlo disponibile a tutto il mondo. Con l’andare del tempo le differenze fra i due browser si sono affievolite, non perchè i due browser siano diventati identici, ma perchè il set di funzionalità "standard" di entambi è cresciuto e quindi ad oggi è possibile scrivere un’interfaccia utente carina, accattivante e funzionale senza diventare pazzi nella scrittura del codice. Già da qualche anno però, si sono affacciati sul mercato dispositivi diversi dai classici desktop, in grado di leggere contenuti HTML. Pocket PC e Palm hanno non solo uno schermo di dimensioni ridotte e un supporto ai font limitato, ma soprattutto non supportano le "ultime" tecnologie con cui siamo abituati a lavorare nell’ambiente desktop: alcuni di essi supportano solo HTML 3.2, altri non supportano i Cascading Style Sheet, altri ancora non supportano Javascript o almeno non l’ultima versione.
Forse per colpa della ciclicità delle stagioni informatiche, siamo tornati al passato: è impossibile produrre una pagina che venga visualizzata in modo identico su Netscape e su un Pocket PC, su Internet Explorer e su un Palm; e pensare che se l’HTML si fosse fermato alla versione 3.2, non esistessero Javascript e CSS, le pagine prodotte nel 1997 oggi sarebbero perfette anche per un Pocket PC 2003. E’ anche vero forse che se non esistessero queste caratteristiche non avremmo potuto produrre applicazioni web funzionali e siti web di grande successo.
A complicare la vita dello sviluppatore da qualche anno si sono affacciati sul mercato anche telefonini (o comunque device di dimensioni ridotte) in grado di navigare sul web e visualizzare contenuti in formati diversi dall’HTML. Negli stati uniti i telefonini di Phone.com utilizzano HDML, in giappone si usa molto cHTML, in Europa WAP e WML (dove WAP è il protocollo applicativo con cui il device parla con un WAP Server che ospita l’applicazione o con un WAP Gateway che gira le richieste sul protocollo HTTP).
Morale della favola: ci sono device diversi, con schermi diversi, con dimensioni (pixel) diversi, con markup language diversi, con funzionalità diverse. Ad esempio WML ha un supporto totalmente diverso alla gestione dei documenti: un documento WML può contenere diverse "maschere" di visualizzazione client-side, la navigazione spesso si appoggia a tasti funzione del telefono, la gestione dei POST è completamente diversa rispetto al cugino HTML, WML supporta nativamente il concetto di variabile.
Lo sviluppatore ha quindi due strade: scrivere le pagine con funzionalità supportate da tutti i device, che vuol dire fare interfacce utente penose, oppure adattare il layout in base al device che effettua le richieste, il che vuol dire produrre dalle 4 alle 8 pagine diverse per ogni funzionalità applicativa.
Lo sviluppatore furbo potrebbe farsi una libreria che, attingendo a una configurazione esterna, produce il codice corretto in base al device che effettua la richiesta: ogni volta che esce un device diverso o un nuovo markup language adatterà la configurazione e la libreria senza ricompilare o modificare la pagina di contenuti. Un lavoro da certosino.
Fortunatamente in ASP.NET 1.1 troviamo nativamente i Mobile Controls (per ASP.NET 1.0 è scaricabile grauitamente un Add-in denominato Mobile Internet Toolkit): fondamentalmente questa tecnologia corrisponde all’idea che ci è venuta in mente nel paragrafo precedente. ASP.NET Mobile Controls è una libreria (un assembly .NET per la precisione) e una configurazione (memorizzata nel machine.config di ASP.NET) in grado di produrre interfacce utente diverse in base al device che effettua la richiesta. Procediamo per gradi.
Questo è un esempio di pagina mobile:
<%@ Page Inherits="System.Web.UI.MobileControls.MobilePage" %>
<%@ Register TagPrefix="mobile" Namespace="System.Web.UI.MobileControls" Assembly="System.Web.Mobile" %>
...
<mobile:Form runat="server">
Ciao a tutti! <br />
<mobile:Label runat="server" ID="lblTesto" Text="Ecco una Label" />
</mobile:Form>
...
Come si può notare, la pagina deriva da MobilePage e non dalla classica Page e, come accade per gli user control custom, la direttiva Register registra nella pagina con il prefisso mobile la libreria System.Web.Mobile che contiene tutto il necessario per utilizzare i Mobile Control all’interno della pagina stessa.
Il resto del codice dovrebbe essere chiaro: si definisce un mobile form con all’interno del testo puro e una mobile:Label. L’obiettivo è infatti lavorare con un modello a oggetti consistente fra tutti i device. L’architettura in realtà ci fornisce i Mobile Controls che rappresentano i controlli visivi sotto forma di oggetti programmabili (come i classici Web Controls): tali oggetti espongono proprietà, metodi e eventi con cui andremo a lavorare nel codice. Si lavora quindi con un unico controllo e con un unico codice in risposta a eventi applicativi a prescindere dal device che effettua le richieste HTTP. Per ogni controllo fornito esiste un Adapter che si incarica di eseguire la fase di rendering verso il device: il compito dell’adapter è produrre il codice di markup corretto e l’interfaccia adatta al tipo di device che ha fatto la richiesta. Esiste quindi un HtmlLabelAdapter e un WmlLabelAdapter. Tali adapter vengono chiamati in causa durante la fase di render della pagina. Quindi, lo sviluppatore lavora con l’oggetto come se fosse un Web Control classico e dietro le quinte l’Adapter adatta il codice di markup in base al device.
La configurazione degli adapter è memorizzata nel file machine.config. Ecco un estratto per gli Adapter HTML:
<device name="HtmlDeviceAdapters"
predicateClass="System.Web.UI.MobileControls.Adapters.HtmlPageAdapter"
predicateMethod="DeviceQualifies"
pageAdapter="System.Web.UI.MobileControls.Adapters.HtmlPageAdapter">
<control name="System.Web.UI.MobileControls.Form"
adapter="System.Web.UI.MobileControls.Adapters.HtmlFormAdapter"/>
<control name="System.Web.UI.MobileControls.TextBox"
adapter="System.Web.UI.MobileControls.Adapters.HtmlTextBoxAdapter"/>
<control name="System.Web.UI.MobileControls.Label"
adapter="System.Web.UI.MobileControls.Adapters.HtmlLabelAdapter"/>
E un estratto per gli Adapter cHTML
<device name="ChtmlDeviceAdapters"
inheritsFrom="HtmlDeviceAdapters"
predicateClass="System.Web.UI.MobileControls.Adapters.ChtmlPageAdapter"
predicateMethod="DeviceQualifies"
pageAdapter="System.Web.UI.MobileControls.Adapters.ChtmlPageAdapter">
<control name="System.Web.UI.MobileControls.Form"
adapter="System.Web.UI.MobileControls.Adapters.ChtmlFormAdapter"/>
<control name="System.Web.UI.MobileControls.Calendar"
adapter="System.Web.UI.MobileControls.Adapters.ChtmlCalendarAdapter"/>
<control name="System.Web.UI.MobileControls.Image"
adapter="System.Web.UI.MobileControls.Adapters.ChtmlImageAdapter"/>
<control name="System.Web.UI.MobileControls.TextBox"
adapter="System.Web.UI.MobileControls.Adapters.ChtmlTextBoxAdapter"/>
I controlli (Form, TextBox etc) hano un corrispondente Adapter per ogni markup language disponibile. cHTMLDeviceAdapter deriva da HtmlDeviceAdapter, quindi, come si può notare dai due estratti di codice, la Label viene ereditata dalla versione HTML: la label infatti produce del testo semplice che è perfetto anche per la versione Compact di HTML (cHtml).
La funzione degli adapter non si limita a produrre markup language diverso, ma anche l’interfaccia più consona al device che effettua le richieste. Prendiamo ad esempio il controllo mobile:Calendar dell’esempio seguente:
<mobile:Form id="Form1" runat="server">
<mobile:Calendar
id="Calendario" runat="server"
CalendarEntryText="Inserisci data"
FirstDayOfWeek="Monday" /><mobile:Label id="lblData" runat="server" />
</mobile:Form>
Nel Code-behind della pagina intercettiamo l’evento SelectionChanged che si scatena quando l’utente sceglie un giorno e per valorizzare la label:
private void SelectionChanged(object sender, System.EventArgs e)
{
lblData.Text = Calendario.SelectedDate.ToShortDateString();
}
Se visualizziamo la pagina su un Pocket PC otterremo il seguente risultato (prima e dopo il click su un giorno)


Lo stesso risultato lo si ottiene su un browser desktop.
Se visualizziamo il tutto su un device WML con uno schermo piccolo, questo è il risultato (figure in sequenza)


Nella form iniziale non si vede nessun calendario, ma solo un link che riporta la proprietà CalendarEntryText: si risparmia spazio per altri controlli. Cliccato (o meglio selezionato con i tasti funzione del telefono) il link si aprono 3 scelte: la data di default, la possibilità di digitare una data e la possibilità di scegliere una data. Nei device di piccole dimensioni non c’è il mouse e quindi l’utente si sposta con i tasti funzione del telefono (Su, Giù e ove disponibili Dx, Sx, OK): è necessario limitare il più possibile interfacce complesse (come dover segliere fra 30 giorni di un mese premendo sempre Giù, Giù, Giù, Giù, Dx, Dx, Dx, Dx per raggiungere una data in fondo). Scegliere la data di default nell’interfaccia preparata dall’Adapter è un semplice OK. Credo concorderete anche che, digitare sul tastierino numerico del telefono una data conosciuta spesso sia più veloce che cercare una data, magari a 12 mesi di distanza dal mese corrente: la seconda scelta apre infatti un campo numerico per l’inserimento della data (tramite le proprietà del controllo è possibile indicarne il formato). Se invece l’utente ha bisogno di vedere il calendario per scegliere una data opterà per la terza scelta "scegli una data". Anche in questo caso non verrà presentato l’intero calendario, ma un’interfaccia molto più veloce per la scelta. Ecco a voi:




Si sceglie prima il mese, poi la settimana, in seguito il giorno e al termine si torna alla pagina completa di tutti i controlli con la label che visualizza il giorno del mio compleanno...quest’anno viene di Giovedì (tra l’altro quest’anno compio 20 anni...in esadecimale J).
Compito degli adapter quindi è produrre un’interfaccia adatta al device in tutto e per tutto, non solo produrre il markup language interpretabile dal client.
Per capire il tipo di browser che sta effettuando le richieste il motore del Mobile Controls non fa altro che analizzare nell’header HTTP, in prima battuta, la stringa USER_AGENT e in base a tutte le informazioni trovate all’interno, legge la configurazione dei device memorizzata nel machine.config. Più informazioni vengono inviate dal browser più accurata sarà l’interfaccia utente che potrà essere prodotta. Vi mostro ad esempio un estratto dell’header HTTP inviata dal mio Pocket PC 2003 Phone Edition:
<accept>*/*</accept>
<ua-os>Windows CE (Pocket PC) - Version 4.20</ua-os>
<ua-color>color16</ua-color>
<ua-pixels>240x320</ua-pixels>
<ua-cpu>Intel(R) PXA263</ua-cpu>
<ua-voice>TRUE</ua-voice>
<ua-language>JavaScript</ua-language>
<accept-encoding>gzip, deflate</accept-encoding>
<user-agent>
Mozilla/4.0 (compatible; MSIE 4.01; Windows CE; PPC; 240x320)
</user-agent>
In base a ua-pixels, ua-color, ua-language, user-agent l’adapter prenderà decisioni su come formare l’interfaccia utente per il mio Qtek 2020. Da notare che il mio telefono indica anche ua-voice true che consente al controllo PhoneCall di effettuare direttamente una chiamata al numero telefonico. A unn device che non supporta VoiceCall verrà invece semplicemente inviato il numero di telefono sotto forma di stringa.
In moltissimi casi, l’interfaccia prodotta dai Mobile Controls sarà più che ottima su tutti i device che effettuano le richieste. E’ possibile personalizzare il comportamento di default dei vari mobile control con l’indicazione di eseguire un override del valore delle varie proprietà in base a semplici condizioni: ad esempio, se il client è un Palm, allora la listbox deve avere 10 elementi, mentre se è un telefono WAP, solo tre elementi. Inoltre, nessuno ci vieta di scriverci un proprio adapter per fornire un comportamento completamente custom: vedremo in un articolo separato come personalizzare le modalità di funzionamento dei mobile controls, rendendo ancora più efficiente l’interfaccia utente che viene prodotta.
Un ultima cosa, visto che dall’uscita di ASP.NET a oggi, sono usciti diversi device, è importante aggiornare la configurazione presente nel machine.config. Per fare questo, prima di procedere eventualmente a mano (operazione non difficile e che vedremo nel prossimo articolo), si possono scaricare i Device Update dal sito Microsoft. Occhio che gli update per ASP.NET 1.1 Mobile Controls sono diversi da quelli per ASP.NET 1.0 Mobile Internet Toolkit: è necessario scaricare l’aggiornamento corretto.
In ASP.NET 2.0 l’architettura dei Mobile Controls è stata unificata a quella dei Web Controls, nel senso che tutti i controlli adatteranno il loro output in base alle caratteristiche del device che effettua le richieste: in due parole, in ASP.NET 2.0 non esistono più i Web Controls, saranno tutti Mobile Controls (anche se ovviamente la sintassi sarà sempre <asp:XXXXX).
