L’utilizzo di rich plugin su Internet e nelle intranet come strumento per lo sviluppo di interfacce client sta diventando sempre più frequente. L’introduzione con Silverlight 2 dello sviluppo con .NET e la possibilità di consumare e aggregare dati provenienti da Web Service in una interfaccia in cui si possono utilizzare controlli e databinding ha allargato le possibilità di utilizzo anche a quelle classiche applicazioni Intranet di gestione dati.

La crescita nelle potenzialità utilizzabili per lo sviluppo e l’impiego di Silverlight in applicazioni di maggiore complessità e che implementano una notevole quantità di funzionalità e che in alcuni casi comportano in conseguenza un incremento nelle dimensioni del codice compilato che viene prodotto e che deve essere in conseguenza scaricato sul client.

Con il tradizionale modello di deployment di Silverlight le componenti dell’applicazione implementata vengono compilate in dll e racchiusi un file compresso che prende l’estensione .xap che viene utilizzato come contenitore. L’utilizzo dell’applicazione è indicato nella pagina client attraverso lo specifico tag object che tra i parametri contiene l’indicazione del file .xap contenente il codice compilato dell’applicazione da eseguire che deve essere scaricato e utilizzato nel client.

Il seguente esempio di tag illustra una tipica implementazione dell’inserimento di un’applicazione Silverlight con evidente l’indicazione dello .xap contenente l’applicazione:

<object data="data:application/x-silverlight-2," type="application/x-silverlight-2" width="100%" height="100%">

<param name="source" value="ClientBin/SilverlightComponent.xap"/>

<param name="onerror" value="onSilverlightError" />

<param name="background" value="white" />

<param name="minRuntimeVersion" value="2.0.31005.0" />

<param name="autoUpgrade" value="true" />

<a href="http://go.microsoft.com/fwlink/?LinkID=124807" style="text-decoration: none;">

<img src="http://go.microsoft.com/fwlink/?LinkId=108181" alt="Get Microsoft Silverlight" style="border-style: none"/>

</a>

</object>

Nel caso di applicazioni più corpose i tempi di download del file e di avvio dell’applicazione possono diventare significativi ed impattare in modo negativo sulla esperienza del’’utente nell’utilizzo dell’applicazione stessa. Ovviamente si può agire sui meccanismi di cache per ridurre al minimo le volte in cui all’avvio dell’applicazione la stessa sia scaricata nuovamente nel client. Con questo tipo di tecnica per il deployment però , si riduce il problema , ma non si eliminano comunque i tempi necessari nei casi in cui a seguito di una modifica o all’avvia su una nuova macchina dell’applicazione deve essere scaricato l’intero .xap.

Per gestire questo tipo di situazioni è inoltre possibile utilizzare alcuni parametri che consentono di mostrare delle animazioni personalizzate durante lo scaricamento dello .xap. Attraverso il parametro splashscreensource del tag object utilizzato per istanziare nella pagina l’applicazione è infatti possibile introdurre un file .xaml con una grafica personalizzata e ricevere con delle callback Java Script onSourceDownloadProgressChanged e onSourceDownloadComplete le informazioni sullo stato di avanzamento del download dello xap in maniera da permettere lo sviluppo di una interfaccia personalizzata, per gestire in modo più efficace la comunicazione versol’utente durante il download dell’applicazione.

In particolare nel seguente tag abbiamo un esempio di come sia possibile configurare questa funzionalità:

<object data="data:application/x-silverlight-2," type="application/x-silverlight-2" width="100%" height="100%">

<param name="source" value="ClientBin/SilverlightComponent.xap"/>

<param name="onerror" value="onSilverlightError" />

<param name="background" value="white" />

<param name="minRuntimeVersion" value="2.0.31005.0" />

<param name="autoUpgrade" value="true" />

<param name=”splashscreensource” value=”SplashScreen.xaml”/>

<param name=”onSourceDownloadProgressChanged” value=”onSourceDownloadProgressChanged” />

<param name=”onSourceDownloadComplete” value=”onSourceDownloadProgressChanged” />

<a href="http://go.microsoft.com/fwlink/?LinkID=124807" style="text-decoration: none;">

<img src="http://go.microsoft.com/fwlink/?LinkId=108181" alt="Get Microsoft Silverlight" style="border-style: none"/>

</a>

</object>

Attraverso il parametro splashscreensource ho impostato una grafica in xaml che Silverlight mostrerà durante il tempo di caricamento e attraverso le due callback provvederò a pilotare l’aggiornamento dello stato di download dell’interfaccia mostrata all’utente durante il download.

Ad esempio nel file splashscreen.xaml si potrà mostrare una grafica simile a quella riportata di seguito:

<Canvas
   xmlns=”
http://schemas.microsoft.com/winfx/2006/xaml/presentation”
   xmlns:x=”
http://schemas.microsoft.com/winfx/2006/xaml”
   Width=”550″ Height=”550″>
  <Grid>
    <TextBlock Text=”Loading: 0%” x:Name=”testo”/>
  </Grid>
</Canvas>

E con le due seguenti funzioni si potrà così modificare il contenuto della interfaccia utente in base allo stato di avanzamento:

function onSourceDownloadProgressChanged(sender, eventArgs)
{
    sender.findName(”testo”).Text =  “Loading: ” + Math.round((eventArgs.progress * 1000)) / 10 + “%”;
}

function onSourceDownloadComplete(sender, eventArgs)
{
    sender.findName(”testo”).Text =  “Completed”;
}

Questo tipo di tecnica consente di gestire in modo più efficace l’interazione con l’utente ma non elimina il tempo di attesa per quei casi in cui abbiamo applicazioni corpose che devono essere scaricate nel client.

Oltre alla modalità di deployment classica che abbiamo visto, Silverlight consente di effettuare il download ed il caricamento di componenti aggiuntive preparate in xap differenti durante l’esecuzione dell’applicazione. Questo consente di poter pensare la propria applicazione differenti componenti che verranno poi compilati ed inseriti in xap differenti. Il componente principale conterrà la parte di startup dell’applicazione e sarà quello che verrà indicato nel tag source inserito nella pagina web.

Durante l’esecuzione di questo primo componente, verranno caricati su richiesta nel momento in cui occorrono per l’esecuzione dell’applicazione , le restanti componenti.

Questo tipo di modello consente di scomporre l’applicazione in componenti diverse modulando i tempi di caricamento ed avvio dell’applicazione stessa.

clip_image002

Silverlight consente, infatti, di caricare a runtime componenti aggiuntive ed eseguirle. Lo scaricamento sul client del componente aggiuntivo può essere effettuato attraverso la classe WebClient . Terminato lo scaricamento si può estrarre l’oggetto dallo xap o dalla dll scaricata utilizzando delle classi Assembly ed AssemblyPart.

Nel seguente frammento di codice ho riportato un esempio di download e caricamento di un oggetto Silverlight da un componente esterno allo xap dell’applicazione. Come primo passo si utilizza il WebClient per effettuare il download del componente satellite:

WebClient client = new WebClient();

client.DownloadStringCompleted +=

new DownloadStringCompletedEventHandler(OnDownloadCompleted);

Uri uri = new Uri( …url del componente satellite.. , UriKind.Relative);

client.DownloadStringAsync(uri);

Sulla callback chiamata al termine del download si procede a creare un istanza dell’oggetto e ad esempio aggiungerla come Child al contenitore dove dovrà essere visualizzata:

void OnDownloadCompleted(object sender, DownloadStringCompletedEventArgs e)

{

// caricamento dell’assembly dallo XAP package dove e è l’args della callback del web client

Assembly ass = GetAssemblyFromXap(nomeAssembly, e.Result);

// Creazione istanza object

object component = ass.CreateInstance(className);

UserControl componentControl = component as UserControl;

// Caricamento come child di un contenitore

contenitore.Children.Clear();

contenitore.Children.Add(componentControl);

}

Assembly GetAssemblyFromXap(string assemblyName, Stream xap)

{

Uri assemblyUri = null;

StreamResourceInfo resPackage = null;

StreamResourceInfo resAssembly = null;

AssemblyPart part = null;

assemblyUri = new Uri(assemblyName, UriKind.Relative);

resPackage = new StreamResourceInfo(xap, null);

resAssembly = Application.GetResourceStream(resPackage, assemblyUri);

//Estrae l’assembly

part = new AssemblyPart();

Assembly a = part.Load(resAssembly.Stream);

return a;

}

In modo similare è anche possibile scaricare ed visualizzare dei file xaml. In questo caso terminato lo scaricamento del file contenente lo xaml nella funzione di callback del webclient si può:

void OnDownloadCompleted(object sender, DownloadStringCompletedEventArgs e)

{

// Parsing e costruzione oggetti UI dallo XAML

string xaml = e.Result;

UIElement uiObjects = XamlReader.Load(xaml) as UIElement;

// Append to the DOM

contenitore.Children.Clear();

contenitore.Children.Add(uiObjects);

}

Sfruttando questo tipo di funzionalità disponibile per il deployment in SL diventa semplice scomporre in componenti l’applicazione e permettere una migliore gestione del download delle risorse necessarie al funzionamento dell’applicazione stessa implementando ad esempio pattern come il LazyLoad (http://en.wikipedia.org/wiki/Lazy_loading ) per gestire il caricamento su richiesta delle componenti.

Per poter gestire con più semplicità il caricamento delle componenti e stabilire una semplice suddivisione della applicazione un possibile approccio consiste nell’implementare i diversi componenti satellite come Sliverlight Control e nel gestire come Applicazione solo la componente principale che si occuperà di caricare il menù delle funzioni applicative e l’infrastruttura di base utilizzata per la gestione dello stato e per il caricamento delle componenti che implementano le singole funzionalità.

Allo scopo di consentire anche un utilizzo trasparente del caricamento a richiesta delle componenti aggiuntive è utile sviluppare un controllo che consenta di rendere trasparente questa operazione rispetto allo sviluppo delle funzionalità dell’applicazione stessa.

Ad esempio immaginando un’applicazione che presenta un menù di funzionalità ciascuna implementata attraverso una delle componenti satellite, può essere utile implementare un controllo da inserire nello UserControl principale dell’applicazione che attraverso la semplice modifica di alcuni parametri , provveda automaticamente a caricare ed eseguire, inserendo nello spazio in cui il controllo e inserito nell’ applicazione principale , gli elementi del componente satellite impostati sulle proprietà . Ad esempio nel seguente frammento di xaml abbiamo una Grid con all’interno il controllo con le proprietà impostate all’interno del quale verrà visualizzato lo UserControl TestComponente, estratto dallo xap TestComponente:

<StackPanel Grid.Row="1" x:Name="contenitore">

<c:ComponentLoaderControl>

<c:ComponentLoaderControl.ComponentInfo>

<c:ComponentInfo ComponentXapUrl="TestComponent.xap" ComponentAssembly="TestComponent.dll" ComponentControl="TestComponent.Page" />

</c:ComponentLoaderControl.ComponentInfo>

</c:ComponentLoaderControl>

</StackPanel>

Un controllo di questo genere oltre a gestire con semplicità il meccanismo di Download e caricamento dei componenti aggiuntivi, potrebbe occuparsi di gestire sia gli eventuali errori di download o caricamento del componente aggiuntivo sia i feedback verso l’utente relativi agli errori e alla grafica necessaria a comunicare lo stato di download del componente aggiuntivo.

Un controllo di questo tipo potrebbe essere implementato attraverso un customcontrol di Silverlight in maniera da ottenere un componente facilmente riutilizzabile in più applicazioni.

Come esempio ho realizzato un prototipo di customcontrol che implementa questa funzionalità da utilizzare come base per realizzare un componente di questo tipo nelle vostre applicazioni e che potete scaricare da qui.