2010-10-04 13 views
12

que un botón en una vista, con destino a una propiedad ICommand del modelo de vista (en realidad es RelayCommand de mvvv-luz)¿A dónde pertenece la lógica de navegación, View, ViewModel u otra parte?

Si el usuario hace clic en el botón Quiero navegar a una nueva vista. Por supuesto, NavigationService es parte de View, no ViewModel. Eso implica que la navegación es responsabilidad de View? Pero en mi caso, la vista que iré cuando se haga clic en el botón depende de muchos factores, incluido quién es el usuario conectado, el estado en que se encuentra la base de datos, etc. Seguramente, la Vista no debería necesitar todo Esa información.

¿Cuál es la opción preferida para ejecutar una llamada NavigationService.Navigate?

Respuesta

12

Si ya está utilizando MVVM Light, una opción es hacer uso del bus de mensajes que incluye. Así que vincula su botón a un RelayCommand en el modelo de vista, como ya dijo que ya está haciendo. En el controlador para su RelayCommand, puede tomar la decisión de a qué vista navegar. Esto mantiene toda esa lógica en el modelo de vista.

Una vez que el controlador de comando haya decidido a qué vista navegar, puede publicar un mensaje en el bus de mensajes. Su vista escuchará ese mensaje y luego usará NavigationService para realizar la navegación. Por lo tanto, no está haciendo otra cosa que esperar a que le digan que navegue en algún lugar y luego navegue donde se lo dice.

He estado haciendo esto definiendo una clase de NavigationMessage que mis modelos de vista pueden publicar, y una clase base de vista que mi vista hereda de la que contiene el oyente. El NavigationMessage se ve así:

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; 
    } 
} 

Esto permite simplemente pasar el nombre de la página, o incluyendo también opcionalmente cualquier parámetro de cadena de consulta necesarios. Un controlador RelayCommand sería publicar este mensaje como este:

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

Por último, la clase vista base se parece a esto:

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)); 
    } 
} 

Esto es suponiendo una convención donde todos los puntos de vista están en una carpeta "Vistas" en la raíz de la aplicación. Esto funciona bien para nuestra aplicación, pero por supuesto, esto podría ampliarse para admitir diferentes escenarios sobre cómo organizar sus vistas.

+0

Excelente sugerencia, gracias! –

+0

Admitiré que no estoy muy familiarizado con el sistema Messenger que MVVM-Light ofrece. Si se reflexiona más, ¿no significa que todas las vistas se registrarán y escucharán este NavigationMessage? –

+0

Supongo que podría ser un problema. Estoy usando esta técnica en el contexto de una aplicación Windows Phone 7 donde solo tengo una vista activa a la vez, así que funciona perfectamente. Si está trabajando en la versión de escritorio de Silverlight, o WPF, y tiene varias vistas activas a la vez, puedo ver dónde podría haber un problema. Tendrá que pensar en eso un poco más. –

6

Advertencia: obstinado alerta MVVM novato :) (soy muy nuevo en MVVM, pero disfrutando mucho hasta ahora.)

Buena pregunta. He descubierto que es perfectamente posible (si es un poco feo en algunos lugares) burlarse de NavigationService y pasar un INavigationService a un ViewModel. De hecho, incluso puede hacer que la interfaz sea ligeramente más agradable con los genéricos, para pasar un tipo (como un argumento de tipo) en lugar de un URI de cadena.

Sin embargo, he descubierto que me he deshecho un poco cuando se trata de poner los datos adicionales involucrados en la navegación ... No he encontrado un buen lugar para hacer toda la codificación/descodificación para propagar afirmar prolijamente Sospecho que ViewModelFactory puede ser parte de esa ecuación ...

Por lo tanto, no es una solución perfecta todavía, pero al menos ViewModel puede ser responsable de la acción de "navegar ahora" (o "volver atrás").

+0

Yo también estoy disfrutando de MVVM, hay algunas partes de las plantillas de Silverlight que no son especialmente amigables con MVVM; navegación siendo uno.Gracias por la idea de pasar un tipo en lugar de una cadena como el "uri". eso tiene perfecto sentido en este contexto. –

Cuestiones relacionadas