Windows 8, applicazioni Metro style, navigazione e MVVM
Tranquilli, non ho tradito Windows Phone per Windows 8
Windows Phone al momento si trova in una situazione un po’ di stallo: la versione 7.5 è ormai sul mercato da più di un anno, perciò diventa sempre più difficile trovare qualcosa di nuovo su cui bloggare a riguardo. Si tratta però di uno stallo temporaneo: nel corso dell’estate Microsoft rilascerà la prima beta del’SDK di Windows Phone 8, allora vedrete che i post su Windows Phone torneranno in gran numero ![]()
In più, alla luce dei recenti annunci, imparare a conoscere WinRT è sicuramente un ottimo trampolino di lancio per preparsi al futuro.
Ma veniamo al dunque: oggi vi voglio parlare di un servizio che vi semplificherà la navigazione da una pagina all’altra nella vostra applicazione XAML / C# sviluppata utilizzando il pattern MVVM. Questo servizio arriva direttamente dalla mia esperienza di sviuppo per Windows Phone; una delle sfide da affrontare nell’utilizzo del pattern MVVM è che solitamente la navigazione verso un’altra pagina viene avviata da qualcosa che si è verificato in un ViewModel: il variare di una proprietà (ad esempio, l’elemento selezionato di una lista) o l’utilizzo di un comando (ad esempio, la pressione di un pulsante). Il problema è che il NavigationService è accessibile solo dal code behind, in quanto fa parte delle proprietà esposte dalla classe PhoneApplicationPage, dalla quale derivano tutte le pagine di Windows Phone.
Per risolvere questo inghippo ci sono diverse strategie: quella da me utilizzata sfrutta un’implementazione di un NavigationService illustrata in un post di Laurent Bugnion. Questo servizio non fa altro che da wrapper al NavigationService reale, offrendo la possibilità in un ViewModel di avviare una navigazione semplicemente inizializzando questo servizio all’interno dello stesso.
Veniamo ora all’applicazione Windows 8: il NavigationService di Windows Phone, così com’è, non è in grado di funzionare, dato che questi utilizza la proprietà Application.Current.RootVisual per accedere al frame principale dell’applicazione e, di conseguenza, gestire gli eventi di navigazione. Tale proprietà non è disponibile in WinRT: ecco perciò che è stato necessario trovare una soluzione alternativa.
La mia prima soluzione, funzionale ma non molto elegante, è stata quella di esporre come proprietà pubblica il rootFrame che viene inizializzato nel file App.xaml.cs: è tramite l’inizializzazione di questa classe che abbiamo la possibilità, nel code behind di una pagina, di avviare una navigazione tramite il metodo Frame.Navigate. In questo modo, l’implementazione del NavigationService era sostanzialmente la stessa di Windows Phone, con la differenza che utilizzavo la proprietà appena definita per accedere al frame e gestire gli eventi di navigazioneal posto di usare Application.Current.RootVisual, come nell’esempio:
_mainFrame = (Application.Current as App).rootFrame;
Il mio amico Joost van Shaik, MVP anche lui nella categoria Windows Phone Development, partendo dal mio approccio è riuscito ad elaborare una soluzione molto più elegante: il costruttore del NavigationService accetta ora come parametro in fase di inizializzazione l’oggetto frame da utilizzare. Sfruttando poi il meccanismo della dependency injection siamo in grado, nell’App.xaml.cs, di registrare all’avvio il NavigationService opportunamente inizializzato con il rootFrame e di utilizzare questa istanza in tutti i nostri view models, mantenendo perciò sempre attivo il riferimento al frame.
Ecco il codice del NavigationService:
public class NavigationService : INavigationService
{
public NavigationService(Frame mainFrame)
{
_mainFrame = mainFrame;
}
private Frame _mainFrame;
public event NavigatingCancelEventHandler Navigating;
public void NavigateTo(Type type)
{
_mainFrame.Navigate(type);
}
public void NavigateTo(Type type, object parameter)
{
_mainFrame.Navigate(type, parameter);
}
public void GoBack()
{
if (_mainFrame.CanGoBack)
{
_mainFrame.GoBack();
}
}
Rispetto al NavigationService di Windows Phone illustrato nel post di Laurent Bugnion le uniche differenze sono:
- Il tipo di parametro passato al metodo NavigateTo, dato che il metodo Frame.Navigate di Windows 8 richiede il tipo della pagina verso cui ci vogliamo spostare, al contrario di quello di Windows Phone che accetta un URL.
- La presenza di un overload del metodo NavigateTo, dato che Windows 8 supporta la possibilità di portare un oggetto da una pagina all’altra, al contrario di Windows Phone che supporta solo il passaggio di parametri tramite query string.
Ecco un esempio di come avviene questa inizializzazione utilizzando uno dei dependency injection container disponibile per WinRT, chiamato MetroIoc. La registrazione avviene all’interno del metodo OnLaunched del file App.xaml.cs, che viene invocato nel momento in cui l’applicazione viene avviata dalla tile principale o da una di quelle secondarie (se supportate).
protected override void OnLaunched(LaunchActivatedEventArgs args)
{
var rootFrame = new Frame();
// Do not repeat app initialization when already running, just ensure that
// the window is active
if (args.PreviousExecutionState == ApplicationExecutionState.Running)
{
Window.Current.Activate();
return;
}
if (args.PreviousExecutionState == ApplicationExecutionState.Terminated || args.PreviousExecutionState==ApplicationExecutionState.NotRunning)
{
ioc.RegisterInstance(typeof (INavigationService), new NavigationService(rootFrame));
}
if (!rootFrame.Navigate(typeof(MainPage)))
{
throw new Exception("Failed to create initial page");
}
// Place the frame in the current Window and ensure that it is active
Window.Current.Content = rootFrame;
Window.Current.Activate();
}
A questo punto all’interno dei vostri view model, previa inizializzazione del NavigationService (tramite dependency injection oppure in maniera tradizionale) potete avviare la navigazione semplicemente chiamando il metodo NavigateTo, come nell’esempio:
public MainViewModel(INavigationService navigationService)
{
navigationService.NavigateTo(typeof(DetailPage));
}
Happy coding!

Recent Comments