Buscar este blog

martes, 8 de junio de 2010

Temas Dinámicos en Silverlight 3.0

Cuando creamos una aplicación Silverlight es común que queramos aplicar diferentes Temas a nuestra aplicación para que los colores y efectos sean personalizables. El Silverlight Toolkit de CodePlex nos permite hacer esto de manera sencilla. Para este ejemplo requieres los siguientes prerrequisitos.

·  Silverlight 3.0.

·  Silverlight Toolkit. Puedes descargarlo de este enlace http://silverlight.codeplex.com/Wikipage

·  Visual Studio 2008.

·  Expression Blend 3.0. Esta herramienta no se require realmente, pero se recomienda utilizar Blend cuando se va a realizar algún diseño de Interfaz de usuario, es más sencillo y más rápido que en Visual estudio para dichas actividades.

Primero que nada es necesario crear una aplicación Silverlight con su respectivo Website.



A continuación, hay que agregar algunos controles a los que se les desea aplicar el tema. Para este ejemplo se agregan algunos TextBox , algunos TextBlocks y un botón. A continuación se muestra el Xaml creado.




La linea más importante es:




Con esta linea estamos indicando al plugin de Silverlight que usaremos la dll Theming.dll que forma parte del Silverlight Toolkit. Esto no funciona si no se agregan las referencias correspondientes, así que hay que agregar la referencia a la dll System.Windows.Controls.Theming.Toolkit.dll.

Para ello, es necesario buscarla en el path C:\Program Files\Microsoft SDKs\Silverlight\v3.0\Toolkit\Nov09\Themes\Xaml (Toma en cuenta tu versión de SO por si el path pudiera cambiar). Este es el path de instalación por defecto del Silverlight Toolkit en Windows 7 a 32 bits. Luego hay que copiar 2 archivos de Tema (o más si así lo decides) al directorio raíz de la aplicación. Personalmente copié los siguientes:

·  System.Windows.Controls.Theming.BubbleCreme.xaml

·  System.Windows.Controls.Theming.BureauBlue.xaml

Ahora es necesario utilizar el siguiente código para hacer el cambio de temas:

private void UserControl_Loaded(object sender, System.Windows.RoutedEventArgs e)
{

// TODO: Add event handler implementation here.


Uri uri = new Uri(@"SLTheming;component/System.Windows.Controls.Theming.BureauBlue.xaml",
UriKind.Relative);


ImplicitStyleManager.SetResourceDictionaryUri(LayoutRoot, uri);


ImplicitStyleManager.SetApplyMode(LayoutRoot, ImplicitStylesApplyMode.Auto);


ImplicitStyleManager.Apply(LayoutRoot);



}



private void Button_Click(object sender, System.Windows.RoutedEventArgs e)

{


Uri uri = new Uri(@"SLTheming;component/System.Windows.Controls.Theming.BubbleCreme.xaml", UriKind.Relative);

ImplicitStyleManager.SetResourceDictionaryUri(LayoutRoot, uri);


ImplicitStyleManager.SetApplyMode(LayoutRoot, ImplicitStylesApplyMode.Auto);

ImplicitStyleManager.Apply(LayoutRoot);


}


Con éste código, solo hay que iniciar la aplicación con el Website correspondiente y presionar el botón para revisar
cómo el Website cambia de esta apariencia:




A esta:




jueves, 20 de mayo de 2010

Compartiendo información entre Aplicaciones Silverlight

Dentro de una solución dentro de cuya arquitectura se utilicen aplicaciones Silverlight, puede ser bastante útil el pasar información de un .XAP a otro a manera de estado, o como parámetros o banderas para ejecutar tal o cual acción. Esta funcionalidad se logra con Isolated Storage.

Isolated Storage funciona de manera similar a las cookies dentro de un cliente que explora una página web, es decir, crea archivos dentro de la máquina cliente que accede a la aplicación, y puede utilizarse para guardar el estado de una aplicación o bien, para pasar grandes cantidades de información, siempre y cuando el cliente permita crear archivos de gran dimensión. Estos son los principales puntos sobre el manejo de Isolated storage:

• La forma en que Silverlight 2 maneja Isolated Storage es a través de archivos que se guardan en la máquina del cliente de acuerdo a su sistema operativo.
Las ubicaciones son las siguientes:



Sistema Operativo


Ubicación Física


Windows Vista


<SYSTEMDRIVE>\Users\<user>\AppData\LocalLow\Microsoft\Silverlight\is


Windows XP


<SYSTEMDRIVE>\Documents and Settings\<user>\Local Settings\Application Data\Microsoft\Silverlight\is


MAC OS


/Users/<user>/Library/Application Support/Microsoft/Silverlight/is




• El Isolated Storage se basa en la identidad del usuario y de la aplicación, o bien de la identidad del usuario y la identidad de la aplicación.

• Cada aplicación puede guardar su propio Isolated Storage, el cual es visible solo para esa aplicación mientras se esté ejecutando (o mientras no lo eliminen de la ruta física).

• Todas las aplicaciones de un mismo sitio tienen un Isolated Storage común, el cual es visible para todas las aplicaciones del mismo sitio. La forma de grabar a este Storage es mediante el siguiente código:



using (var store = IsolatedStorageFile.GetUserStoreForSite())

using (var stream = store.CreateFile("sharedhello.txt"))

using (var writer = new StreamWriter(stream)) {

writer.Write ("Hello Shared World");

}

• La forma de leer de este Storage desde otra
aplicación es la siguiente:



using (var store = IsolatedStorageFile.GetUserStoreForSite())
{

if (store.FileExists("sharedhello.txt"))
{

using (var stream = store.OpenFile("sharedhello.txt",
FileMode.Open))

using (var reader = new StreamReader(stream)) {

string contents = reader.ReadToEnd(); // "Hello


Shared World"

}

}

}

• El Isolated Storage es
medido en Quotas, que para silverlight representa la cantidad de espacio físico disponible para este almacenaje. Por default el espacio del Quota es de 1MB.

• Es posible solicitar más espacio físico al usuario si así se requiere, pero el código debe dispararse desde un evento disparado por el usuario (un click de botón) y debe esperar la autorización del usuario.
Esto es que una vez que se ejecuta el código, la aplicación pide permiso al
usuario para incrementar su espacio disponible de almacenaje.
Esto se hacemediante las siguientes instrucciones:



using (var store = IsolatedStorageFile.GetUserStoreForApplication())
{

// 5 MB of isolated storage space is needed

int spaceNeeded = 1024 *
1024 * 5;



if (store.AvailableFreeSpace < spaceNeeded)
{

if (store.IncreaseQuotaTo(store.Quota
+ spaceNeeded))

{

// The user accepted the request

}

}

}

viernes, 7 de mayo de 2010

Manejo de errores en Silverlight con WCF.

Cuando se manda llamar un servicio WCF desde una aplicación Silverlight y el servicio arroja un error,  el
cliente de Silverlight regresa únicamente un error muy genérico:


The remote server returned an error: NotFound


Este mensaje no ayuda en mucho ni al usuario ni al desarrollador para conocer del error, aún más, es necesario por cuestiones de seguridad hacer una división de la información del error: un mensaje amistoso al usuario y un mensaje detallado al administrador del sistema o el desarrollador.




La primera parte  del error dedicada al administrador de la aplicación consiste en escribir a detalles la excepción que se genera en el código. Hay muchas herramientas que permiten hacer este loggeo de errores (Log for Net, Microsoft Enterprise Library, entre otros). Para este ejemplo se establece un loggeo sencillo  al Application Log del servidor donde se encontraría nuestro servicio. El método además captura dos parámetros: una “Entidad” que indica la entidad de negocio donde ocurrió el error (como por ejemplo País, Estado, Empleado, etc) y una acción, que indica la operación que no pudo ser realizada (Guardar, Seleccionar, Eliminar, etc). Con estos dos parámetros podemos entregar un mensaje amistoso que será enviado al usuario.


public class ExceptionLogger
    {
        public static string LogAndReturnError(Exception ex, string entidad, string accion)

        {

            StringBuilder cadenaError = new StringBuilder();           
            cadenaError.Append("Ocurrió un error en el servicio de la aplicación: \n\n
            cadenaError.Append("Error: ");
            cadenaError.Append(ex.Message);
            cadenaError.Append("\n\nStackTrace: \n\n");
            cadenaError.Append(ex.StackTrace);
            EventLog.WriteEntry("NombreAplicación", cadenaError.ToString(), EventLogEntryType.Error);

            cadenaError = new StringBuilder();
            cadenaError.Append(string.Format("Ocurrió un error al intentar {0} {1}. Contacte al administrador del sistema.", accion, entidad));
            return cadenaError.ToString();
        }
    }


Lo siguiente es generar una entidad como un DataContract para el objeto que almacene los errores que ocurran en el servicio.
[DataContract]
    public class ServiceError
    {              
        [DataMember]   
        public string Type { get; set; }   
        [DataMember]   
        public string Message { get; set; }
    }


Lo que sigue es hacer que nuestras operaciones del servicio puedan arrojar un objeto de error hacia la capa de interfaz. Para ello utilizaremos un parámetro de salida en nuestros métodos de Servicio y utilizaremos la clase de loggeo de error.

[OperationContract]
public void DoWork(string Parameter1, out ServiceError errorService)
        {
errorService = null;

try
    {
       //Operaciones a realizar
    }
catch (Exception ex)
            {
               errorService = new ServiceError
               {
                   Message = ExceptionLogger.LogAndReturnError(ex, "Acción", "Entidad"),
                   Type = "Aplicación"
               };
            }
}



Ahora necesitamos una aplicación Silverlight que consuma nuestro servicio. Para ello hay que agregar una aplicación Silverlight y agregar una referencia  nuestro servicio. Una vez que llamamos nuestro servicio desde tendríamos lo siguiente:

MyServiceClient client = new MyServiceClient();
client.DoWorkCompleted += new EventHandler<DoWorkCompletedEventArgs>(client_DoWorkCompleted);
            client.DoWorkAsync("Hola Mundo");

                void client_DoWorkCompleted(object sender, DoWorkCompletedEventArgs e)
        {
//Error propio de WCF
            if (e.Error == null)
            {
//Nuestro Parámetro de Error!!!
               if (e.errorService == null)
               {
                   if (e.Result != null)
                   {
//Manipular resultado
}
                 }
            }
          }



Esta es una manera sencilla de implementar el manejo de errores en Silverlight.