Lo sé. Estoy desactualizado... pero me sigue gustando Office 2003, como supongo que ocurre a mucha gente todavía. Y de hecho, continúo creando mis documentos con Microsoft Word 2003.
Sin embargo, a pesar del largo camino que hemos recorrido juntos, aún no he sido capaz de verle sentido al modo diseño de lectura, esa extraña forma de presentar los documentos por defecto al abrirlos, o que podemos activar voluntariamente desde el menú “edición”. ¿A quién se le ocurrió esa idea? ¿Alguien la ha visto útil alguna vez? ¿Tiene, quizás, alguna utilidad oculta que no he sido capaz de intuir en todos estos años?
Bueno, en cualquier caso, podemos conseguir muy fácilmente que no utilice el diseño de lectura en el momento de abrir los documentos, accediendo al cuadro de diálogo de configuración (Herramientas >> Opciones) y desmarcando el check “Permitir el inicio en diseño Lectura”:
Sencillo, ¿eh? Desde luego, no tengo excusa para haber aguantado este odioso comportamiento durante más de un lustro…
Publicado en: Variable not found.
Publicado por José M. Aguilar a las 11:12 p. m.
Etiquetas: nivel básico, office, trucos
He recibido algunos mensajes de lectores de Variable not found que quieren iniciarse en ASP.NET MVC, pero no saben por dónde empezar, y me sugieren que escriba algunos posts que expliquen desde cero este nuevo framework. Posiblemente lo haga algún día, pero mientras tanto, les he recordado que existen recursos gratuitos que pueden ser de mucha utilidad para dar los primeros pasos (e incluso profundizar un poco) en esta tecnología.
Por ejemplo, recientemente han aparecido una gran cantidad de libros sobre ASP.NET MVC, y de la mayoría de ellos se pueden descargar capítulos gratuitos que, además de ayudarnos a decidir cuál de ellos puede ser de nuestro interés, tienen valor por la información que nos pueden aportar. Como siempre, el problema es la dispersión de esta información, por lo que he decidido hacer esta recopilación para tenerlos todos a mano. Conforme vaya descubriendo nuevos ejemplares iré ampliando la lista; y por supuesto, si conocéis alguno que no esté aquí, me lo comentáis y lo añadimos.
Eso sí, todos en inglés, aunque al ser muy técnicos creo que son fáciles de comprender.
Pro ASP.NET MVC Framework Autor: Steve Sanderson Post de presentación: Now Published Pro ASP.NET MVC Framework (Apress) « Steve Sanderson’s blog Capítulo gratuito: Chapter 2: Your First ASP.NET MVC Application (23 páginas)En este capítulo, el autor muestra muy detalladamente, prácticamente paso a paso, cómo crear nuestra primera aplicación simple con ASP.NET MVC. En primer lugar, crea un proyecto muy básico mediante el cual explica los fundamentos básicos de la programación siguiendo este modelo, y a continuación entra en la creación de una mini-aplicación con entrada de datos, validaciones y lógica simple. | |
Professional ASP.NET MVC 1.0 Autores: Scott Hanselman, Rob Conery, Phil Haack y Scott Guthrie Capítulo gratuito: Chapter 1: Nerdinner (192 páginas) Este extenso capítulo describe la creación de un sitio web medianamente complejo, www.nerddinner.com, utilizando gran cantidad de tecnologías relacionadas con el framework MVC como filtros, Ajax, LinqToSql, pruebas unitarias, etc. Muy recomendable, imprescindible. | |
ASP.NET MVC 1.0 Quickly Autor: Maarten Balliauw Post de presentación: Announcing my book- ASP.NET MVC 1.0 Quickly Capítulo gratuito: Chapter 2: Your first ASP.NET MVC application (20 páginas) | ASP.NET MVC in Action Autores: Jeffrey Palermo, Ben Scheirman, Jimmy Bogard Post de presentación: Announcing ASP.NET MVC in Action (from Manning) - Jeffrey Palermo (.com) - CodeBetter.Com Recurso gratuito: Getting Started with the ASP.NET MVC Framework (Green Paper – PDF) (18 páginas) (El enlace a este documento te lo envían por correo electrónico tras facilitar tu dirección). Se trata de otra introducción al framework, el sistema de rutas, controladores y vistas partiendo desde cero. Capítulo gratuito: Chapter 9: AJAX in ASP.NET MVC (21 páginas) |
ASP.NET MVC Framework Unleashed Autor: Stephen Walther Capítulos gratuitos: el autor ha publicado varios capítulos en su blog, y los retirará en cuanto el libro esté disponible en Amazon, así que daos prisa que los contenidos son excelentes. | |
Beginning ASP.NET MVC 1.0 Autores: Simone Chiaretta, Keyvan Nayyeri Capítulo gratuito: Chapter 9: Testing ASP.NET MVC Applications (38 páginas) |
Publicado por José M. Aguilar a las 12:00 a. m.
Etiquetas: asp.net, aspnetmvc, desarrollo, libros, servicios on-line
El control de errores en aplicaciones web es fundamental si queremos ofrecer un interfaz robusto y amigable para los usuarios en cualquier situación. No hay nada más frustrante para un usuario que una pantalla de error con contenidos indescifrables y que no le aportan alternativas de salida.
El framework ASP.NET MVC nos ofrece mecanismos de control de errores muy potentes basada en la utilización del atributo HandleError,
el cual definirá la vista que será mostrada al usuario cuando se produzca alguna excepción no controlada en el código de los controladores, siempre que en el web.config
se haya activado el uso de errores personalizados mediante la propiedad CustomErrors
.
En este post vamos a profundizar en el uso del atributo HandleError,
comentando cómo se implementa en el controlador, su ámbito de actuación, los parámetros que ofrece y la forma de crear las vistas para mostrar los errores de forma amigable.
El controlador
HandleError
puede ser declarado tanto a nivel de clase (controlador) como a nivel de acción. En el primer caso, se establecerá el comportamiento general para todas las acciones del controlador, mientras que en el segundo será aplicable sólo a la acción a la que se asocie el atributo:
// Control de errores a nivel de clase de controlador,
// que será aplicado a todas las acciones del mismo.
[HandleError()]
public class HomeController : Controller
{
...
}
// Control de errores a nivel de acción concreta...
[HandleError(ExceptionType=typeof(ArgumentException), View="ArgumentError")]
public ActionResult Calculate(int a, int b)
{
ViewData["results"] = calculateSomething();
return View();
}
En realidad, el comportamiento definido en el atributo HandleError
a nivel de clase también se aplicará a los errores generados por las vistas u otros resultados (ActionResult
) retornados por los controladores. Es decir, sobre el segundo de los ejemplos anteriores, la vista “ArgumentError” (que existirá en un archivo llamado ArgumentError.aspx) será invocada cuando la excepción ArgumentException
sea lanzada bien por el propio controlador, o bien por la vista “Calculate” que retorna por defecto.
Un último detalle sobre esto: el atributo HandleError
puede ser especificado tantas veces como necesitemos sobre la misma acción o controlador, indicando comportamientos para distintos tipos de excepción. El atributo que se tendrá en cuenta cuando se produzca un error será el primero que se encuentre cuyo tipo de excepción (parámetro ExceptionType) sea compatible con la excepción lanzada.
El manejador de errores que se empleará en una acción será el primero que corresponda al tipo de excepción producida, teniendo en cuenta tanto los atributos que adornan la acción como los que acompañan a su controlador, y siempre según un orden preestablecido.
Veamos con más detenimiento los parámetros que admite la declaración del atributo.
Parámetros de HandleError
[HandleError(
ExceptionType=typeof(DivisionByZero),
View="ErrorPersonalizado",
Master="MaestraErrores",
Order = 0)
]
public ActionResult Index()
...
- ExceptionType permite indicar el tipo de excepción que se pretende controlar. Por defecto, el sistema entenderá que la regla se refiere al tipo base
Exception
, por lo que se aplicará a todos los errores que se generen, pues todas las excepciones heredan de esta clase. - View, el nombre de la vista que será mostrada al usuario. Por defecto se tomará el valor “Error”, por eso en la plantilla de proyectos ASP.NET MVC ya existe una vista con este nombre.
- Master, la página maestra con la que será renderizada la vista, independientemente de lo que tenga declarado ésta.
- Order, un valor numérico (por defecto –1, el más prioritario) que indica la prioridad de aplicación de esta regla cuando el sistema encuentre varios atributos
HandleError
aplicables al mismo elemento y que puedan presentar conflictos. Los valores más pequeños, Por ejemplo, si no se indicara este parámetro en el siguiente caso, el resultado dependería del orden de declaración, lo cual no es demasiado recomendable:
[HandleError(Order=10, View="ErrorGenerico")]
public class HomeController : Controller
{
...
[HandleError (ExceptionType=typeof(DivisionByZero),
View="OperacionIncorrecta", Order=1)]
public ActionResult Calculate()
...
Como puede intuirse, esto hará que en caso de producirse una división por cero, se muestre la vista “OperacionIncorrecta” y no la “ErrorGenerico”.
Cada vez que utilicemos HandleError
es conveniente tener muy en cuenta la prioridad (definida en la propiedad Order
), el alcance (las excepciones a tratar, definidas en la propiedad ExceptionType
), así como los valores por defecto en cada caso. Esto evitará comportamientos misteriosos del sistema una vez se produzcan errores en tiempo de ejecución.
Acceso desde la vista a la información del error
Ya hemos comentado anteriormente que la vista que será mostrada a los usuarios cuando se produzca un error será la indicada en el parámetro View
del atributo HandleError
, o la vista "Error", si este parámetro no es informado. Sea cual sea, el archivo nombredevista.aspx deberá estar localizable por el motor de vistas en el momento de su lanzamiento (por cierto, si no te gustan las ubicaciones por defecto, puedes ver cómo modificar la forma en la que se buscan las vistas en este post).
La vista de un error es una página .aspx normal, como una vista más de la web, pero con la particularidad de que puede recibir información sobre el error que ha provocado su presentación. De hecho, se trata de una vista tipada que hereda de ViewPage<System.Web.Mvc.HandleErrorInfo>
, de forma que la propiedad Model
será del tipo HandleErrorInfo
, clase que nos ofrece completa información sobre el origen del problema:
<%@ Page Language="C#" MasterPageFile="~/Views/Shared/Site.Master"
Inherits="System.Web.Mvc.ViewPage<System.Web.Mvc.HandleErrorInfo>" %>
<asp:Content ID="errorTitle" ContentPlaceHolderID="TitleContent" runat="server">
Error en el sistema
</asp:Content>
<asp:Content ID="errorContent" ContentPlaceHolderID="MainContent" runat="server">
<h2>
Ups, se ha producido un ligero inconveniente...
</h2>
<p>
La acción <%= Model.ActionName %>
del controlador <%= Model.ControllerName %>
ha lanzado la excepción <%= Model.Exception.GetType().Name %> con
el mensaje "<%= Model.Exception.Message %>".
</p>
</asp:Content>
A pesar de no ser un buen ejemplo como pantalla del error amigable para el usuario ;-), el código anterior ilustra cómo es posible acceder desde la vista a los datos del contexto del error proporcionados por el entorno, y cómo la clase HandleErrorInfo
nos ofrece una información sobre la acción en la que se ha lanzado la excepción, el controlador en el que se encuentra la misma, y lo más interesante, nos ofrece en la propiedad Exception
la excepción producida, por lo que tendremos acceso a su tipo, descripción e incluso al estado de la pila de llamadas en el momento de producirse el error.
Publicado en: www.variablenotfound.com
Publicado por José M. Aguilar a las 11:59 p. m.
Etiquetas: asp.net, aspnetmvc, c#, desarrollo, programación, web
<code>
y </code>
el texto que tengamos seleccionado en el momento de su utilización, pero evita tener que entrar al modo de edición HTML para hacer estos ajustes. Basándome en el código fuente de CodeTag (que podéis descargar al final de este post), voy a describir, paso a paso, cómo podemos crear complementos para Writer que nos faciliten un poco las tareas cotidianas de edición.
0. Antes de empezar…
Para poder desarrollar el plugin necesitáis básicamente dos cosas:- en primer lugar, el propio software Live Writer, más que nada porque necesitamos referenciar algunos de sus ensamblados, y por ir probando el complemento durante el desarrollo.
- en segundo lugar, Visual Studio 2005 o 2008, aunque sea una edición express. Este desarrollo vamos a realizarlo en C#, pero la traducción a Visual Basic .NET sería trivial.
1. Preparación del proyecto
Los plugins para Live Writer son archivos .dll, es decir, ensamblados .NET, que se colocan en el directorio "plugins" de la carpeta de instalación del programa. En mi caso, están en la carpetaC:\Archivos de programa\Windows Live\Writer\Plugins,
y si no habéis cambiado las rutas de instalación por defecto, será allí donde los podéis encontrar también. Durante el proceso de arranque, Writer examinará dicha carpeta y cargará los complementos que encuentre en ella. Lo que vamos a hacer es crear desde Visual Studio un proyecto de librería de clases. Cada vez que compilemos, copiaremos el ensamblado resultante en dicho directorio (veremos cómo podemos automatizar esta tarea) y podremos lanzar Live Writer para comprobar el funcionamiento.
Por tanto, en primer lugar creamos un proyecto de librería de clases como siempre, al que le damos el nombre CodeTag. Obviamente, podéis dar al proyecto el nombre que estiméis conveniente.
Una vez que el IDE crea la estructura, debemos añadir dos referencias al proyecto, que serán necesarias para poder continuar:
- La primera referencia es al ensamblado que contiene el API de Live Writer, que podéis encontrar en el directorio de instalación de la herramienta. El archivo a incluir es
WindowsLive.Writer.Api.dll
. - La segunda es a
System.Windows.Forms
. Hay que tener en cuenta que Live Writer es una herramienta de escritorio, y esta referencia es importante para poder interactuar con el mismo.
Otro detalle más que nos va a facilitar las cosas: vamos a indicar a Visual Studio que el resultado de la compilación lo copie en el directorio de plugins de Live Writer. Para ello, lo único que tenemos que hacer es acudir a las propiedades del proyecto, pestaña “eventos de generación” e incluir la siguiente orden en el cuadro “línea de comandos del evento posterior a la generación” (obviamente, modificando la ruta si es necesario):
XCOPY /D /Y /R "$(TargetPath)" "C:\Archivos de Programa\Windows Live\Writer\Plugins\"
Ojo, es importante tener en cuenta algo más cuando vayamos a compilar: si Live Writer está abierto, no podremos sobrescribir nuestro plugin con una nueva versión, pues éste se encontrará en uso. En estos casos debemos cerrar el programa antes de copiar el archivo, o antes de compilar.
Con estas operaciones ya tenemos el proyecto listo para empezar a codificar el plugin.
2. Escribimos la clase principal
Un plugin sencillo como este no requiere demasiada programación, pero sí hay que cumplir una serie de requisitos para que se integre correctamente en Live Writer.En primer lugar, creamos una clase, a la que llamaremos
CodeTag
, e insertamos el siguiente código: public class CodeTag: ContentSource
{
public override DialogResult CreateContent(IWin32Window dialogOwner,
ref string content)
{
if (string.IsNullOrEmpty(content))
content = "YourCodeHere";
content = "<code>" + content + "</code> ";
return DialogResult.OK;
}
}
Como se puede observar, la clase hereda de
ContentSource,
un tipo definido en WindowsLive.Writer.Api
, que sirve como base para la creación de complementos sencillos como el que nos ocupa. Para casos más complejos, que permitieran por ejemplo edición de contenidos bidireccional entre el contenido de la página y un editor personalizado podríamos heredar de SmartContentSource
, pero no profundizaremos en ello ahora. La codificación de la lógica del plugin puede realizarse en varios puntos, dependiendo de la forma en que Writer lo active; en nuestro caso, el complemento se lanzará cuando el usuario presione un botón en la barra lateral o seleccione la opción correspondiente en el menú "insertar", por lo que simplemente deberemos sobrescribir el método
CreateContent
. Dicho método recibe dos parámetros. El primero de ellos hace referencia a la ventana activa, que podemos utilizar como "padre" si quisiéramos crear un cuadro de diálogo desde nuestro código. El segundo parámetro contendrá una referencia al texto seleccionado en el momento de lanzar el plugin, pudiendo darse los dos casos que se contemplan en la codificación:
- Si la variable
content
viene vacía o con valor nulo, es que el complemento ha sido lanzado en modo inserción, es decir, no existía ningún texto seleccionado y por lo tanto lo que se pretende es añadir las etiquetas de código para introducir posteriormente contenido. Como puede observarse en el código, lo que se hace en este caso es insertar las etiquetas<code>
con un texto arbitrario para que el usuario lo modifique a su antojo más adelante. - En caso contrario, si la variable
content
trae algún contenido, lo que se hace es rodear éste por la apertura y cierre de la etiqueta<code>
.
En ambos casos se retorna un valor
DialogResult.OK
, que indica a Live Writer que debe insertar el texto contenido en content en la ubicación donde se encuentre el cursor, o bien sustituir el texto seleccionado por el nuevo valor. 3. Añadimos metadatos
Heredar de la claseContentSource
no es el único requisito para que esta sea considerada como un componente de Writer, es necesario adornarla con un conjunto de atributos como los mostrados en el siguiente código: [WriterPlugin("98497c2b-bbfd-4bd1-b343-226f3c9e766b", "Code Tag",
Description = "Crea etiquetas <code></code> en el contenido",
PublisherUrl = "http://www.variablenotfound.com")]
[InsertableContentSource("Etiqueta <code>")]
public class CodeTag: ContentSource
{
[...]
}
El atributo [
WriterPlugin]
es el que realmente identifica la clase como plugin de Live Writer. Los parámetros que se le están enviando son los siguientes: - El primer parámetro es un GUID, es decir, un identificador único que debemos generar para el plugin, utilizando este servicio on-line u otras herramientas como la incluida en Visual Studio.
- El segundo parámetro es el nombre del plugin. Es la identificación que aparece en el cuadro de diálogo de configuración de complementos de Live Writer.
Description
permite añadir una descripción textual ampliada del plugin.PublisherUrl
es una dirección web de referencia al autor. Pura propaganda ;-)
El atributo
[InsertableContentsource]
indica que el complemento debe aparecer en el menú "insertar" de las barra de tareas de Writer y en el menú principal. El parámetro que le estamos enviando es simplemente el texto que aparecerá en estos menús. 4. Compilamos el proyecto
Con lo hecho hasta el momento ya podemos compilar e intentar probar nuestro complemento. Este procedimiento lo repetiremos varias veces durante el desarrollo, por lo que es posible que nos encontremos a menudo con un error como el siguiente:Esto simplemente quiere decir que Live Writer está usando CodeTag.dll y no puede ser sobrescrito, por lo que ¡cerrad Live Writer antes de compilar!
Una vez superado este leve impedimento, si aparece, ya podremos comenzar a disfrutar de nuestro plugin. Veremos que aparece en el menú "insertar", con la descripción apropiada, y funcionando correctamente.
Pero vaya, es cierto que en el menú aparece, pero destaca sobre el resto de complementos porque es el único que no tiene un icono, así que habrá que mejorarlo un poco…
5. Añadirle un icono al plugin
Incluir un icono a nuestro complemento le dará sin duda un aspecto más profesional, vamos a ello.Lo primero que necesitamos es una imagen de 16x16 píxeles, por ejemplo en formato .bmp, que actuará como icono. Como el diseño gráfico decididamente no es lo mío, he creado una imagen muy simple pero creo que bastante ilustrativa de la tarea que realiza el complemento: la propia etiqueta
<code>
que pretendemos crear. El archivo debemos incluirlo, para no tener problemas, en el directorio raíz del proyecto, y a continuación, hay que indicar a Visual Studio que dicha imagen será un recurso incrustado (embedded resource) en el ensamblado. Este paso es importante, pues si no se hace correctamente, la imagen no será incluida en la DLL.
A continuación es necesario indicar a Live Writer la imagen a utilizar como icono, lo que se consigue añadiendo un parámetro más (
ImagePath
) en la definición del atributo [WriterPlugin]
con la ruta hacia el fichero que hemos incrustado. Eso sí, no me refiero a la ruta física del archivo .bmp, sino a la representación como espacio de nombres de la misma (por ejemplo, si la imagen se llama logo.bmp
y está en (raíz)\recursos
, la ruta hacia ella será “recursos.logo.bmp”
). Como en este caso hemos depositado la imagen en el directorio raíz, la declaración de atributos quedará como sigue:
[WriterPlugin("98497c2b-bbfd-4bd1-b343-226f3c9e766b", "Code Tag",
Description = "Crea etiquetas <code></code> en el contenido",
ImagePath = "CodeTag.bmp",
PublisherUrl = "http://www.variablenotfound.com")]
[InsertableContentSource("Etiqueta <code>")]
public class CodeTag : ContentSource
{
[...]
}
Un último apunte relativo a este tema: si al iniciar Live Writer éste es incapaz de localizar el recurso indicado en el parámetro
ImagePath
, el plugin funcionará, pero aparecerá el siguiente mensaje en el arranque de la aplicación: 6. O, por si no quieres teclear…
… he colgado en SkyDrive el código fuente del proyecto, para el que quiera utilizarlo como base de creación de sus propios complementos, o simplemente para trastearlo un poco.Requiere, como mínimo, Visual C# 2008 Express, con su correspondiente Service Pack 1.
Publicado en: Variable not found.
Publicado por José M. Aguilar a las 11:52 p. m.
Etiquetas: blogging, c#, desarrollo, software, utilidades
El framework ASP.NET MVC utiliza en varios puntos la filosofía que suele denominarse convención sobre configuración, ahorrando tiempo y esfuerzos al desarrollador que decida asumir una serie de normas preestablecidas, a la vez que facilita la homogeneidad y coherencia en las soluciones que las implementen.
Por ejemplo, todos sabemos que las vistas de una aplicación ASP.NET MVC deben encontrarse en la carpeta Views
. Si se trata de una vista compartida como puede ser una página maestra, debemos colocarlas en la subcarpeta Shared
; en caso contrario, deberá existir una subcarpeta con el nombre del controlador en la que se encontrarán las vistas para las distintas acciones, nombradas igual que éstas. Esto, a la postre, evita que sea el desarrollador el que tenga que decidir dónde ubicar dichos archivos y configurar el sistema para que sea capaz de localizarlos cuando sean necesarios: simplemente deberá seguir la convención preestablecida.
Pero, ¿qué ocurre cuando estas convenciones no encajan con nuestras necesidades o preferencias?
En este post voy a describir cómo es posible modificar la ubicación de las vistas en una aplicación ASP.NET MVC 1.0, saltándonos las convenciones establecidas por el framework. Pero sobre todo, ojo:
Saltarse las convenciones = malo
Hazlo sólo cuando realmente esté justificado... o por diversión, como es el caso ;-)
1. Cómo localiza ASP.NET MVC las vistas
Cada vez que el framework necesita localizar una vista, por ejemplo después de ejecutar una acción cuyo resultado indica que debe mostrarse al usuario una página, recorre los directorios donde, por convención, se supone que debe estar. Esta información se encuentra definida a nivel de código en el constructor de la clase WebFormViewEngine
, es decir, en el motor de vistas Webforms que se utiliza por defecto en ASP.NET MVC. Su definición es la siguiente (recordad que está disponible el código fuente del framework):
public class WebFormViewEngine : VirtualPathProviderViewEngine
{
[...]
public WebFormViewEngine()
{
base.MasterLocationFormats = new string[]
{
"~/Views/{1}/{0}.master",
"~/Views/Shared/{0}.master"
};
base.ViewLocationFormats = new string[]
{
"~/Views/{1}/{0}.aspx",
"~/Views/{1}/{0}.ascx",
"~/Views/Shared/{0}.aspx",
"~/Views/Shared/{0}.ascx"
};
base.PartialViewLocationFormats = base.ViewLocationFormats;
}
[...]
}
Como se puede observar, existen tres propiedades en el motor del vistas, heredadas de su antecesor, que indican los directorios donde se encuentran los archivos con la definición del interfaz:
- MasterLocationFormats es un array con las rutas en las que se buscarán sucesivamente las páginas maestras. En el código puede verse que por defecto el sistema intentará localizar las masters en
~/Views
y~/Views/Shared
; los parámetros {0} y {1} corresponden al nombre de la master y del controlador que están siendo buscados, respectivamente.Es importante tener en cuenta que el valor de esta propiedad sólo se aplica cuando se establece desde el código la página maestra con la que debe ser mostrada una vista.
- ViewLocationFormats contiene las rutas donde se intentarán localizar las vistas. En la implementación por defecto, puede verse cómo el framework intenta localizar primero en
~/Views/{1}
(recordad que este parámetro se sustituirá por el nombre del controlador) y luego en~/Views/Shared
una página .aspx o .ascx con el nombre de la vista (parámetro {0}). - Por último, PartialViewLocationFormats indica las carpetas donde se deberán buscar las vistas parciales. Por defecto, se buscarán en los mismos directorios que las vistas normales.
2. Lo que pretendemos
Una vez visto en qué se basa el framework para localizar las vistas, vamos al ataque. Nuestra intención es saltarnos las convenciones creando una nueva estructura de carpetas para las mismas, como la que puede observarse en la imagen lateral. Existirá un directorio raíz, llamado "Interfaz", en el que podemos encontrar dos subdirectorios: "Vistas" y "Masters". En ellos almacenaremos, respectivamente, las vistas y las páginas maestras de nuestra aplicación.
La estructura de carpetas del proyecto será, por tanto, diferente a la propuesta por las plantillas de Visual Studio. Además, no tiene demasiado sentido, desde el punto de vista práctico, pero nos valdrá como ejemplo.
En los siguientes apartados vamos a ver dos formas distintas de conseguirlo.
3. Cómo lograrlo
La forma más sencilla de conseguir reemplazar las rutas de búsqueda de las vistas es, sin duda, alterar el contenido de las propiedades del motor descritas en el primer punto (MasterLocationFormats
, ViewLocationFormats
y PartialViewLocationFormats
).
Esto puede conseguirse introduciendo el siguiente código en la inicialización de la aplicación, en el archivo global.asax:
public class MvcApplication : System.Web.HttpApplication
{
[...]
protected void Application_Start()
{
ReplacePaths(); // Reemplaza las rutas en el View Engine actual
RegisterRoutes(RouteTable.Routes);
}
private void ReplacePaths()
{
WebFormViewEngine eng = ViewEngines.Engines[0] as WebFormViewEngine;
eng.MasterLocationFormats =
new string[] {
"~/Interfaz/Masters/{0}.master"
};
eng.ViewLocationFormats =
new string[] {
"~/Interfaz/Vistas/{0}.aspx",
"~/Interfaz/Vistas/{0}.ascx"
};
eng.PartialViewLocationFormats = eng.ViewLocationFormats;
}
[...]
}
Como se observa en el código anterior, en el método ReplacePaths()
en primer lugar se obtiene una referencia al motor de vistas por defecto, que sabemos que es del tipo WebFormViewEngine
. Después, modificamos las propiedades para introducir las rutas que nos interesan. Tan simple como eso.
Es obvio que de esta forma estamos realizando una actualización destructiva de las propiedades. Si quisiéramos mantener las localizaciones por defecto del WebFormViewEngine
pero añadir nuevas ubicaciones de búsqueda deberíamos copiar el contenido original del array, añadirle las rutas adicionales, y establecerlo en las propiedades.
Veamos otra forma de conseguir lo mismo.
4. Cómo lograrlo, toma 2
ASP.NET MVC framework ha sido diseñado desde sus orígenes con la extensibilidad en mente. Prácticamente cualquiera de sus comportamientos puede ser personalizado sustituyendo componentes como si se trataran de piezas de un mecano.
Una de las posibilidades de extensión incluidas en la plataforma es la sustitución del motor de vistas que utiliza para componer los interfaces de usuario. Esto, llevado al extremo, permite la utilización de motores distintos al proporcionado por defecto (WebForms) y utilizar alternativas como NHaml, Spark, Brail, o NVelocity, entre otros.
Pero nosotros no iremos tan lejos. Para cumplir con nuestros objetivos simplemente necesitamos crear y registrar en el sistema un nuevo motor de vistas que herede de WebFormViewEngine
y reemplace la inicialización de las propiedades que definen la localización de las vistas.
El nuevo motor, al que llamaremos AcmeViewEngine, será algo como lo siguiente:
public class AcmeViewEngine : WebFormViewEngine
{
public AcmeViewEngine()
{
MasterLocationFormats =
new string[] {
"~/Interfaz/Masters/{0}.master"
};
ViewLocationFormats =
new string[] {
"~/Interfaz/Vistas/{0}.aspx",
"~/Interfaz/Vistas/{0}.ascx",
};
PartialViewLocationFormats = base.ViewLocationFormats;
}
}
Y ahora sólo nos faltaría registrar el motor de vistas en ASP.NET MVC framework, tarea que podemos realizar en el global.asax, así:
protected void Application_Start()
{
ReplaceViewEngine(); // Registra el nuevo View Engine
RegisterRoutes(RouteTable.Routes);
}
private void ReplaceViewEngine()
{
ViewEngines.Engines.Clear();
ViewEngines.Engines.Add(new AcmeViewEngine());
}
Observad que antes de añadir el nuevo motor de vistas a la colección estamos vaciándola (llamando a su método Clear()
), lo cual asegurará que nuestro AcmeViewEngine
será el utilizado por la plataforma.
5. Conclusión
Como ya he comentado al principio, saltarnos las convenciones no es buena idea. Por ejemplo, un daño colateral de romper con la convención propuesta por el framework MVC para la localización de las vistas es que Visual Studio no será capaz de proporcionarnos la función que nos permite abrir la vista asociada con una acción seleccionando la opción “Go to View” del menú contextual, o intentará insistentemente crear las nuevas vistas (con la opción “Add View”) en las carpetas en las que deberían encontrarse según la convención.
Pero en cualquier caso, es un ejercicio interesante para conocer las interioridades de este framework y comprobar de primera mano la flexibilidad con la que ha sido implementado.
Si lo deseas, puedes descargar el código fuente de los ejemplos (requiere Visual Studio 2008 o Web Developer Express con SP1 y ASP.NET MVC 1.0):
Publicado en: www.variablenotfound.com
Publicado por José M. Aguilar a las 11:58 p. m.
Etiquetas: asp.net, aspnetmvc, desarrollo, programación, trucos
No sé si será un cambio en la forma de comunicarse a través de la red, pero sin duda es una idea conceptualmente atractiva: unir en una única plataforma todas las conversaciones que mantenemos sobre Internet… pero entendiendo el término conversación de una forma más amplia de lo habitual.
Mensajes de correo, foros, mensajería instantánea, compartición de imágenes y documentos, posts, comentarios en blogs, sus respuestas, twits, intercambio de mensajes vía redes sociales, documentos colaborativos en tiempo real, wikis, juegos… todo cabe en Google Wave, la criatura en la que los hermanos Lars y Jens Rasmussen (famosos por haber creado Google Maps) llevan trabajando más de dos años, y que fue presentada hace unos días en el Google I/O.
Básicamente, Google Wave es la respuesta a una serie de preguntas que se formularon al comienzo del proyecto:
- ¿cómo sería el correo electrónico si hubiera sido inventado hoy, y no hace más de tres décadas?
- si en el fondo todo es comunicación, ¿por qué tenemos que diferenciar entre emails, chats, conversaciones o documentos?
- ¿es posible que un único modelo de comunicaciones sea capaz de unir de forma sencilla los sistemas web que utilizamos hoy?
- dado que las herramientas de comunicación habituales son meros reflejos digitales de los procesos del mundo analógico (email vs. correo tradicional, mensajería instantánea vs. teléfono…), ¿cómo sería un sistema diseñado para beneficiarse de las capacidades de las redes y ordenadores actuales, en lugar de limitarse a emular los medios no electrónicos?
Pero no penséis que Google Wave sólo es una aplicación más, como GMail o Google Reader, capaz de hacer de forma diferente cosas que ya se podían hacer antes. Se trata de un trío compuesto por tres elementos: producto, plataforma y protocolo:
- El producto, que es la aplicación cliente que mostraron en el evento, muy al estilo Google, desde la que se pueden iniciar y gestionar nuestras conversaciones. Se trata de una aplicación HTML5 construida sobre Google Web Toolkit, que todavía sólo está disponible para un grupo reducido de desarrolladores, y que han confirmado que será open source.
- La plataforma, el conjunto de APIs y especificaciones que permiten integrar Wave en cualquier sistema web, o extenderlo mediante la creación de Robots o Gadgets.
- El protocolo, Google Wave Federation Protocol, aún en borrador, se encuentra definido en una serie de documentos que detallan su arquitectura, el modelo de datos, las comunicaciones entre clientes y servidores y, en general, toda la información necesaria para que puedan existir distintas implementaciones del servicio. De hecho, el producto de Google será únicamente una implementación de referencia.
Esta nueva forma de colaboración gira en torno a la figura del Wave, que según dicen los entendidos es una mezcla entre una conversación y un documento sobre los que trabajan de forma colaborativa una serie de participantes. Y la verdad, tiene un aire a las conversaciones de GMail, y también a la edición multiusuario de Google Docs, pero va bastante más allá. Un Wave puede contener mensajes, comentarios, anotaciones y elementos más avanzados, como mapas, encuestas, juegos, o tomar datos desde otras aplicaciones y servicios, como blogs, twits, foros, sistemas de seguimiento de errores, y todo integrado dentro de un mismo contexto (la conversación). La cuestión es tener agrupadas todas las comunicaciones relativas a un mismo tema en un único punto.
Además, los participantes no tienen que ser necesariamente humanos, es decir, por ejemplo es posible que en un Wave intervenga un robot que se encargue de ir traduciendo los mensajes al idioma de cada participante, lo cual abre unas posibilidades inimaginables. En el vídeo al que hago referencia abajo hay una demo de Rosy, un robot traductor muy apañado que realiza estas funciones.
Otro aspecto interesante es la facilidad para organizar la información, y me ha gustado mucho la capacidad de temporalizar las conversaciones, de forma que sea posible reproducir la secuencia de aportaciones realizadas al Wave por cada uno de los participantes.
Si tenéis una horita y media libre, y podéis haceros de coca-cola y palomitas, no os perdáis el vídeo de la presentación del producto de mano de sus responsables. Muy divertida y sin duda espectacular. Eso sí, si no domináis el inglés, tendréis que activar vuestro modo de inferencia lingüística, ese que se encarga de rellenar con la imaginación las expresiones que no llegamos a comprender ;-)
Se dice que Wave estará disponible a finales de año. En este momento, lo más que podéis hacer es solicitar que os tengan en consideración cuando lo abran al público.
Enlaces interesantes:
- Google Wave, todo lo que tienes que saber (Cristalab)
- ¿Qué significa Google Wave? (Maestros del Web)
- Google Wave: lo nuevo de Google (Jorge Oyhenard)
Más enlaces interesantes (en inglés):
- Sitio oficial: Google Wave
- Google Wave Federation Protocol
- Official Google Blog Went Walkabout. Brought back Google Wave
- Google Wave Developer Blog: Introducing the Google Wave APIs: what can you build
- Google Wave API - Google Code
Publicado en: Variable not found.
Publicado por José M. Aguilar a las 11:45 p. m.
Etiquetas: google, noticias, novedades, servicios on-line