Saltar al contenido

Artículos, tutoriales, trucos, curiosidades, reflexiones y links sobre programación web ASP.NET Core, MVC, Blazor, SignalR, Entity Framework, C#, Azure, Javascript... y lo que venga ;)

17 años online

el blog de José M. Aguilar

Inicio El autor Contactar

Artículos, tutoriales, trucos, curiosidades, reflexiones y links sobre programación web
ASP.NET Core, MVC, Blazor, SignalR, Entity Framework, C#, Azure, Javascript...

¡Microsoft MVP!
miércoles, 30 de mayo de 2012
ASP.NET MVCComo venimos comentando desde la aparición de la preview de MVC 4, el Display Mode o modo de visualización es un nuevo mecanismo que permite la creación de aplicaciones capaces de retornar vistas específicas según las características y capacidades del cliente conectado u otros parámetros del entorno de la petición.

Esto tiene mucha utilidad directa, por ejemplo, a la hora de construir aplicaciones web que adapten su interfaz en función de si el cliente es un dispositivo móvil o desktop, pero como casi siembre ocurre en el framework, se trata de un mecanismo muy extensible y fácil de adaptar a otras necesidades.

¿Cómo funciona?

Las aplicaciones, tanto ASP.NET MVC 4 como WebPages 2, disponen de una lista de display modes admitidos, a la que se puede acceder a través de la propiedad DisplayModeProvider.Instance.Modes. Esta colección está poblada inicialmente por dos elementos, aunque podemos añadir todos los que nos interesen siempre que éstos implementen el interfaz IDisplayMode, cuya definición es la siguiente:
public interface IDisplayMode
{
    string DisplayModeId { get; } 
    bool CanHandleContext(HttpContextBase httpContext);

    DisplayInfo GetDisplayInfo(HttpContextBase httpContext, 
                               string virtualPath,
                               Func<string, bool> virtualPathExists);
}
Cuando llega una petición, el mismo sistema de routing recorrerá la colección DisplayModeProvider.Instance.Modes, e invocará el método CanHandleContext() de cada uno de los objetos IDisplayMode encontrados en ella. El primero que responda afirmativamente será introducido en el contexto de la petición como modo de visualización activo.

A partir de ese momento es posible consultar el display mode actual a través de la propiedad DisplayMode disponible en la clase ControllerContext .

Continuando con el ciclo de vida de la petición, una vez ésta ha sido procesada por el controlador, si éste retorna una vista entrarán en juego los motores de vistas o view engines. Éstos son los encargados, entre otras cosas, de localizar el archivo de plantilla (.cshtml, aspx, etc.) a usar para renderizar la vista en función del nombre de ésta, del nombre del controlador, y, si procede, del área activa. Por ejemplo, si es necesario retornar una vista llamada “index”, del controlador “customers”, en el área “admin”, el motor de vistas Razor (implementado en la clase RazorViewEngine) será capaz de localizar y crear la vista a partir de la plantilla disponible en /areas/admin/views/customers/index.cshtml.

Pues bien, el modo de visualización activo toma relevancia justo en el momento de la localización de la plantilla. El view engine encargado de procesar la vista llamará al método GetDisplayInfo() de éste, desde donde tendrá la oportunidad de modificar la ruta hacia el archivo. En la implementación por defecto, el identificador del display mode activo, tomado de su propiedad DisplayModeId, será insertado como prefijo de la extensión del archivo, de forma, por ejemplo, que la vista que tradicionalmente hubiéramos encontrado en “~/views/home/index.cshtml”, usando el modo de visualización identificado como “mobile” se encontraría en “~/views/home/index.mobile.cshtml”.

Display Modes por defecto

Antes había comentado que al arrancar una aplicación MVC 4 o WebPages 2, la colección de modos de visualización, DisplayModeProvider.Instance.Modes, es cargada con dos elementos. Ambos son del tipo DefaultDisplayMode, clase que, por supuesto, implementa IDisplayMode, e implementa unas bases bastante genéricas y reutilizables.

El primer display mode que encontramos es el específico para dispositivos móviles, que nivel a de framework se define más o menos así:
var displayMode = new DefaultDisplayMode(DisplayModeProvider.MobileDisplayModeId)
                        {
                            ContextCondition =
                                context => context.GetOverriddenBrowser().IsMobileDevice
                        };
El parámetro que estamos enviando al constructor es el identificador que daremos al modo de visualización (y, por tanto, que será usado como prefijo de la extensión del archivo de la vista). La constante DisplayModeProvider.MobileDisplayModeId está definida con un valor de “Mobile” en el código, y por esta razón las vistas específicas para móviles las encontraremos en archivos del tipo “xyz.mobile.cshtml”.

Por otra parte, haciendo uso de la inicialización rápida de objetos de C#, establecemos en la propiedad ContextCondition la condición que activará el display mode, expresado como lambda. La expresión lambda recibe un parámetro de tipo HttpContextBase y debe retornar un booleano, que será el valor que retorne el método CanHandleContext(), como recordaréis, invocado por el framework para averiguar si un IDisplayMode puede procesar una petición.

Por cierto, el  GetOverridenBrowser() que veis ahí es otra característica nueva que encontraremos en MVC 4; de momento, quedaos con que, simplemente, nos devuelte información sobre el cliente que ha realizado la petición. Le dedicaremos un post en exclusiva más adelante ;-)

El segundo display mode que encontramos en DisplayModeProvider.Instance.Modes es una instancia de la clase DefaultDisplayMode a la que no se ha establecido ninguna propiedad; las llamadas a su método CanHandleContext() siempre retornan true, y su identificador es una cadena vacía.

Teniendo este objeto al final de la lista al más puro estilo catch-all, conseguimos que si ninguno de los display modes es capaz de gestionar adecuadamente la petición, se procesará éste, y dado que su identificador es una cadena de texto en blanco, no se producirá ningún cambio en el nombre del archivo. En otras palabras, el framework seguirá funcionando como hasta ahora, por lo que este elemento es, al fin y al cabo, el que garantiza la compatibilidad hacia atrás de este mecanismo.

Creación de display modes personalizados

Como ya podréis intuir, es bastante sencillo crear nuevos display modes, cuya activación dependa de las condiciones que más nos interesen. Lo más sencillo es utilizar como base el DefaultDisplayMode, por ejemplo así:
DisplayModeProvider.Instance.Modes.Insert(0, new DefaultDisplayMode("iPhone")
{
    ContextCondition = (context => context.Request.UserAgent.IndexOf
        ("iPhone", StringComparison.OrdinalIgnoreCase) >= 0)
});
Con el código anterior estaríamos creando un nuevo display mode que se activaría cuando en el user-agent de la petición (enviado en el encabezado HTTP) se encuentre el texto “iPhone”, lo que permitiría crear vistas específicas para este dispositivo nombrándolas de la forma “xyz.iphone.cshtml”.

Otra posibilidad sería crear una clase que implemente IDisplayMode e introducir en ella la lógica que deseemos ejecutar en cada uno de los miembros definidos en el contrato. Pero el ejemplo y el escenario en el que podría ser utilizado lo dejamos para un futuro post ;-)


Publicado en: Variable not found.

9 Comentarios:

Pablo Rausch dijo...

Interesante. Buen Aporte!

josé M. Aguilar dijo...

Gracias, Pablo!

Anónimo dijo...

Como puedo obtener el DisplayMode que se está utilizando?.
Intente con
@ViewContext.DisplayMode.DisplayModeId
pero siempre devuelve un texto vacio

josé M. Aguilar dijo...

Hola!

En principio, como lo estás haciendo es correcto.

Lo único que tienes que hacer es asegurarte de que al crear tu DisplayMode tiene el Id asignado cuando lo añades a la relación de DisplayModes.

var dm = new DefaultDisplayMode("MyDm") { ... }

Y recuerda que cuando este Display Mode se active, las vistas tienes que codificarlas usando como prefijo el DisplayModeId.

Saludos.

martinib dijo...

Muchas gracias por tu respuesta. Te comento que asi estoy agregando los displayModes, con Id. Sin embargo en ningun momento he logrado obtener el actual con @ViewContext.DisplayMode.DisplayModeId.
Me podrás ayudar a identificar el problema?. Gracias.

josé M. Aguilar dijo...

Hola!

Pues algo se debe estar quedando por atrás, porque a mí sí me funciona...

Pruebas que puedes hacer para comprobar:

1) el DisplayModeId para las vistas normales es, por defecto, la cadena vacía. Lo más probable es que sea éste el que está activo en ese momento. Comprueba que las condiciones de activación son correctas.

2) Puedes probar a mostrar por pantalla (o donde sea) directamente el objeto DisplayMode. Al menos verás la clase del DisplayMode actual (DefaultDisplayMode u otras que estés usando).

3) Prueba a introducir en la primera posición de la lista de DisplayModes un nuevo DefaultDisplayMode cuya condición retorne siempre true para forzar a que sea éste el usado siempre. Haz que su id sea, por ejemplo, "hola", y crea una vista home/index.hola.cshtml (sin layouts ni parciales ni nada que te pueda estorbar), y comprueba que es ella la que se muestra. En este caso, podrás ver que la propiedad DisplayModeId es "hola".

4) Comprueba que tienes actualizado MVC 4 a la versión RTM, no sea que estés con una preview que no tuviera eso bien implementado.

Con esto deberías dar con el tema.

Suerte!

martinib dijo...

Gracias. He visto cual es el problema. La información del Id la muestra únicamente cuando la vista principal esta definida para un modo (.mobile por ejemplo). En mi caso estoy usando un partial. En ese caso, el Id queda vacio.
Una pena porque creo es útil saber que modo se va a utilizar para la pagina.

Cesar dijo...

Hola Jose M.

Aquí no se puede utilizar un diseño adaptativo en vez de DisplayModes.

Saludos

José María Aguilar dijo...

Hola, César!

Por supuesto :) Los DisplayModes sólo tienen utilidad si piensas crear una vista totalmente diferente para uno o varios dispositivos específicos, pero lo más recomendable sería siempre intentar crear un diseño responsive o adaptativo para poder reutilizar el código al máximo.

Saludos!