Tag Archives: Spanish

Evento “Ciclo de vida unplugged” con Madrid.NET

SquareWheels

Observemos la imagen que preside este post. ¿Qué podemos ver en ella? ¿Una litografía sobre personas empujando un carro con ruedas cuadradas? No, en realidad lo que vemos es un proyecto software clásico.

En la delantera tenemos al jefe de proyecto, que desconocedor de la existencia de las ruedas redondas, ha intentado idear un mecanismo para que el proyecto avance más rápidamente que si hubiera que desplazar su pesada carga a mano. Sin embargo, sus buenas intenciones se han plasmado en un EPIC FAIL de ruedas cuadradas del que, por mucho que tira, no consigue poner en marcha.

Detrás, ejerciendo un terrible esfuerzo empujando el proyecto, tenemos al grupo de sufridos desarrolladores que, más mal que bien, intentan hacer avanzar el engendro rodándolo sobre esas ruedas cuadradas que tan desacertadamente ha diseñado el jefe de proyecto.

Si quieres evitar ser víctima de semejante estampa, vente al evento de Madrid.NET el próximo jueves 23 de Septiembre. La idea es tener un coloquio/mesa redonda/charla distendida con gurús de la gestión de proyectos como Luis Fraile y El Bruno, al tiempo que todos aprendemos de todos y de nuestras propias ruedas cuadradas.

El link para registrarse es éste: https://msevents.microsoft.com/CUI/EventDetail.aspx?EventID=1032462873&Culture=es-ES

Nos vemos allí.

Silverlight: Binding de textboxs a propiedades nullables

No descubro nada si digo que Silverlight tiene un magnífico sistema de binding, con el que nos podemos ahorrar muchísimo “code behind”. Sin embargo, me he encontrado un extraño comportamiento cuando se combina con propiedades nulables, como serían int?. Mi escenario era el siguiente:

  • Un textbox bindeado a una propiedad int? en una entidad que era el data context de mi control
  • Un botón que, al pulsarlo, lanzaba de forma manual el checkeo de los bindings de todos los textboxs, incluido el anterior (método UpdateSource del binding)
  • Todos los métodos set de las propiedades son correctamente invocados, menos el de mi propiedad nulable.

¿Por qué? Además, como corolario, todos aquellos que no estaban correctamente formateados, se marcaban con el correspondiente borde rojo, incluido el que aparentemente no había llegado a checkearse. Y por si esto no fuera suficiente misterio, el mensaje que acompañaba al borde rojo, aparentemente había salido de ninguna parte; yo no lo había definido.

 

MISTERIO DESENTREAÑADO

Después de buscar bastante por Internet, llegué a esta página en la que explicaban el porqué de la situación. Al parecer, los bindings de Silverlight no se actualizan cuando no son capaces de deducir si el valor del control, es “casteable” a la propiedad que se está intentando asignar.

En mi caso, el textbox, al estar vacío, provocaba que el binding no supiera convertir un string.Empty a un int?, algo lógico por otra parte. De ahí que el método set de la propiedad nunca llegara a llamarse. La última pieza del misterio, el mensaje salido de la nada, era responsabilidad del propio binding, avisando de que no es capaz de hacer la conversión.

 

La solución

Ahora que ya sabemos el porqué, lo mejor es saber cómo resolverlo. Es fácil, necesitamos que “algo” convierta los valores que el binding no va a ser capaz de resolver, a valores que sí sean asignables a nuestras propiedades nulables. Por ejemplo, que cada string.Empty o puñado de caracteres vacíos, se conviertan en un null, algo con lo que el binding sí va a poder manejarse y setear en nuestra propiedades. En definitiva, necesitamos un Converter. Un ejemplo de uno totalmente funcional lo tenéis a continuación:

/// <summary>
/// Class that converts a value from an UI control to a nullable property value.
/// </summary>
/// <remarks>This class intents to resolve a "gap" in the Silverlight binding model.
/// Currently, the binding model is not able to invoke the setter of a nullable property, if
/// the value inside the control is empty.
/// Silverlight binding mechanism always avoids to invoke a setter if it is not sure how to
/// represent the type of the property to which is binded. For example, in a textbox with an empty
/// value for Text property, it will not set this value to a int? property in the underlying model.
/// </remarks>
public class NullableValueConverter : IValueConverter
{
    #region IValueConverter Members

    /// <summary>
    /// Converts a value from the underlying entity into its UI control representation.
    /// </summary>
    /// <param name="value">Value to convert.</param>
    /// <param name="targetType">Target type.</param>
    /// <param name="parameter">Conversion parameter.</param>
    /// <param name="culture">Culture to use in the conversion.</param>
    /// <returns>Object converted.</returns>
    public object Convert(object value, Type targetType, object parameter, System.Globalization.CultureInfo culture)
    {
        return value;
    }

    /// <summary>
    /// Converts a value from a UI control to its representation in the underlying entity.
    /// </summary>
    /// <param name="value">Value to convert.</param>
    /// <param name="targetType">Target type.</param>
    /// <param name="parameter">Conversion parameter.</param>
    /// <param name="culture">Culture to use in the conversion.</param>
    /// <returns>Object converted.</returns>
    public object ConvertBack(object value, Type targetType, object parameter, System.Globalization.CultureInfo culture)
    {
        string castedValue = (string)value;
        if (string.IsNullOrEmpty(castedValue) == true || castedValue.Trim().Length == 0)
        {
            return null;
        }

        return value;
    }

    #endregion
}

 

Y ya lo único que nos faltaría sería utilizarlo en nuestro código XAML, en dos pasos. El primero, definirlo como recurso dentro del XAML en que vamos a usarlo:

<UserControl.Resources>        
        <controls:NullableValueConverter x:Key="NullableValueConverter"/>
    </UserControl.Resources>

 

Y el segundo, asociándolo al binding que hemos establecido entre el control y la propiedad nulable.

Text="{Binding MyNullableProperty, Mode=TwoWay, NotifyOnValidationError=true, ValidatesOnExceptions=true, Converter={StaticResource NullableValueConverter}}"

Y esto es todo. Espero que os sea de utilidad.

Filtrar generación en Moles

Últimamente me he tenido que pelear un poco con Moles. Para los que no sepan qué es Moles, pueden consultar este post mío de hace un tiempo (ya me parezco a Enrique Dans autocitándome). Para los perezosos, resumir en que es un framework para testing que permite inyectar código en cualquier llamada al framework .NET. El ejemplo que suele verse en la mayoría de vídeos y textos demuestra cómo se puede “configurar” lo que queremos que devuelva DataTime.UtcNow, algo que sin Moles resulta imposible, pues UtcNow no tiene método set.

Bien, si en una librería que estás intentando “molear” tienes un tipo rebelde que se te rompe la generación, o bien prefieres limitar el número de clases que se molean a sólo aquellas que van a utilizar, esto te puede ser de utilidad. En cada mole tendremos un fichero XXX.moles, donde XXX es el ensamblado que hemos moleado. En dicho fichero podemos configurar algunas de las características del mole, lo que hará que se vuelva a generar teniendo en cuenta nuestra configuración.

Si quisiéramos evitar que se generara código para la clase DateTime en el ensamblado System, deberíamos añadir algo parecido a esto en nuestro fichero .moles:

<Moles xmlns="http://schemas.microsoft.com/moles/2010/">
  <Assembly Name="System" />
  <CodeStyle DisableUniqueAlias="true" />
  <StubGeneration>
    <Types>
      <Remove Namespace="System" TypeName="DateTime" />
    </Types>
  </StubGeneration>
  <MoleGeneration>
    <Types>
      <Remove Namespace="System" TypeName="DateTime" />
    </Types>
  </MoleGeneration>
</Moles>

El fragmento es bastante autoexplicativo: le estamos indicando que, tanto de la generación de Stubs como de Moles, nos elimine la clase DateTime dentro del espacio de nombres System. Podremos introducir tantas entradas dentro de las etiquetas Types, como tipos queramos evitar “molear”.

 

Visto en los Foros de social.msdn.microsoft.com

Atributo InternalsVisibleTo

El atributo InternalsVisibleTo puede resultarnos muy útil en situaciones en las que tengamos la necesidad de acceder, desde un ensamblado, a los miembros Internal de las clases de otro ensamblado.

Un ejemplo que me he encontrado últimamente de su uso es en el testeo de aplicaciones Silverlight. Por desgracia, Silverlight no tiene ningún framework de testeo que sea capaz de generar accessors, probablemente porque Reflection no funciona para campos privados en Silverlight.

Para sustituir esta carencia, en algunos casos podremos marcar la visibilidad de miembros que queramos testear como Internal en lugar de Private y, haciendo uso de InternalsVisibleTo, acceder a ellos desde los correspondientes tests. ¿Cómo lo lograríamos? Son unos pocos pasos:

Primer paso: obtener la clave pública

Necesitamos conocer la clave pública de firmado del ensamblado al que queremos conceder el privilegio de poder acceder a los miembros internal. Para ello, en una consola de Visual Studio, deberemos introducir el siguiente comando:

sn -Tp c:UsersjavierprojectsMyProjectDllToGrantAccessToInternals.dll

De la salida que nos genera este comando, nos la copiamos para hacer el siguiente paso.

Segundo paso: definir el atributo

Ahora que ya conocemos la PublicKey del ensamblado (no confudir con la PublicKeyToken que aparece en los qualified names), necesitamos definir el atributo en el fichero AssemblyInfo del proyecto. En dicho fichero deberemos añadir algo como lo siguiente:

[assembly: InternalsVisibleTo("DllToGrantAccessToInternals.dll, PublicKey=0024000004…)]

 

¿Sencillo no? Con esto ya podríamos ver los miembros Internal del ensamblado en que hemos definido el atributo, desde el ensamblado DllToGrantAccessToInternals.

Invocando dinámicamente un método genérico

No sé si es debido a un bug o simplemente es algo a lo que no hay modo sencillo de dar soporte, pero el caso es que los accessors que podemos utilizar en los proyectos de testing en Visual Studio 2008, no son capaces de invocar métodos que sean privados, estáticos y genéricos (a la vez).

Una posible solución (desconozco si hay más y más sencillas) es la invocación de estos métodos en nuestros tests mediante Reflection. Con este tipo de fragmentos de código vuelves una y otra vez a por ellos según vas pasando de proyecto en proyecto, aquí queda para el futuro.

 

   1:  // Obtener el MethodInfo asociado al metodo a invocar
   2:  MethodInfo methodInfo = typeof(ClassToInvoke).GetMethod(
   3:      "MethodToInvoke", System.Reflection.BindingFlags.Static | BindingFlags.Public);
   4:   
   5:  // Bindear el tipo con el metodo que queremos invocar
   6:  Type[] genericArguments = new Type[] { genericType };
   7:  MethodInfo genericMethodInfo = methodInfo.MakeGenericMethod(genericArguments);
   8:   
   9:  // Invocar el metodo y pasarle los parametros necesarios
  10:  // El parametro null es el objeto desde el que se invoca el metodo. 
  11:  // Al ser un estatico, debe ser null
  12:  try
  13:  {
  14:      genericMethodInfo.Invoke(null, new object[] { value1, value2 });
  15:  }
  16:  catch (TargetInvocationException ex)
  17:  {
  18:      // Cualquier excepción lanzada por el método irá wrappeada dentro 
  19:      // de una TargetInvocationException
  20:      if (ex.InnerException != null)
  21:      {
  22:          throw ex.InnerException;
  23:      }
  24:      else
  25:      {
  26:          throw;
  27:      }
  28:  }

Microsoft Web Deploy: Instalación y Uso

Actualización: He añadido al artículo original una explicación de porqué es más seguro utilizar usuarios de IIS en lugar de cuentas Windows, así como los problemas que podemos tener con el certificado a la hora de realizar los despliegues. Gracias desde aquí a Fernando Guillot por sus sugerencias.

 

Son muchas las novedades que han aparecido en Visual Studio 2010, entre ellas varias relacionadas con el despliegue de aplicaciones. A partir de ahora, en el menú contextual de cada proyecto web en VS2010 tendremos tres nuevas opciones, como se muestra en la siguiente imagen: generar un paquete de despliegue (un archivo zip), publicar utilizando alguna de las tecnologías disponibles y establecer la configuración del empaquetamiento/publicación.

NuevasOpcionesDespliegue

Respecto a la publicación, sin duda la principal novedad es poder utilizar la tecnología de Web Deploy, presente en IIS (Internet Information Services) y que ayudará a administradores a simplificar la migración, gestión y despliegue de servidores web IIS. A continuación, vamos a ver cómo instalar y configurar esta tecnología en un servidor Windows Server 2008 R2. Al final del post se detallará cómo configurar Visual Studio 2010 para poder hacer uso de esta nueva forma de despliegue.

 

Instalación

Vamos a partir de una instalación en limpio de Window Server 2008 R2 Standard.

 

Añadir rol de servidor web

El primer paso es añadir el rol de servidor web a nuestra máquina Windows Server. Lanzamos la herramienta “Server Manager” y pulsamos en la opción “Roles”, en el árbol de la parte izquierda. En el centro de la ventana podremos ver una opción para añadir un nuevo rol, como a continuación.

AnadirRolWeb_Paso1

En el asistente que nos aparece, pulsamos el botón “Siguiente” hasta que nos aparezca la lista de roles que podemos instalar. Seleccionamos el rol “Web Server (IIS)” tal como muestra la imagen bajo estas líneas.

AnadirRolWeb_Paso2

Completaremos la instalación del rol indicando que queremos instalar también el servicio de Management, como se puede ver en la siguiente figura. Después, sólo queda esperar a que la instalación termine.

AnadirRolWeb_Paso3

 

Instalar Web Deploy Tool

Ya tenemos instalado nuestro servidor IIS. Ahora debemos “ampliarlo” instalando la herramienta de Web Deploy. Lo primero será descargarnos el instalable y ejecutarlo.

Una vez ejecutado el fichero que acabamos de descargar, sólo tenemos que seguir el asistente pulsando el botón “Siguiente” hasta que nos ofrezca elegir qué tipo de instalación queremos realizar. Debemos instalar la versión completa si queremos tener a nuestra disposición el servicio “Web Deployment Agent”.

Tras terminar la instalación, será necesario arrancar a mano este servicio, puesto que está configurado por defecto como manual y parado. Lo haremos abriendo una consola y ejecutando el siguiente comando.

net start msdepsvc

 

Configuración del Web Deployment Handler

Ya tenemos Web Deploy instalado. Con esto podríamos generar paquetes de instalación con Visual Studio 2010 y desplegarlos en nuestra máquina destino utilizando IIS Manager. Sin embargo, para poder utilizar un perfil de despliegue desde el menú correspondiente de Visual Studio 2010, aún tenemos que dar algunos pasos más.

Lo primero será activar las conexiones remotas al servicio de Management. Para ello, utilizando el IIS Manager, debemos seleccionar el nodo del servidor y, dentro de la vista de Features, hacer doble click en el icono de “Management Service”. Una vez en la pantalla correspondiente, tal como muestra la siguiente imagen, podremos habilitar las conexiones remotas e indicar que será posible autenticarse tanto con cuentas Windows como con credenciales del IIS Manager.

ActivarConexionesRemotas

El siguiente paso a ejecutar es conceder privilegios sobre la carpeta en la que se van a hacer los despliegues. En este punto tenemos dos estrategias posibles a seguir:

  • Concederle permisos a la cuenta sobre la que corre el servicio de Management, por defecto “Local Service”. Es la opción más sencilla, puesto que no tendremos que crear cuentas extra y, además, más adelante al definir las distintas reglas de despliegue, nos simplificará uno de los pasos.
  • La otra opción es crear una cuenta específica y concederle a ella esos privilegios. También podemos optar por crear un grupo y concederle a dicho grupo los permisos. De este modo, permitir a más usuarios poder realizar el despliegue sería tan sencillo como añadirlos al grupo.

Sea cual sea el enfoque que tomemos, es importante aclarar que los mismos permisos que se asignan aquí, se deberán conceder más adelante sobre el fichero applicationHost.config, por lo que la decisión tiene claras implicaciones de seguridad.

Por simplicidad, aquí vamos a concederle los privilegios a “Local Service”. Lo haremos sobre la carpeta donde vamos a desplegar las aplicaciones web, es decir, sobre Inetpubwwwroot, tal como se puede ver en la imagen.

PermisosDirectorioDeploy

El siguiente paso sería crear el usuario de IIS al que queremos conceder la posibilidad de hacer despliegues remotos de aplicaciones. Podríamos crear, en su lugar, una nueva cuenta Windows y realizar todo el proceso con ella. Sin embargo, tendremos un nivel extra de seguridad si utilizamos un usuario de IIS puesto que, en caso de ser comprometido, este usuario no sería reconocido por el sistema ni por ninguna otra aplicación de la máquina. Para ello, de nuevo es necesario utilizar IIS Manager, pulsar sobre el nodo del servidor y, en la ventana de Features, hacer doble click, esta vez sobre el icono de “IIS Manager Users”. En la parte derecha de la pantalla encontraremos un enlace para añadir nuevos usuarios. Tras pulsar en él, podremos introducir en la ventana emergente un nombre para el usuario y una contraseña.

AnadirIISManagerUsuario

Debemos dar permisos al usuario que acabamos de crear sobre el sitio web en el que va a realizar sus despliegues. Para ello, todavía en IIS Manager, pulsamos sobre el nodo que corresponde al sitio web. Sobre la ventana de Features, en este caso correspondiente al sitio web, debemos hacer doble click en el icono de “IIS Manager Permissions”. En la parte derecha de la pantalla podremos ver un enlace para añadir un usuario, que nos mostrará una ventana como la que se ve el imagen que sigue. Esta pantalla permite elegir entre cuentas Windows o cuentas de IIS Manager. Puesto que el usuario que hemos añadido en el paso anterior es del segundo tipo, usaremos el segundo botón para elegirlo de la lista de usuarios de IIS Manager y añadirlo.

AsignarPermisosSobreSitio

 

Reglas de delegación

Recapitulando, en este punto tenemos correctamente creado nuestro usuario IIS y asignados permisos sobre el sitio web. Necesitamos ahora crear diferentes reglas relacionadas con el despliegue de aplicaciones. Vamos a crear un total de tres, siempre utilizando IIS Manager.

Para crear cada una de las reglas, debemos lanzar IIS Manager si no lo tenemos todavía abierto y pinchar sobre el nodo del servidor. Como en pasos anteriores, pulsaremos en la vista de Features y haremos doble click sobre uno de sus iconos: el que corresponde a “Management Service Delegation”. Pulsando en la parte derecha de la pantalla sobre “Add Rule” aparecerá una ventana emergente como la que muestra la siguiente imagen. Seleccionaremos una regla de tipo “Deploy Applications with Content”. Quedémonos con la secuencia de pasos que nos ha llevado a esta ventana de selección de reglas, puesto que volveremos a ella dos veces más.

AnadirReglaParaUsuario

Aceptamos la configuración por defecto para la regla pulsando OK y en la ventana que aparece despues introducimos el nombre que hayamos dado a nuestro usuario IIS. El resultado que veríamos sería parecido a la siguiente imagen.

ResultadoReglaDespliegue

Hay más reglas que podemos configurar, como se ha visto en la pantalla anterior. Otra de las reglas permite al usuario configurar las listas ACL (Access Control Lists) de los ficheros que componen la aplicación web. Para añadirla, es preciso seguir los pasos anteriores y seleccionar en la pantalla de tipos de regla, la denominada “Set Permissions”. Pulsaremos OK en la ventana que aparecerá para crear la regla con sus valores por defecto, y de nuevo introduciremos el nombre del usuario IIS que creamos varios pasos antes.

La última regla que vamos a añadir permite al usuario la creación de aplicaciones en el servidor IIS. La regla a seleccionar de entre los distintos tipos que aparecen en la ventana tras pulsar el botón de “Add Rule” se llama “Mark Folders as Applications” y presenta una pequeña diferencia con las anteriores: no podemos aceptar la configuración por defecto, puesto que tenemos que seleccionar como cuenta de usuario para ejecutar la regla, la cuenta actual (“Local Service”). Para que la regla funcione correctamente, es necesario que la cuenta sobre la que se ejecuta tenga permisos de escritura sobre el fichero c:windowssystem32inetsvcconfigapplicationHost.config. Por tanto, lo primero será dirigirnos a la carpeta que contiene ese fichero y concederle privilegios de escritura a la cuenta “Local Service”.

PermisosApplicationHosts

Una vez hecho eso, añadiremos la regla siguiendo los pasos anteriores. En la ventana que nos aparece tras pulsar “Add Rule”, debemos seleccionar la opción CurrentUser

ModificacionReglaAplicaciones

Una vez modificada la regla, tras pulsar el botón de OK, la asociaremos con el usuario de IIS como hemos hecho con las anteriores reglas. Con esto, podemos dar la configuración por terminada.

 

Conexión desde IIS Manager

Para comprobar si hemos completado correctamente todo el proceso, podemos descargar la herramienta de administración remota de IIS y tratar de conectarnos al servidor que acabamos de configurar.

Una vez instalada y arrancada, es posible probar si nuestra configuración es correcta intentando conectarnos al sitio con el usuario IIS que creamos. Para ello, tendremos que pulsar en el menú “File” y seleccionar la opción “Connect to a Site”.

En el asistente que va a aparecer tendremos que meter los siguientes datos en la primera pantalla:

  • Server name: dirección IP o nombre de la máquina que hemos configurado.
  • Site name: nombre del sitio. Si es el sitio por defecto de IIS, será “Default Web Site”.

PrimeraPantallaWizardConexion

En la siguiente pantalla del asistente, tendremos que introducir las credenciales de nuestro usuario IIS. Si todo es correcto, tras conectarse nos solicitará confirmación de que confiamos en el certificado con el que se está cifrando la conexión.

AceptarCertificado

Este error nos aparece puesto que el nombre del servidor no coincide con la máquina para la que se ha emitido este certificado. Si pulsamos sobre el botón “View Certificate” para ver más información del mismo, podremos comprobar que en el campo “Issued to” aparecerá siempre “WMSvc-“ seguido del nombre de la máquina. Sin embargo, si generamos un nuevo certificado en IIS con el nombre de la máquina, configuramos el servicio de Management para usarlo con la conexión SSL y especificamos el nombre de la máquina en lugar de su dirección IP para conectarnos a ella, recibiremos este otro aviso.

AvisoCertificadoInvalido_2

En este caso, si pinchamos sobre “View details…” nos encontraremos con un poco útil mensaje indicándonos que ha habido un error desconocido. Si las máquina servidor y cliente estuvieran en un dominio en el que ambas confiaran en una máquina con autoridad certificadora, podríamos generar un certificado de dominio y resolver el problema. Sin embargo, en instalaciones de prueba como la de este ejemplo, bajo grupos de trabajo, no resulta práctico y es más productivo instalar el certificado para poder completar el proceso.

Tras conectarnos y darle un nombre a la nueva conexión, podremos ver información sobre el servidor IIS remoto en nuestra ventana de IIS Manager. Ahora que sabemos que está listo para conectarse a él, nos queda utilizarlo desde Visual Studio 2010.

 

Conexión desde Visual Studio 2010

Ya estamos donde queríamos estar. Tenemos listo Web Deploy en el servidor donde vamos desplegar nuestras aplicaciones web, y ahora sólo nos resta configurar Visual Studio para empezar a disfrutar de las ventajas de esta tecnología.

En cualquier proyecto web que queramos desplegar, sobre el nombre del proyecto, con el botón derecho del ratón, haremos aparecer el menú que veíamos en la primera imagen de este artículo. Debemos pulsar sobre la opción “Publish” para que se muestre una ventana como la siguiente.

PerfilDespliegueVS2010

Los datos a introducir serían:

  • Un nombre para el nuevo perfil de publicación. En el ejemplo, “WebDeploy DevIIS”.
  • Seleccionar un método de publicación: Web Deploy.
  • La dirección del servicio, que será, por defecto, http://servidor:8172/MsDeploy.axd
  • La aplicación web a crear. Puesto que estamos creando sobre el sitio “Default Web Site”, deberá aparecer antes de la barra. Al otro lado, podemos dar el nombre de una aplicación existente o crear una nueva.
  • Si marcamos el checkbox que viene inmediatamente después, estaremos indicando que queremos crear una aplicación en IIS al hacer el despliegue.
  • Necesitamos activar el checkbox relacionado con las credenciales si tenemos un certificado de desarrollo, como en este ejemplo.
  • Por último, introducimos las credenciales del usuario IIS que definimos varios pasos antes en este artículo.

Una vez configurado correctamente el perfil de despliegue, sólo necesitaremos pulsar el botón de Publish para ver cómo, de forma rápida, limpia y segura, tenemos desplegada nuestra aplicación web en nuestro servidor.

 

Conclusiones

Puede que el proceso inicial de configuración del servidor resulte laborioso, pero una vez completados los pasos, la ganancia que en tiempo, facilidad y seguridad de despliegue van a obtener tanto administradores como desarrolladores, bien merece la inversión. Desde el punto de vista de los administradores, la granularidad a la hora de controlar qué tareas se permiten para cada usuario, independizando las cuentas del sistema de las cuentas únicamente destinadas a la gestión del servidor web, justifican el esfuerzo.

Y desde el punto de vista de los desarrolladores, resulta toda una gozada poder desplegar nuestras aplicaciones a entornos de desarrollo, preproducción y producción con en tres clicks. De este modo, pasar de unos entornos a otros dejará de ser la tarea complicada a y propensa a errores que es actualmente.

 

Bibliografía

Recetas de Joel para ser número 1

Me ha encantado esta conferencia de Joel Spolsky sobre cómo hacer que tu aplicación sea la número 1 y no la número 2, 3, etc. Me ha gustado mucho porque la propia conferencia es un ejemplo en sí de que las recetas que Joel propone en ella, realmente funcionan. Aplicadas a su propia conferencia, he disfrutado durante 40 minutos de charla no especialmente relacionada con mis intereses. Pero la forma tan inteligente en que viene “empaquetada” ha conseguido de mí venir rápidamente a mi blog a compartirla con todo el mundo, algo que no he hecho con otras charlas probablemente más orientadas a mis intereses.

Creo que la lección a aprender es sencilla: los productos que tienen éxito son aquellos que parecen tener un toque mágico. Probablemente sea porque todos llevamos un pequeño niño dentro que se siente inmediatamente atraído por estas cosas que parecen brillar con luz propia.

Silverlight: Watermarked control

Mi experiencia con Silverlight no está siendo todo lo placentera que me gustaría. Me estoy encontrado permanentemente en la necesidad de cambiar las cosas que presume por defecto esta tecnología, para adecuarla a mis necesidades.

Una de ellas ha sido la creación de un control Textbox con marca de agua. Buscando un poco por Internet el primer enlace redirige al blog de Tim Heuer, donde tenemos un ejemplo de implementación de este control.

El problema se plantea cuando se quiere utilizar este control para usarlo como control de password. No nos ofrece la posibilidad de enmascarar los caracteres. La solución podría ser algo como esto (no recuerdo si se me ocurrió a mí, lo leí por ahí o adapté algo que hubiera encontrado):

/// <summary>
/// Changes the visual state when the control changes its text.
/// </summary>
/// <param name="sender">Sender of the event.</param>
/// <param name="e">Event arguments.</param>>
private void OnTextChanged(object sender, TextChangedEventArgs e)
{
    if (this.IsPasswordBox == true)
    {
        TextBox textBox = sender as TextBox;
        if (textBox != null)
        {
            string newText = textBox.Text;

            // If we don't have text, clean the plain text back up.
            if (string.IsNullOrEmpty(newText) == true)
            {
                this.Plaintext = string.Empty;
                return;
            }

            // If the new text is shorter than the previous text, we simply cut the previous text
            if (newText.Length < this.Plaintext.Length)
            {
                this.Plaintext = this.Plaintext.Substring(0, newText.Length);
                return;
            }

            // We have an string longer than the previous one. We can have a new character in any place
            // inside the text, so we go along the string to find the new characters (probably only one).
            // This new character will not be the "hidden" character
            StringBuilder newBackup = new StringBuilder();

            // We will use the offset because every time we find a character in the new text that is clear,
            // it is a new character. For example, if the new character is in the 5º position, and the next
            // 6º character is hidden, it would be in the 5º position of the old text. So we use the offset
            // to count in which place the character is in the old text.
            int offset = 0;
            for (int i = 0; i < newText.Length; i++)
            {
                if (newText[i] == 'u25CF')
                {
                    // We add this character from the original plain text to the new one
                    newBackup.Append(this.Plaintext[i + offset]);
                }
                else
                {
                    // We add this character from the new text, because it's one of the new characters
                    newBackup.Append(newText[i]);

                    // We must update the offset
                    offset--;

                    // We must change this character in the new text for the hidden character.
                    newText = newText.Substring(0, i) + 'u25CF' + newText.Substring(i + 1, newText.Length - i - 1);
                }
            }

            // Set the masked text in the control
            textBox.Text = newText;

            // Move the caret to the end of the text
            textBox.SelectionStart = textBox.Text.Length;

            // Update the backup plain text
            this.Plaintext = newBackup.ToString();
        }
    }

    this.ChangeVisualState(true);
}

Por último, quedaría la validación del control. Por desgracia para los que no estamos muy informados sobre cómo funciona Silverlight, el ejemplo de Tim no incluía nada relacionado con la validación del control. Es decir, la validación en sí se realiza con mecanismos ajenos al control, como los Data Annotations. El problema es que, una vez validado el control y encontrado que no es correcta su información, es necesario que pase a un estado “No Válido” y que muestre alguna forma de error.

Después de darle un poco de vueltas y echarle un vistazo a estilos de otros controles por defecto de Silverlight, ésta es la solución que me está funcionando a las mil maravillas (esta vez, sí, cosecha propia 100%).

En primer lugar, el estilo para el control. Me voy a limitar a mostrar las partes directamente relacionadas con la validación, puesto que al final del artículo adjunto tanto el estilo del control como el código del mismo.

<VisualStateManager.VisualStateGroups>
    <VisualStateGroup x:Name="ValidationStates">
        <VisualState x:Name="Valid"/>
        <VisualState x:Name="InvalidUnfocused">
            <Storyboard>
                <ObjectAnimationUsingKeyFrames Storyboard.TargetName="ValidationErrorElement" Storyboard.TargetProperty="Visibility">
                    <DiscreteObjectKeyFrame KeyTime="0">
                        <DiscreteObjectKeyFrame.Value>
                            <Visibility>Visible</Visibility>
                        </DiscreteObjectKeyFrame.Value>
                    </DiscreteObjectKeyFrame>
                </ObjectAnimationUsingKeyFrames>
            </Storyboard>
        </VisualState>
        <VisualState x:Name="InvalidFocused">
            <Storyboard>
                <ObjectAnimationUsingKeyFrames Storyboard.TargetName="ValidationErrorElement" Storyboard.TargetProperty="Visibility">
                    <DiscreteObjectKeyFrame KeyTime="0">
                        <DiscreteObjectKeyFrame.Value>
                            <Visibility>Visible</Visibility>
                        </DiscreteObjectKeyFrame.Value>
                    </DiscreteObjectKeyFrame>
                </ObjectAnimationUsingKeyFrames>
                <ObjectAnimationUsingKeyFrames Storyboard.TargetName="validationTooltip" Storyboard.TargetProperty="IsOpen">
                    <DiscreteObjectKeyFrame KeyTime="0">
                        <DiscreteObjectKeyFrame.Value>
                            <System:Boolean>True</System:Boolean>
                        </DiscreteObjectKeyFrame.Value>
                    </DiscreteObjectKeyFrame>
                </ObjectAnimationUsingKeyFrames>
            </Storyboard>
        </VisualState>
    </VisualStateGroup>
</VisualStateManager.VisualStateGroups>
<Border x:Name="ValidationErrorElement"
        Visibility="Collapsed"
        BorderBrush="#FFDB000C"
        BorderThickness="1"
        >
    <ToolTipService.ToolTip>
        <ToolTip x:Name="validationTooltip" Template="{StaticResource CommonValidationToolTipTemplate}"
            DataContext="{Binding RelativeSource={RelativeSource TemplatedParent}}" Placement="Right"
            PlacementTarget="{Binding RelativeSource={RelativeSource TemplatedParent}}">
            <ToolTip.Triggers>
                <EventTrigger RoutedEvent="Canvas.Loaded">
                    <BeginStoryboard>
                        <Storyboard>
                            <ObjectAnimationUsingKeyFrames Storyboard.TargetName="validationTooltip" Storyboard.TargetProperty="IsHitTestVisible">
                                <DiscreteObjectKeyFrame KeyTime="0">
                                    <DiscreteObjectKeyFrame.Value>
                                        <System:Boolean>true</System:Boolean>
                                    </DiscreteObjectKeyFrame.Value>
                                </DiscreteObjectKeyFrame>
                            </ObjectAnimationUsingKeyFrames>
                        </Storyboard>
                    </BeginStoryboard>
                </EventTrigger>
            </ToolTip.Triggers>
        </ToolTip>
    </ToolTipService.ToolTip>
    <Grid Height="12" HorizontalAlignment="Right"
          Margin="1,-4,-4,0" VerticalAlignment="Top"
          Width="12" Background="Transparent">
        <Path Fill="#FFDC000C" Margin="1,3,0,0" Data="M 1,0 L6,0 A 2,2 90 0 1 8,2 L8,7 z"/>
        <Path Fill="#ffffff" Margin="1,3,0,0" Data="M 0,0 L2,0 L 8,6 L8,8"/>
    </Grid>
</Border>

A destacar especialmente que hay dos partes claramente diferenciadas en el estilo:

  • La parte en la que se definen el grupo de estados “ValidationStates”, formado por los estados “Valid”, “InvalidUnfocused” y “InvalidFocused”. Con estos tres cubriríamos todas las posibilidades relacionadas con el estado del control y su validación.
  • La otra parte relevante del estilo es la definición de un elemento “ValidationErrorElement”, que junto al tooltip nos permite marcar, en este caso en rojo, el control que ha fallado y mostrar un mensaje con información del error.

La segunda parte de este truco consiste en definir las transacciones correspondientes entre los estados del control así como dispararlas ante eventos concretos que acontezcan en el control. En este caso, es necesario definir una propiedad “isErroneous” para indicar cuándo el control ha pasado al estado erróneo. Esta propiedad la controlaremos conectándonos al evento BindingValidationError, tal como se muestra en el siguiente extracto del código del control:

/// <summary>
/// Indicates if the control is in an erroneous state.
/// </summary>
private bool isErroneous;

/// <summary>
/// Initializes a new instance of the <see cref="WatermarkedTextBox"/> class.
/// </summary>
public WatermarkedTextBox()
{
    this.BindingValidationError += new EventHandler<ValidationErrorEventArgs>(WatermarkedTextBox_BindingValidationError);
}

El código que vamos a ligar al evento es tan sencillo como lo que podemos ver a continuación:

/// <summary>
/// Configures the properties of the control to indicate it has an error.
/// </summary>
/// <param name="sender">Sender of the event.</param>
/// <param name="e">Event arguments.</param>
private void WatermarkedTextBox_BindingValidationError(object sender, ValidationErrorEventArgs e)
{
    this.isErroneous = e.Action == ValidationErrorEventAction.Added ? true : false;
    this.ChangeVisualState(true);
}

Controlando la acción que acompaña como argumento al evento, podremos saber si la función ha saltado porque el control ha entrado o ha salido del estado erróneo. Toda la lógica, y por tanto lo relevante, queda relegado al método ChangeVisualState. Este método controlará las transacciones entre los distintos estados en los que puede estar el control. Un extracto de este método, específico para la parte de validación de errores, en el siguiente fragmento:

/// <summary>
/// Change to the correct visual state for the textbox.
/// </summary>
/// <param name="useTransitions">
/// true to use transitions when updating the visual state, false to
/// snap directly to the new visual state.
/// </param>
private void ChangeVisualState(bool useTransitions)
{

    // Update the ValidationStates group
    if (this.hasFocus && this.isErroneous)
    {
        VisualStateHelper.GoToState(this, useTransitions, VisualStateHelper.InvalidFocused, VisualStateHelper.InvalidUnfocused);
    }
    else if (this.isErroneous)
    {
        VisualStateHelper.GoToState(this, useTransitions, VisualStateHelper.InvalidUnfocused);
    }
    else
    {
        VisualStateHelper.GoToState(this, useTransitions, VisualStateHelper.StateValid);
    }
}

Cómo se puede ver, dependiendo de si el control tiene foco o no, se pasa a los distintos estados inválidos en caso de existir errores. Caso de no haberlos, se pasa a un estado de validez que desactiva los adornos de estilo informativos.

El resultado es un control que se comporta igual que los textbox por defecto de Silverlight, a pesar de ser totalmente personalizado. En cualquier caso, cambiar el aspecto que mostrará al entrar en estado erróneo es tan sencillo como modificar el estilo.

Ejemplo

Descargas:

PD: Perdonad los comentarios en inglés en el código, pero es para un proyecto de la empresa en la que trabajo y la pereza me impide ponerme a traducirlo todo 🙂

Desarrollando con Silverlight: Data binding

Unos cuantos apuntes rápidos sobre Silverlight y Data binding, más a título personal (para no olvidarme de estos recursos y estas conclusiones) que con ánimo de salvarle la vida a nadie (no he conseguido ni arreglar mis propios problemas…).

En primer lugar, este artículo en MSDN sobre Data Binding. Algunas de las conclusiones que he podido sacar son:

  • Usar la propiedad DataContext de los objetos para enlazarlos con objetos del CLR (sources). Permite herencia hacia abajo y también se hereda de los controles padre, por lo que si no queremos que un objeto tenga el mismo valor que su padre, habrá que sobreescribirla.
  • La propiedad ElementName nos vale para enlazar a otros controles XAML en lugar de a un objeto del CLR
  • La propiedad RelativeSource para servir para enlazar a elementos en un Control Template.
  • Es necesario implementar INotifyPropertyChanged en los objetos que queramos que funcionen como sources, si queremos tener TwoWay en el binding. Si lo queremos hacer con una colección, tendrá que implementar INotifyCollectionChanged. En cualquier caso, la colección ObservableCollection<T> ya lo hace, con lo que es una buena opción para no reinventar la rueda.
  • Si queremos que nos aparezcan esos cartelitos rojos tan chulos con los mensajes de error que hayamos definido (aquí entra en juego los Data Annotations, estupendos para poder hacer validaciones más finas), tenemos que marcar a true las propiedades ValidatesOnException y NotifyOnValidationError del binding, en el fragmento XAML.

El punto en que me encuentro ahora es que tengo una validación correcta, pero al estar usando un control con su propio manejo de los estados, no es capaz de mostrar los errores (más bien, imagino que pasar a un estado “erróneo” que, por defecto en SL, tendrá asociada una transición que hace aparecer la caja con el error y demás artificios visuales).

Enlaces sobre los que trabajar:

Tan pronto resuelva el problema, publicaré puntualmente.

Bing Maps y la realidad aumentada

Por romper mi silencio reciente (cosas de la ausencia de inspiración), un vídeo espectacular que he descubierto vía el blog corporativo de Microsoft Ibérica. Trata sobre Bing Maps y, en general, sobre la realidad aumentada, la próxima revolución en cuanto a información se trata.

Lo reconozco, me encanta la realidad aumentada. Creo que la era de la información en bruto pero desordenada se termina, y el contexto va a ser la próxima revolución.

Lástima que a día de hoy los servicios que podemos ver en el vídeo no se ofrezcan sobre ciudades españolas. Aunque, al ritmo que progresamos en nuevas tecnologías en este país, lo mismo lo tenemos para… 2020.