Silverlight 4 e la possibilità di navigare su Page definite in un assembly separato
Introduzione
Sappiamo tutti che Silverlight 4 ha un meccanismo di navigation interno. A cosa serve? Il tutto si basa sul controllo Frame, che può contenere oggetti Page. Questi oggetti Page possono contenere a loro volta contenere tutto ciò che vi può passare per la testa, quindi testo, immagini, altri controlli, animazioni, etc. etc.
Perchè si chiama navigation? Perchè dietro le quinte Silverlight fa un pochino di cose interessanti, prima fra tutte la history delle Page che avete visitato. Attraverso l’oggetto NavigationService, quindi, potete chiamare metodo come GoBack(), GoForward(), accedendo quindi a tutte le Page già visitate dall’utente.
Maggiori informazioni sull’argomento a questo indirizzo.
Concetti base
Ok, supponiamo di aver creato una semplice SilverlightApplication1, hostata da un progetto SilverlightApplication1.Web. Sulla MainPage.xaml creiamo un controllo Frame:
<UserControl x:Class="SilverlightApplication1.MainPage"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
xmlns:nav="clr-namespace:System.Windows.Controls;assembly=System.Windows.Controls.Navigation"
mc:Ignorable="d"
d:DesignHeight="300" d:DesignWidth="400">
<nav:Frame Background="LightCyan" Source="/Page1.xaml">
</nav:Frame>
</UserControl>
Il controllo Frame è definito nell’assembly System.Windows.Controls.Navigation, quindi ho dovuto dichiarare il namespace ‘nav’. Nulla di anormale fin qua.
Notare la proprietà Source del controllo, impostata su “/Page1.xamlâ€. Questo significa che nella stessa directory dove abbiamo la MainPage.xaml dobbiamo aggiungere una Silverlight Page, chiamarla Page1.xaml, e definirne il contenuto. Supponiamo di aver creato una pagina così:
<navigation:Page x:Class="SilverlightApplication1.Page1"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
mc:Ignorable="d"
xmlns:navigation="clr-namespace:System.Windows.Controls;assembly=System.Windows.Controls.Navigation"
d:DesignWidth="640" d:DesignHeight="480"
Title="Page1 Page">
<TextBlock Text="Page 1" FontSize="56" />
</navigation:Page>
La pagina contiene una semplice TextBlock con scritto “Page 1â€. In questo momento, quindi, se avviate il progetto la prima cosa che vedete è:
Impostazione della proprietà UriMapper del Frame
Ritorniamo sul valore della proprietà Source del Frame. In questo momento, esso rappresenta a tutti gli effetti il path fisico del file .xaml che vogliamo caricare e renderizzare. E’ possibile però "giocare†con la proprietà UriMapper del Frame, in modo tale da “convertire†un url logico in uno fisico. Supponiamo di impostarla come segue:
<nav:Frame Background="LightCyan" Source="/Page1">
<nav:Frame.UriMapper>
<uriMapper:UriMapper>
<uriMapper:UriMapping Uri="/{pageName}" MappedUri="/{pageName}.xaml" />
</uriMapper:UriMapper>
</nav:Frame.UriMapper>
</nav:Frame>
Notare l’import del namespace uriMapper, definito come segue:
xmlns:uriMapper="clr-namespace:System.Windows.Navigation;assembly=System.Windows.Controls.Navigation"
Cosa succede adesso? Succede che Source adesso vale “/Page1â€, e ci pensa l’UriMapper a convertirlo nel vero Uri che punta al file .xaml. Tutte le volte che chiediamo una pagina nella forma “/PaginaElencoOrdiniâ€, l’uri effettivo diventerà “/PaginaElencoOrdini.xamlâ€. Ovviamente possiamo sfruttare questa caratteristica in modo più intelligente, per esempio mettendo tutti i files di Page in una cartella e lasciare che l’UriMapper li recuperi per noi. Maggiori informazioni a questo indirizzo.
Ed ora la parte più “difficileâ€: navigare su pagine definite in un assembly esterno
Lo so, sono blog-orroico: questo è il tema principale di questo post, e ci sono arrivato solo alla fine. Supponiamo di voler aggiungere un nuovo progetto – SilverlightClassLibrary1.Pages – contenente solo oggetti di tipo Page. Vogliamo navigare su queste pagine, e raggiungerle attraverso il controllo Frame.
La vera domanda è questa: quale url devo utilizzare? Qual’è l’url corretto che punta ad un file Page1.xaml definito nell’assembly SilverlightClassLibrary1.Pages?
La risposta, una volta trovata la soluzione, è abbastanza semplice. E’ bastato trovare la pagina Pack URIs in WPF. Definiamo il controllo Frame come segue:
<nav:Frame Background="LightCyan" Source="/Page1">
<nav:Frame.UriMapper>
<uriMapper:UriMapper>
<uriMapper:UriMapping Uri="/External/{pageName}"
MappedUri="/SilverlightClassLibrary1.Pages;component/{pageName}.xaml" />
<uriMapper:UriMapping Uri="/{pageName}" MappedUri="/{pageName}.xaml" />
</uriMapper:UriMapper>
</nav:Frame.UriMapper>
</nav:Frame>
Abbiamo aggiunto un UriMapping nuovo. Se la pagina viene richiesta nella forma “/External/{pageName}â€, il Mapped Uri viene risolto così:
/SilverlightClassLibrary1.Pages;component/{pageName}.xaml
Quello che dobbiamo fare poi è aggiungere il riferimento a SilverlightClassLibrary1.Pages nel progetto SilverlightApplication1. Fatto questo, abbiamo terminato.
In pratica, la pagina viene cercata nell’assembly SilverlightClassLibrary1.Pages. Adesso basta reimpostare il Source su “/External/Page1†per caricare la pagina dall’altro assembly. Ovviamente prima di eseguire dobbiamo andare nell’altro assembly e definire anche lì una Page1.xaml – magari con un contenuto diverso rispetto a quella precedente – per ottenere quanto segue:
Missione compiuta!
Il codice sorgente, se proprio volete vederlo – è scaricabile da qui (86 Kb).