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"
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
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)
if (this.assemblies.ContainsKey(assemblyName))
return this.assemblies[assemblyName].CreateInstance(targetUri.ToString());
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
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:
frame1.Navigate(new Uri("CustomContentLoaderDemo.LocalPage", UriKind.Relative));
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)
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!
Original Post: Silverlight 4.0: INavigationContentLoader
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.