Silverlight Feeds - All your Silverlight feeds in one place.

Sponsors

Wednesday, December 30, 2009

Silverlight 4.0: INavigationContentLoader

by Corrado Cavalli via Corrado's BLogs on 12/30/2009 8:00:44 AM

Silverlight 3.0 ha introdotto l’oggetto Frame grazie al quale è possibile navigare le pagine che compongono l’intera applicazione, l’utilizzo di Frame è abbastanza semplice, supponendo infatti di avere la pagina principale della nostra applicazione (MainPage.xaml) definita in questo modo:

<Grid x:Name="LayoutRoot">
        <Grid.RowDefinitions>
            <RowDefinition Height="*" />
            <RowDefinition Height="2*" />
        </Grid.RowDefinitions>
 
        <navigation:Frame Grid.Row="1"  Name="frame1" />
 
        <Button Content="Local" Height="33"  HorizontalAlignment="Left"
                Margin="12,8,0,0"
                Width="69"
                Click="button1_Click" />
        <Button Content="External" Height="33" HorizontalAlignment="Left"
                Margin="97,8,0,0"
                VerticalAlignment="Top"
                Width="69"
                Click="button2_Click" />
    </Grid>

e supponendo di avere nell’applicazione due pagine Local.xaml e External.xaml, per navigare da una pagina all’altra è sufficiente scrivere:

private void button1_Click(object sender, RoutedEventArgs e)
{
   frame1.Navigate(new Uri("/LocalPage.xaml", UriKind.Relative));
}
 
private void button2_Click(object sender, RoutedEventArgs e)
{
   frame1.Navigate(new Uri("/ExternalPage.xaml", UriKind.Relative));
}

Come sapete, quando le dimensioni dell’applicazione diventano ‘importanti’ è bene componentizzare affinchè le varie parti vengano scaricate on-demand evitando il download di parti inutilizzate e migliorando la velocità di startup. Tornando al nostro esempio, sarebbe interessante se la pagina ExternalPage.xaml che per come è stata organizzata la nostra applicazione risiede in uno xap separato, venisse scaricata esclusivamente quando l’utente la richiede e successivamente cachata attraverso un meccanismo trasparente e riutilizzabile: ecco dove entra in gioco INavigationContentLoader.

Implementando questa interfaccia e associandone un istanza alla proprietà ContentLoader è possibile gestire autonomamente l’elaborazione della richiesta delle varie pagine da visualizzare all’interno del frame.
Modifichiamo quandi la parte di xaml di MainPage relativa al frame in questo modo:

<navigation:Frame Grid.Row="1" Name="frame1">
    <navigation:Frame.ContentLoader>
        <local:MyContentLoader />
    </navigation:Frame.ContentLoader>
</navigation:Frame>

MyNavigationContentLoader è una classe che implementa INavigationContentLoader ed è riportata di seguito:

public class MyContentLoader : INavigationContentLoader
{
   private Dictionary<string, Assembly> assemblies = new Dictionary<string, Assembly>();
 
   public MyContentLoader()
   {
      //Adds current to list of loaded assemblies
      Assembly currentAssembly = Assembly.GetExecutingAssembly();
      this.AddAssembly(currentAssembly);
   }
 
   public IAsyncResult BeginLoad(Uri targetUri, Uri currentUri, AsyncCallback userCallback, object asyncState)
   {         
      var iar = new MyAsyncResult(asyncState);
      //Tries to load type from already loaded assemblies
      iar.Result = this.TryLoad(targetUri);
      //Type not loaded yet, download it asyncronously
      if (iar.Result == null)
      {
         string assemblyName = targetUri.ToString().Split('.')[0];
         //Probably it'a an external page not loaded yet...
         WebClient client = new WebClient();
         client.OpenReadCompleted += (s, e) =>
            {                  
               Uri assemblyUri = new Uri(assemblyName + ".dll", UriKind.Relative);
               StreamResourceInfo xapPackageSri = new StreamResourceInfo(e.Result, null);
               StreamResourceInfo assemblySri = Application.GetResourceStream(xapPackageSri, assemblyUri);
               //Load it
               Assembly assembly = new AssemblyPart().Load(assemblySri.Stream);
               iar.Result = assembly.CreateInstance(targetUri.ToString());
               //Add assemblies to list of loaded ones
               this.AddAssembly(assembly);
               //Page loaded, end asyncronous operation
               userCallback(iar);
            };
 
         client.OpenReadAsync(new Uri(assemblyName + ".xap", UriKind.Relative));
      }
      else
      {
         userCallback(iar);
      }
      return iar;
   }
 
   public bool CanLoad(Uri targetUri, Uri currentUri)
   {
      return true;
   }
 
   public void CancelLoad(IAsyncResult asyncResult) { }
 
   public LoadResult EndLoad(IAsyncResult asyncResult)
   {
      return new LoadResult((asyncResult as MyAsyncResult).Result);
   }
 
   private void AddAssembly(Assembly assembly)
   {
      string name = assembly.FullName.Split(',')[0];
      this.assemblies.Add(name, assembly);
   }
 
   private object TryLoad(Uri targetUri)
   {
      string assemblyName = targetUri.ToString().Split('.')[0];
      if (this.assemblies.ContainsKey(assemblyName))
         return this.assemblies[assemblyName].CreateInstance(targetUri.ToString());
      else
         return null;
   }
}

Il principio di funzionamento è abbastanza semplice, ci sono una serie di metodi CanLoad,CancelLoad, BeginLoad etc, che vengono invocati passando la Uri da caricare, la parte più complessa è il fatto che il caricamento potrebbe avvenire in maniera asincrona ecco perchè tra i paramentri viene passata anche IAsyncResult.
Nel nostro caso tutta l’implementazione è racchiusa nel metodo CanLoad il quale in TryLoad verifica se la pagina è già disponibile altrimenti attraverso WebClient la scarica e la mette nella “cache” interna a MyContentLoader.
Terminata l’operazione viene invocato il metodo callback passando un implementazione di MyAsyncResult che espone la pagina caricata attraverso la proprietà Result:

public class MyAsyncResult : IAsyncResult
{
   public MyAsyncResult(object asyncState)
   {
      this.AsyncState = asyncState;
      this.AsyncWaitHandle = new ManualResetEvent(true);
   }
 
   public object Result { get; set; }
 
   public object AsyncState { get; private set; }
 
   public WaitHandle AsyncWaitHandle { get; private set; }     
 
   public bool CompletedSynchronously
   {
      get { return true; }
   }
 
   public bool IsCompleted
   {
      get { return true; }
   }      
}

il risultato della chiamata è l’invocazione del metodo EndLoad nel quale viene creato e restituito un oggetto LoadResult contenente la pagina richiesta.

L’esempio fa uso della convenzione per la quale la Uri da passare è nella forma “Assembly.PageName” e presuppone che lo xap contenente la pagina si chiami esattamente come il nome dell’assembly:

private void button1_Click(object sender, RoutedEventArgs e)
 {
    frame1.Navigate(new Uri("CustomContentLoaderDemo.LocalPage", UriKind.Relative));
 }
 
 private void button2_Click(object sender, RoutedEventArgs e)
 {
    frame1.Navigate(new Uri("ExternalPages.ExternalPage", UriKind.Relative));
 }

A questo punto, supponendo che la nostra applicazione Silverlight 4.0 sia organizzata in questo modo (notate la presenza di ExternalPages.xap)

image

Premendo i vari buttons, la prima volta che cercheremo di visualizzare ExternalPage questa verrà prima scaricata e poi visualizzata.

Questo è solo una delle possibili applicazioni, ogni volta che volete avere il controllo totale della navigazione all’interno di un applicazione Silverligt 4.0 ricordatevi di questa interfaccia che è la stessa implementata da PageResourceContentLoader ovvero il gestore di navigazione predefinito dell’oggetto Frame.

Buona navigazione!

email it!bookmark it!digg it!

Original Post: Silverlight 4.0: INavigationContentLoader

Subscribe

New Feed

Product Spotlight

Recently Updated Sources

Legal Note

The content of the postings is owned by the respective author. Silverlight Feeds is not responsible for the contents of the postings. This site is automatically generated and cannot be reviewed for abusive content. If you find abusive content on Silverlight Feeds, please contact us. Designated trademarks and brands are the property of their respective owners. All rights reserved.

Advertise with us