2010-10-04 5 views
12

Ho un pulsante in una vista, legato a una proprietà ICommand del ViewModel (in realtà è RelayCommand da mvvv-luce)Dove si trova la logica di navigazione, View, ViewModel o altrove?

Se l'utente fa clic sul pulsante che voglio passare a una nuova visione. Ovviamente NavigationService è parte della View not the ViewModel. Ciò implica che la navigazione è responsabilità della vista? Ma nel mio caso, la vista che andrò facendo clic sul pulsante dipende da molti fattori, tra cui chi è l'utente che ha effettuato l'accesso, lo stato in cui si trova il database, ecc ... Sicuramente la vista non dovrebbe aver bisogno di tutti quell'informazione.

Qual è l'opzione preferita per l'esecuzione di una chiamata NavigationService.Navigate?

risposta

12

Se si utilizza già MVVM Light, un'opzione è utilizzare il bus dei messaggi che include. Quindi leghi il tuo pulsante a RelayCommand sul modello di visualizzazione, come hai detto che lo stai già facendo. Nel gestore per RelayCommand puoi decidere quale vista navigare. Ciò mantiene tutta quella logica nel modello di vista.

Una volta che il gestore comandi ha deciso a quale vista navigare, può pubblicare un messaggio sul bus dei messaggi. La tua vista ascolterà quel messaggio e poi userà il NavigationService per eseguire effettivamente la navigazione. Quindi non sta facendo altro che aspettare di essere detto di navigare da qualche parte e poi di navigare dove viene detto.

Ho eseguito questa operazione definendo una classe NavigationMessage che i miei modelli di visualizzazione possono pubblicare e una classe di base di visualizzazione ereditata dalle mie viste e che contiene il listener. Il NavigationMessage assomiglia a questo:

public class NavigationMessage : NotificationMessage 
{ 
    public string PageName 
    { 
     get { return base.Notification; } 
    } 

    public Dictionary<string, string> QueryStringParams { get; private set; } 

    public NavigationMessage(string pageName) : base(pageName) { } 

    public NavigationMessage(string pageName, Dictionary<string, string> queryStringParams) : this(pageName) 
    { 
     QueryStringParams = queryStringParams; 
    } 
} 

Questo permette semplicemente passando il nome della pagina, o eventualmente anche inclusi eventuali parametri di stringa di query necessari. Un gestore RelayCommand avrebbe pubblicato questo messaggio come questo:

private void RelayCommandHandler() 
{ 
    //Logic for determining next view, then ... 
    Messenger.Default.Send(new NavigationMessage("ViewToNavigate")); 
} 

Infine, la classe di visualizzazione di base si presenta così:

public class BasePage : PhoneApplicationPage 
{ 
    public BasePage() 
    { 
     Messenger.Default.Register<NavigationMessage>(this, NavigateToPage); 
    } 

    protected void NavigateToPage(NavigationMessage message) 
    { 
     //GetQueryString isn't shown, but is simply a helper method for formatting the query string from the dictionary 
     string queryStringParams = message.QueryStringParams == null ? "" : GetQueryString(message); 

     string uri = string.Format("/Views/{0}.xaml{1}", message.PageName, queryStringParams); 
     NavigationService.Navigate(new Uri(uri, UriKind.Relative)); 
    } 
} 

Ciò presuppone una convenzione in cui tutti i punti di vista sono in una cartella "Vista" nella radice dell'app. Questo funziona bene per la nostra app, ma naturalmente questo potrebbe essere esteso per supportare diversi scenari per come organizzi le tue visualizzazioni.

+0

Eccellente suggerimento, grazie! –

+0

Devo ammettere che non ho molta familiarità con il sistema Messenger offerto da MVVM-Light. Se si riflette ulteriormente, ciò non significa che tutte le viste si registreranno e ascolteranno questo NavigationMessage? –

+0

Suppongo che potrebbe essere un problema. Sto usando questa tecnica nel contesto di un'app di Windows Phone 7 in cui ho solo una vista attiva alla volta, quindi funziona perfettamente. Se stai lavorando nella versione desktop di Silverlight, o WPF, e hai più viste attive contemporaneamente, posso vedere dove questo potrebbe essere un problema. Dovrà pensarci un po 'di più. –

6

Attenzione: supponente avviso MVVM newbie :) (io sono molto nuovo a MVVM, ma godendo un sacco finora.)

Buona domanda. Ho scoperto che è perfettamente fattibile (se un po 'brutto in alcuni punti) prendere in giro NavigationService e passare un INavigationService a un ViewModel. In effetti, puoi persino rendere l'interfaccia leggermente più carina con i generici, per passare in un tipo (come argomento tipo) piuttosto che come un URI di stringa.

Tuttavia, ho scoperto che mi sono un po 'scollato quando si tratta di inserire i dati aggiuntivi coinvolti nella navigazione ... Non ho trovato un buon posto per fare tutte le codifiche/unencoding da propagare stato ordinatamente Sospetto che ViewModelFactory possa far parte di questa equazione ...

Quindi, non è ancora una soluzione perfetta, ma almeno ViewModel può essere responsabile dell'azione di "navigare ora" (o "tornare indietro").

+0

Anche io mi sto godendo MVVM, a quanto pare ci sono alcuni dei modelli Silverlight che non sono particolarmente compatibili con MVVM; navigazione essendo uno.Grazie per l'idea di passare un tipo piuttosto che una stringa come "uri". questo ha perfettamente senso in questo contesto. –