Creación de vistas HTML mediante plantillas de Razor

En el mundo del desarrollo móvil, el término "aplicación híbrida" suele hacer referencia a una aplicación que presenta algunas (o todas) de sus pantallas como páginas HTML dentro de un control de visor web hospedado.

Hay algunos entornos de desarrollo que permiten compilar la aplicación móvil completamente en HTML y JavaScript; sin embargo, esas aplicaciones pueden sufrir problemas de rendimiento al intentar lograr efectos complejos de procesamiento o interfaz de usuario y también están limitadas en las características de la plataforma a las que pueden acceder.

Xamarin ofrece lo mejor de ambos mundos, especialmente cuando se utiliza el motor de plantillas HTML de Razor. Con Xamarin tiene la flexibilidad de crear vistas HTML con plantilla multiplataforma que usan JavaScript y CSS, pero también tienen acceso completo a las API de plataforma subyacentes y procesamiento rápido mediante C#.

En este documento se explica cómo usar el motor de plantillas de Razor para compilar vistas HTML+JavaScript+CSS que se pueden usar en plataformas móviles mediante Xamarin.

Usar vistas web mediante programación

Antes de obtener información sobre Razor, en esta sección se explica cómo usar vistas web para mostrar contenido HTML directamente, en concreto el contenido HTML que se genera dentro de una aplicación.

Xamarin proporciona acceso completo a las API de plataforma subyacentes en iOS y Android, por lo que es fácil crear y mostrar HTML mediante C#. A continuación se muestra la sintaxis básica de cada plataforma.

iOS

Mostrar HTML en un control UIWebView en Xamarin.iOS también toma solo unas pocas líneas de código:

var webView = new UIWebView (View.Bounds);
View.AddSubview(webView);
string contentDirectoryPath = Path.Combine (NSBundle.MainBundle.BundlePath, "Content/");
var html = "<html><h1>Hello</h1><p>World</p></html>";
webView.LoadHtmlString(html, NSBundle.MainBundle.BundleUrl);

Consulte las recetas de UIWebView de iOS para obtener más detalles sobre el uso del control UIWebView.

Android

La visualización de HTML en un control WebView mediante Xamarin.Android se realiza en unas pocas líneas de código:

// webView is declared in an AXML layout file
var webView = FindViewById<WebView> (Resource.Id.webView);

// enable JavaScript execution in your html view so you can provide "alerts" and other js
webView.SetWebChromeClient(new WebChromeClient());

var html = "<html><h1>Hello</h1><p>World</p></html>";
webView.LoadDataWithBaseURL("file:///android_asset/", html, "text/html", "UTF-8", null);

Consulte las recetas de Android WebView para obtener más detalles sobre el uso del control WebView.

Especificar el directorio base

En ambas plataformas hay un parámetro que especifica el directorio base para la página HTML. Esta es la ubicación en el sistema de archivos del dispositivo que se usa para resolver referencias relativas a recursos como imágenes y archivos CSS. Por ejemplo, etiquetas como

<link rel="stylesheet" href="style.css" />
<img src="monkey.jpg" />
<script type="text/javascript" src="jscript.js">

consulte estos archivos: style.css, monkey.jpg y jscript.js. La configuración del directorio base indica a la vista web dónde se encuentran estos archivos para que se puedan cargar en la página.

iOS

La salida de la plantilla se representa en iOS con el siguiente código de C#:

webView.LoadHtmlString (page, NSBundle.MainBundle.BundleUrl);

El directorio base se especifica como que NSBundle.MainBundle.BundleUrl hace referencia al directorio en el que está instalada la aplicación. Todos los archivos de la carpeta Resources se copian en esta ubicación, como el archivo style.css que se muestra aquí:

Solución iPhoneHybrid

La acción de compilación para todos los archivos de contenido estático debe ser BundleResource:

Acción de compilación del proyecto de iOS: BundleResource

Android

Android también requiere que se pase un directorio base como parámetro cuando las cadenas HTML se muestran en una vista web.

webView.LoadDataWithBaseURL("file:///android_asset/", page, "text/html", "UTF-8", null);

La cadena especial file:///android_asset/ hace referencia a la carpeta Recursos de Android de la aplicación, que se muestra aquí que contiene el archivo style.css.

Solución AndroidHybrid

La acción de compilación para todos los archivos de contenido estático debe ser AndroidAsset.

Acción de compilación del proyecto de Android: AndroidAsset

Llamada a C# desde HTML y JavaScript

Cuando se carga una página HTML en una vista web, trata los vínculos y formularios como lo haría si la página se cargara desde un servidor. Esto significa que si el usuario hace clic en un vínculo o envía un formulario, la vista web intentará navegar al destino especificado.

Si el vínculo es a un servidor externo (como google.com), la vista web intentará cargar el sitio web externo (suponiendo que haya una conexión a Internet).

<a href="http://google.com/">Google</a>

Si el vínculo es relativo, la vista web intentará cargar ese contenido desde el directorio base. Obviamente, no se requiere ninguna conexión de red para que funcione, ya que el contenido se almacena en la aplicación en el dispositivo.

<a href="somepage.html">Local content</a>

Las acciones de formulario siguen la misma regla.

<form method="get" action="http://google.com/"></form>
<form method="get" action="somepage.html"></form>

No va a hospedar un servidor web en el cliente; Sin embargo, puede usar las mismas técnicas de comunicación de servidor empleadas en los patrones de diseño dinámicos actuales para llamar a servicios a través de HTTP GET y controlar las respuestas de forma asincrónica mediante la emisión de JavaScript (o la llamada a JavaScript ya hospedada en la vista web). Esto le permite pasar fácilmente datos del código HTML al código de C# para su procesamiento y, a continuación, volver a mostrar los resultados en la página HTML.

Tanto iOS como Android proporcionan un mecanismo para que el código de la aplicación intercepte estos eventos de navegación para que el código de la aplicación pueda responder (si es necesario). Esta característica es fundamental para compilar aplicaciones híbridas porque permite que el código nativo interactúe con la vista web.

iOS

El evento ShouldStartLoad de la vista web en iOS se puede invalidar para permitir que el código de la aplicación controle una solicitud de navegación (por ejemplo, un clic de vínculo). Los parámetros del método suministran toda la información

bool HandleShouldStartLoad (UIWebView webView, NSUrlRequest request, UIWebViewNavigationType navigationType) {
    // return true if handled in code
    // return false to let the web view follow the link
}

y, a continuación, asigne el controlador de eventos:

webView.ShouldStartLoad += HandleShouldStartLoad;

Android

En Android, simplemente subclase WebViewClient y, a continuación, implemente código para responder a la solicitud de navegación.

class HybridWebViewClient : WebViewClient {
    public override bool ShouldOverrideUrlLoading (WebView webView, IWebResourceRequest request) {
        // return true if handled in code
        // return false to let the web view follow the link
    }
}

y, a continuación, establezca el cliente en la vista web:

webView.SetWebViewClient (new HybridWebViewClient ());

Llamada a JavaScript desde C #

Además de decir a una vista web que cargue una nueva página HTML, el código de C# también puede ejecutar JavaScript en la página que se muestra actualmente. Los bloques de código de JavaScript completos se pueden crear mediante cadenas de C# y ejecutarse, o bien puede crear llamadas de método a JavaScript que ya están disponibles en la página a través de script etiquetas.

Android

Cree el código JavaScript que se va a ejecutar y, a continuación, prefijéndolo con "javascript:" e indique a la vista web que cargue esa cadena:

var js = "alert('test');";
webView.LoadUrl ("javascript:" + js);

iOS

Las vistas web de iOS proporcionan un método específico para llamar a JavaScript:

var js = "alert('test');";
webView.EvaluateJavascript (js);

Resumen

En esta sección se han presentado las características de los controles de vista web en Android e iOS que nos permiten compilar aplicaciones híbridas con Xamarin, entre las que se incluyen:

  • La capacidad de cargar HTML desde cadenas generadas en el código,
  • La capacidad de hacer referencia a archivos locales (CSS, JavaScript, Imágenes u otros archivos HTML),
  • La capacidad de interceptar solicitudes de navegación en código de C#,
  • La capacidad de llamar a JavaScript desde código de C#.

En la sección siguiente se presenta Razor, lo que facilita la creación del código HTML para su uso en aplicaciones híbridas.

¿Qué es Razor?

Razor es un motor de plantillas que se introdujo con ASP.NET MVC, originalmente para ejecutarse en el servidor y generar HTML para servirlo a los exploradores web.

El motor de plantillas de Razor amplía la sintaxis HTML estándar con C# para que pueda expresar el diseño e incorporar fácilmente hojas de estilos CSS y JavaScript. La plantilla puede hacer referencia a una clase Model, que puede ser cualquier tipo personalizado y cuyas propiedades se pueden tener acceso directamente desde la plantilla. Una de sus principales ventajas es la capacidad de mezclar fácilmente la sintaxis HTML y C#.

Las plantillas de Razor no se limitan al uso del lado servidor, sino que también se pueden incluir en aplicaciones de Xamarin. El uso de plantillas de Razor junto con la capacidad de trabajar con vistas web mediante programación permite crear aplicaciones híbridas multiplataforma sofisticadas con Xamarin.

Conceptos básicos de la plantilla de Razor

Los archivos de plantilla de Razor tienen una extensión de archivo .cshtml. Se pueden agregar a un proyecto de Xamarin desde la sección Plantillas de texto del cuadro de diálogo Nuevo archivo:

Nuevo archivo: plantilla de Razor

A continuación se muestra una plantilla de Razor simple (RazorView.cshtml).

@model string
<html>
    <body>
    <h1>@Model</h1>
    </body>
</html>

Observe las siguientes diferencias con respecto a un archivo HTML normal:

  • El símbolo tiene un significado especial en las plantillas de Razor: indica que la @ siguiente expresión es C# que se va a evaluar.
  • @model La directiva siempre aparece como la primera línea de un archivo de plantilla de Razor.
  • La @model directiva debe ir seguida de un tipo. En este ejemplo se pasa una cadena simple a la plantilla, pero podría ser cualquier clase personalizada.
  • Cuando se hace referencia a través de la plantilla, proporciona una referencia al objeto pasado a la plantilla cuando se genera (en este ejemplo será @Model una cadena).
  • El IDE generará automáticamente una clase parcial para las plantillas (archivos con la extensión .cshtml). Puede ver este código, pero no se debe editar. RazorView.cshtml La clase parcial se denomina RazorView para que coincida con el nombre de archivo de plantilla .cshtml. Es este nombre el que se usa para hacer referencia a la plantilla en código de C#.
  • @using También se pueden incluir instrucciones en la parte superior de una plantilla de Razor para incluir espacios de nombres adicionales.

A continuación, se puede generar la salida HTML final con el siguiente código de C#. Tenga en cuenta que el modelo se especifica como una cadena "Hola mundo" que se incorporará a la salida de la plantilla representada.

var template = new RazorView () { Model = "Hello World" };
var page = template.GenerateString ();

Esta es la salida que se muestra en una vista web en el simulador de iOS y android Emulator:

Hola mundo

Más sintaxis de Razor

En esta sección vamos a presentar algunos datos básicos sintaxis Razor ayudarle a empezar a usarlo. Los ejemplos de esta sección rellenan la siguiente clase con datos y la muestran mediante Razor:

public class Monkey {
    public string Name { get; set; }
    public DateTime Birthday { get; set; }
    public List<string> FavoriteFoods { get; set; }
}

Todos los ejemplos usan el siguiente código de inicialización de datos

var animal = new Monkey {
    Name = "Rupert",
    Birthday=new DateTime(2011, 04, 01),
    FavoriteFoods = new List<string>
        {"Bananas", "Banana Split", "Banana Smoothie"}
};

Mostrar las propiedades del modelo

Cuando el modelo es una clase con propiedades, se hace referencia a ellos fácilmente en la plantilla de Razor, como se muestra en esta plantilla de ejemplo:

@model Monkey
<html>
    <body>
    <h1>@Model.Name</h1>
    <p>Birthday: @(Model.Birthday.ToString("d MMMM yyyy"))</p>
    </body>
</html>

Esto se puede representar en una cadena mediante el código siguiente:

var template = new RazorView () { Model = animal };
var page = template.GenerateString ();

La salida final se muestra aquí en una vista web en el simulador de iOS y android Emulator:

Rupert

Instrucciones de C#

Se puede incluir C# más complejo en la plantilla, como las actualizaciones de la propiedad Model y el cálculo Age de este ejemplo:

@model Monkey
<html>
    <body>
    @{
        Model.Name = "Rupert X. Monkey";
        Model.Birthday = new DateTime(2011,3,1);
    }
    <h1>@Model.Name</h1>
    <p>Birthday: @Model.Birthday.ToString("d MMMM yyyy")</p>
    <p>Age: @(Math.Floor(DateTime.Now.Date.Subtract (Model.Birthday.Date).TotalDays/365)) years old</p>
    </body>
</html>

Puede escribir expresiones complejas de C# de una sola línea (como dar formato a la edad) rodeando el código con @() .

Se pueden escribir varias instrucciones de C# si se rodean con @{} .

Instrucciones If-else

Las ramas de código se pueden expresar con @if como se muestra en este ejemplo de plantilla.

@model Monkey
<html>
    <body>
    <h1>@Model.Name</h1>
    <p>Birthday: @(Model.Birthday.ToString("d MMMM yyyy"))</p>
    <p>Age: @(Math.Floor(DateTime.Now.Date.Subtract (Model.Birthday.Date).TotalDays/365)) years old</p>
    <p>Favorite Foods:</p>
    @if (Model.FavoriteFoods.Count == 0) {
        <p>No favorites</p>
    } else {
        <p>@Model.FavoriteFoods.Count favorites</p>
    }
    </body>
</html>

Bucles

También se pueden agregar construcciones foreach de bucle como . El @ prefijo se puede usar en la variable de bucle ( en este @food caso) para representarlo en HTML.

@model Monkey
<html>
    <body>
    <h1>@Model.Name</h1>
    <p>Birthday: @Model.Birthday.ToString("d MMMM yyyy")</p>
    <p>Age: @(Math.Floor(DateTime.Now.Date.Subtract (Model.Birthday.Date).TotalDays/365)) years old</p>
    <p>Favorite Foods:</p>
    @if (Model.FavoriteFoods.Count == 0) {
        <p>No favorites</p>
    } else {
        <ul>
            @foreach (var food in @Model.FavoriteFoods) {
                <li>@food</li>
            }
        </ul>
    }
    </body>
</html>

La salida de la plantilla anterior se muestra en ejecución en el simulador de iOS y android Emulator:

Rupert X Monkey

En esta sección se han abordado los conceptos básicos del uso de plantillas de Razor para representar vistas sencillas de solo lectura. En la sección siguiente se explica cómo compilar aplicaciones más completas con Razor que pueden aceptar la entrada del usuario e interoperar entre JavaScript en la vista HTML y C#.

Uso de plantillas de Razor con Xamarin

En esta sección se explica cómo usar la compilación de su propia aplicación híbrida mediante las plantillas de solución de Visual Studio para Mac. Hay tres plantillas disponibles en la ventana File New > Solution... (Nueva solución de archivo):

  • Aplicación Android > Android WebView Application
  • Aplicación > WebView de aplicación iOS
  • ASP.NET MVC Project

La ventana Nueva solución tiene este aspecto para los proyectos de iPhone y Android: la descripción de la solución de la derecha resalta la compatibilidad con el motor de templaciones de Razor.

Creación de iPhone y soluciones de Android

Tenga en cuenta que puede agregar fácilmente una plantilla de Razor .cshtml a cualquier proyecto de Xamarin existente, no es necesario usar estas plantillas de solución. Los proyectos de iOS tampoco requieren un guión gráfico para usar Razor; simplemente agregue un control UIWebView a cualquier vista mediante programación y puede representar plantillas de Razor enteras en código de C#.

A continuación se muestra el contenido de la solución de plantilla predeterminada para iPhone y proyectos de Android:

iPhone y plantillas de Android

Las plantillas le dan una infraestructura de aplicaciones lista para usar para cargar una plantilla de Razor con un objeto de modelo de datos, procesar la entrada del usuario y comunicarse con el usuario a través de JavaScript.

Las partes importantes de la solución son:

  • Contenido estático, como el archivo style.css.
  • Archivos de plantilla .cshtml de Razor como RazorView.cshtml.
  • Clases de modelo a las que se hace referencia en las plantillas de Razor, como ExampleModel.cs.
  • Clase específica de la plataforma que crea la vista web y representa la plantilla, como en MainActivity Android y iPhoneHybridViewController en iOS.

En la sección siguiente se explica cómo funcionan los proyectos.

Contenido estático

El contenido estático incluye hojas de estilos CSS, imágenes, archivos JavaScript u otro contenido al que se puede vincular o hacer referencia mediante un archivo HTML que se muestra en una vista web.

Los proyectos de plantilla incluyen una hoja de estilos mínima para mostrar cómo incluir contenido estático en la aplicación híbrida. En la plantilla se hace referencia a la hoja de estilos CSS de la siguiente forma:

<link rel="stylesheet" href="style.css" />

Puede agregar cualquier hoja de estilos y archivos de JavaScript que necesite, incluidos marcos como JQuery.

Plantillas cshtml de Razor

La plantilla incluye un archivo .cshtml de Razor que tiene código escrito previamente para ayudar a comunicar datos entre HTML/JavaScript y C#. Esto le permitirá crear aplicaciones híbridas sofisticadas que no solo muestren datos de solo lectura del modelo, sino que también acepten la entrada del usuario en el código HTML y las pasen de vuelta al código de C# para su procesamiento o almacenamiento.

Representación de la plantilla

Al llamar GenerateString a en una plantilla, se representa HTML listo para mostrarse en una vista web. Si la plantilla usa un modelo, se debe proporcionar antes de la representación. En este diagrama se muestra cómo funciona la representación, no que la vista web resuelve los recursos estáticos en tiempo de ejecución, mediante el directorio base proporcionado para buscar los archivos especificados.

Diagrama de flujo de Razor

Llamada a código de C# desde la plantilla

La comunicación desde una vista web representada que llama a C# se realiza estableciendo la dirección URL de la vista web y, a continuación, interceptando la solicitud en C# para controlar la solicitud nativa sin volver a cargar la vista web.

Se puede ver un ejemplo de cómo se controla el botón de RazorView. El botón tiene el siguiente código HTML:

<input type="button" name="UpdateLabel" value="Click" onclick="InvokeCSharpWithFormValues(this)" />

La InvokeCSharpWithFormValues función de JavaScript lee todos los valores del formulario HTML y establece para location.href la vista web:

location.href = "hybrid:" + elm.name + "?" + qs;

Esto intenta navegar por la vista web a una dirección URL con un esquema personalizado (por ejemplo, hybrid:)

hybrid:UpdateLabel?textbox=SomeValue&UpdateLabel=Click

Cuando la vista web nativa procesa esta solicitud de navegación, tenemos la oportunidad de interceptarla. En iOS, esto se hace controlando el evento HandleShouldStartLoad de UIWebView. En Android, simplemente subclasificamos el webViewClient usado en el formulario e invalidamos ShouldOverrideUrlLoading.

Los elementos internos de estos dos interceptores de navegación son básicamente los mismos.

En primer lugar, compruebe la dirección URL que la vista web está intentando cargar y, si no comienza con el esquema personalizado ( ), permita que la navegación se hybrid: produzca de la forma normal.

Para el esquema de dirección URL personalizada, todo lo que hay en la dirección URL entre el esquema y "?" es el nombre del método que se va a controlar (en este caso, "UpdateLabel"). Todo lo que contiene la cadena de consulta se tratará como los parámetros de la llamada al método :

var resources = url.Substring(scheme.Length).Split('?');
var method = resources [0];
var parameters = System.Web.HttpUtility.ParseQueryString(resources[1]);

UpdateLabel en este ejemplo realiza una cantidad mínima de manipulación de cadenas en el parámetro de cuadro de texto (anteponer "C# dice" a la cadena) y, a continuación, vuelve a llamar a la vista web.

Después de controlar la dirección URL, el método anula la navegación para que la vista web no intente terminar de navegar a la dirección URL personalizada.

Manipulación de la plantilla desde C #

La comunicación con una vista web HTML representada desde C# se realiza mediante una llamada a JavaScript en la vista web. En iOS, esto se hace mediante una llamada EvaluateJavascript a en UIWebView:

webView.EvaluateJavascript (js);

En Android, JavaScript se puede invocar en la vista web cargando JavaScript como una dirección URL mediante el "javascript:" esquema de dirección URL:

webView.LoadUrl ("javascript:" + js);

Hacer que una aplicación sea realmente híbrida

Estas plantillas no usan controles nativos en cada plataforma: toda la pantalla se rellena con una sola vista web.

HTML puede ser excelente para la creación de prototipos y mostrar los tipos de cosas en las que la web es mejor, como texto enriquecido y diseño dinámico. Sin embargo, no todas las tareas son adecuadas para HTML y JavaScript: desplazarse por listas largas de datos, por ejemplo, funciona mejor mediante controles de interfaz de usuario nativos (como UITableView en iOS o ListView en Android).

Las vistas web de la plantilla se pueden aumentar fácilmente con controles específicos de la plataforma: simplemente edite MainStoryboard.storyboard mediante Xcode en un equipo Mac o Resources/layout/Main.axml en Android.

Ejemplo de RazorTodo

El repositorio RazorTodo contiene dos soluciones independientes para mostrar las diferencias entre una aplicación completamente controlada por HTML y una aplicación que combina HTML con controles nativos:

  • RazorTodo: aplicación completamente controlada por HTML mediante plantillas de Razor.
  • RazorNativeTodo: usa controles de vista de lista nativos para iOS y Android, pero muestra la pantalla de edición con HTML y Razor.

Estas aplicaciones de Xamarin se ejecutan en iOS y Android, usando bibliotecas de clases portables (PCL) para compartir código común, como la base de datos y las clases de modelo. Las plantillas .cshtml de Razor también se pueden incluir en la PCL para que se compartan fácilmente entre plataformas.

Ambas aplicaciones de ejemplo incorporan el uso compartido de Twitter y las API de texto a voz de la plataforma nativa, lo que demuestra que las aplicaciones híbridas con Xamarin todavía tienen acceso a toda la funcionalidad subyacente desde vistas basadas en plantillas razor HTML.

La aplicación RazorTodo usa plantillas de Razor HTML para las vistas de lista y edición. Esto significa que podemos compilar la aplicación casi por completo en una biblioteca de clases portable compartida (incluida la base de datos y las plantillas de Razor .cshtml). En las capturas de pantalla siguientes se muestran las aplicaciones iOS y Android.

RazorTodo

La aplicación RazorNativeTodo usa una plantilla de Razor HTML para la vista de edición, pero implementa una lista de desplazamiento nativa en cada plataforma. Esto proporciona una serie de ventajas, entre las que se incluyen:

  • Rendimiento: los controles de desplazamiento nativos usan la virtualización para garantizar un desplazamiento rápido y sin problemas, incluso con listas de datos muy largas.
  • Experiencia nativa: los elementos de interfaz de usuario específicos de la plataforma se habilitan fácilmente, como la compatibilidad con índices de desplazamiento rápido en iOS y Android.

Una ventaja clave de la creación de aplicaciones híbridas con Xamarin es que puede empezar con una interfaz de usuario completamente controlada por HTML (como el primer ejemplo) y, a continuación, agregar funcionalidad específica de la plataforma cuando sea necesario (como se muestra en el segundo ejemplo). A continuación se muestran las pantallas de lista nativa y las pantallas de edición de Razor HTML en iOS y Android.

RazorNativeTodo

Resumen

En este artículo se han explicado las características de los controles de vista web disponibles en iOS y Android que facilitan la creación de aplicaciones híbridas.

A continuación, se ha analizado el motor de plantillas de Razor y la sintaxis que se puede usar para generar HTML fácilmente en aplicaciones de Xamarin mediante . cshtml Archivos de plantilla de Razor. También se describen las Visual Studio para Mac de solución que le permiten empezar a crear rápidamente aplicaciones híbridas con Xamarin.

Por último, se presentaron los ejemplos de RazorTodo que muestran cómo combinar vistas web con interfaces de usuario y API nativas.