127.0.0.1: Hogar dulce hogar
Mostrando entradas con la etiqueta c#. Mostrar todas las entradas
Mostrando entradas con la etiqueta c#. Mostrar todas las entradas

viernes, 24 de junio de 2011

Usando Reflection para tener una única ventana modal personalizable.

 

Tenemos el siguiente escenario: Una aplicación (en este caso en WPF) que tiene varias ventanas modales. Cada ventana contendrá un UserControl único, que a su vez se construirá mediante una serie de parámetros determinados dependientes de lo que requiera el propio control.

El primer enfoque, que lo podemos  llamar a fuerza bruta, sería crear una ventana por cada control de usuario que tenga. El segundo enfoque, algo más fino, sería crear el UserControl antes de la ventana y ajustarlo mediante una propiedad para que lo cargue en el contenedor que le digamos a la ventana. No es lo más elegante, pero estamos instanciando el UserControl en casos particulares y no es algo muy genérico.

El tercer enfoque, que es el que propongo aquí, sería usar Reflection para que la propia ventana sea quien instancie el UserControl y nosotros sólo nos ocupemos de crear la ventana con los parámetros adecuados. Así no tenemos que estar pendiente de la creación del control en sí, sino simplemente de crear la ventana, decirle qué queremos mostrar y mostrarla al usuario.

Para ello, en primer lugar creamos una ventana en WPF. Luego, en el constructor insertaremos los siguientes parámetros:

- Primero, el tipo del control que se instanciará.

- Y después, un listado de parámetros:

  1. 1:  public ModalPropertyWindow(Type childrenType, params object[] logicObject)
  2.  2:          {
  3.  3:              InitializeComponent();
  4.  4:          }

Luego, lo que tenemos que hacer es instanciar el control que queremos con los parámetros que se requieran. Usaremos Activator.CreateInstance, que creará esa instancia con una serie de parámetros. Internamente recorre esa clase y busca un constructor que tenga la disposición de parámetros que le indicamos. Si no lo encuentra, evidentemente producirá una excepción:

  1. this.stackPanel2.Children.Add(Activator.CreateInstance(childrenType, logicObject) as UIElement);

Nótese que directamente añadimos la instancia creada a un StackPanel.

Por último, sólo tenemos que llamar a la ventana e instanciarla con el control que queramos:

  1. ModalPropertyWindow window = new ModalPropertyWindow(typeof(ClientControl), this.currentCliente, this.loggedUser);
  2. window.ShowDialog();

Y así para todas las ventanas que queramos. En resumen: una única ventana modal, todos los controles que queramos y no nos tenemos que preocupar de instanciar cada uno de forma particular.

viernes, 4 de febrero de 2011

[C#] Instancias en runtime de tipos dinámicos

 

Durante el desarrollo de algunas herramientas es necesario generalizarlas para poder reutilizarlas posteriormente, pero manteniendo siempre una funcionalidad concreta. En el caso que nos ocupa, quiero poder listar una serie de atributos genéricos y poder operar con ellos en la precisión deseada, pero partiendo de la premisa anterior. Es decir, no tendré una clase que disponga de algo similar:

  1. public class A
  2.         {
  3.             int a;
  4.             float b;
  5.             int c;
  6.             UInt16 x;
  7.         }

Esto es muy costoso de mantener en futuros proyectos, puesto que estaría obligado a cambiar manualmente todos los atributos del objeto y sus nombres pese a que el comportamiento puede estar definido, puesto que sólo me interesa poder operar con los números.

¿Solución? Pues muy sencillo. Por un lado, vamos a almacenar únicamente el  nombre de atributo junto con su tipo y valor, todo con string. Posteriormente usaremos reflection para cargar el tipo de forma dinámica. Hasta aquí todo es correcto, nuestro mayor problema será que el evidentemente esos objetos dinámicos no son tipados y el compilador no sabrá qué son salvo que lo especifiquemos explícitamente mediante un casting. Es por ello que aquí entran los tipos dinámicos.

Los tipos dinámicos (o dynamic type, en inglés) es una nueva feature de C# 4.0. Recomiendo la lectura de la documentación para no caer en el error de confundirlos con los tipos anónimos. Debemos tener en cuenta que lo que queremos es operar con ellos en tiempo de ejecución, por lo que toda operación no soportada en tiempo de compilación por un tipo no nos servirá. De ahí que los tipos anónimos no sirva para el siguiente ejemplo, ya que no puedo operar entre objetos sin especificar explícitamente qué quiero hacer.

Es por ello que una vez tenemos el tipo deseado a instanciar mediante Reflection, podemos asignarle el valor y el tipo a la variable dinámica. Y podremos operar con ellos como si se tratase de tipos compilados previamente. Veamos un ejemplo:

  1. class Program
  2.     {
  3.         static void Main(string[] args)
  4.         {
  5.             string uno = "1";
  6.             string dos = "2,5";
  7.  
  8.             dynamic a = Convert.ChangeType(uno, Type.GetType("System.Single"));
  9.             dynamic b = Convert.ChangeType(dos, Type.GetType("System.Single"));
  10.  
  11.             Console.Out.WriteLine(a);
  12.             Console.Out.WriteLine(b);
  13.             Console.Out.WriteLine(a+b);
  14.             Console.Out.WriteLine(a-b);
  15.             Console.Out.WriteLine(a*b);
  16.             Console.Out.WriteLine(a/b);
  17.             Console.ReadLine();
  18.         }
  19.     }

A través de Type.GetType() uso Reflection para cargar el tipo que quiero instanciar. Con Convert.ChangeType asigno un valor a un tipo, y todo ello a una variable dinámica. De este modo podremos operar con los números como si se tratasen de floats.

jueves, 6 de enero de 2011

[Ebooks] Libros GRATIS de Rob Miles!

Hoy que vienen los Reyes Magos, os traigo unos cuantos ebooks en pdf de Rob Miles. Los podéis encontrar aquí, aunque pongo los que más puedan interesar en un principio:

CSharp from Java Orange Book: Este libro está destinado para programadores de Java que desean trabajar con CSharp.

CSharp Yellow Book 2010: Un buen libro para introducirse en CSharp. Poco se puede añadir.

miércoles, 29 de diciembre de 2010

[C#] Extension Methods

Con la irrupción de LINQ, el lenguaje se debe adaptar a nuevos usos que agilicen la potencia que proporciona LINQ. Fruto de esta necesidad se incluyó en C# 3.0 los llamados “Extension Methods”. Estos métodos tienen la finalidad, como su propio nombre indica, para extender clases. Y esto puede ser interesante cuando deseo que una clase tenga determinado funcionalidad, propia de un “tipo” derivado pero sin la suficientemente intensidad para crear un tipo derivado a propósito.

Para implementarlo, es muy sencillo. Se debe crear un método estático dentro de una clase estática. A este método le pasaremos un parámetro a través del puntero this, que indica el tipo al que se asociará este método junto con los parámetros que se requieran. Por ejemplo, vamos a extender un tipo base como int con un nuevo método, que llamaremos TestInt() que comprobará cuándo el número es mayor que 0.

   1: public static class Extensions
   2:     {
   3:         public static bool TestInteger(this int number)
   4:         {
   5:             if (number > 0)
   6:             {
   7:                 return true;
   8:             }
   9:             return false;
  10:         }
  11:     }

Luego sencillamente podremos aplicar esto a todos los tipos int, siempre y cuando el acceso al namespace para que encuentre la clase Extensions. Véase el siguiente ejemplo:

   1: public class Item
   2:    {
   3:        int index;
   4:  
   5:        public Item()
   6:        {
   7:            if (index.TestInteger())
   8:            { }
   9:        }
  10:    }

Como se puede apreciar esto es algo bueno, bonito y barato, pero muy arriesgado. Se recomienda usarlo sobre nuestros tipos y siempre que sea posible dedicado a consultas de tipo LINQ o eventos. No debemos usarlo con tipos base (como es el caso) o sobre tipos que no son nuestros porque puede cambiar la implementación en el futuro y posiblemente, tengamos problemas de compatibilidad.

jueves, 16 de diciembre de 2010

[Ebooks] Libro GRATIS Windows Phone programming in C#

A través de Rob Miles nos cuenta que han publicado su curso de programación para Windows Phone 7 en Microsoft Faculty Center.

Windows Phone 7 Blue Book

Este libro es una introducción para Windows Phone 7 para cualquier persona que tenga fundamentos muy básicos de programación. Cuenta con ocho capítulos (WP7, Introducción a Silverlight,Introducción a Visual Studio 2010, Interfaces con Silverlight, Consumo de servicios, XNA, Creando aplicaciones para WP7 y Windows Phone Marketplace).

Descárgalo a través de este enlace. ¡Que aproveche!

martes, 2 de noviembre de 2010

[.NET] Isolated Storage

Tanto en aplicaciones SilverLight como en XNA 4.0 para Windows Phone 7, no se puede usar el espacio de nombres System.File.IO. La pregunta es simple: ¿cómo podemos acceder entonces a un fichero a través de estos sistemas? Pues a través de Isolated Storage.

El motivo es bien sencillo. Una aplicación SilverLight se ejecuta a través del cliente, por lo que por motivos de seguridad sería mejor impedirle cualquier tipo de acceso al sistema externo. En el caso de WP7 es bien distinto, ya que por la política de Microsoft se impide el acceso a la jerarquía de archivos del sistema. Las ventajas de usar un sistema como este son obvias por los siguientes aspectos:

  • Control del código descargado como complementos y la seguridad de que no escribirá en nuestro disco. Sólo se escribirá en el ámbito de la aplicación correspondiente.
  • Almacenamiento de componentes compartidos: Aquellos componentes que se comparten entre aplicaciones se pueden usar en Isolated Storage para garantizar el control de acceso a los datos.
  • Servidor de almacenamiento: Como el acceso a los recursos de Isolated se distribuye en función de los usuarios (más adelante lo explico) el servidor puede distinguir fácilmente entre los datos de los diferentes usuarios.
  • Almacenamiento móvil: Podemos almacenar información móvil del usuario fácilmente.

Evidentemente hay algunos casos donde NO se recomienda usar Isolated Storage:

  • No se debe usar para almacenar claves no encriptadas, valores secretos o contraseñas, ya que el sistema no está protegido contra código mal intencionado u otros usuarios del sistema.
  • Evidentemente,no lo uses para almacenar código!
  • Y tampoco lo uses para almacenar la configuración de la aplicación! Para ello ya dispones de los ConfigurationSettings.

En resumen, ¿qué es Isolated Storage? Pues es un sistema permite trabajar con un sistema virtual de ficheros. Sobre esta encapsulación, ya podemos trabajar con un flujo de datos como si estuviéramos en frente a un sistema real de ficheros. Veamos cómo se puede usar:

En primer lugar debemos incluir el espacio de nombres adecuado para emplear Isolated Storage:

   1: using System.IO;
   2: using System.IO.IsolatedStorage;

Luego vamos a instanciar una referencia al directorio de la aplicación que estamos usando:

   1: IsolatedStorageFile isf = IsolatedStorageFile.GetUserStoreForApplication();

Podemos establecer referencias en función no sólo del ámbito de la aplicación, sino también a partir del usuario, del ensamblado. En este caso deseamos obtenerlo del ámbito de la aplicación, puesto que todo lo que hagamos irá en referencia exclusiva a ella.

Con la referencia podemos manejar Isolated Storage como un sistema de ficheros al uso. Supongamos que queremos comprobar si existe un directorio llamado “SavedGames” y si no existe, lo creamos:

   1: if ( !isf.DirectoryExists("SavedGames") )
   2: {
   3:     isf.CreateDirectory("SavedGames");
   4: }

Con esto se puede crear todo el árbol de directorios. El puntero a la referencia del sistema de ficheros incluye métodos para comprobar ficheros, directorios (como el ejemplo anterior), crear ficheros, crear directorios, borrar y abrir ficheros. Ahora, una vez que seguro tenemos nuestro directorio creado, vamos a almacenar la partida. Para ello primero creamos el fichero para almacenarlo en Isolated Storage y creamos un flujo de datos, como si de un “fichero físico” se tratase:

   1: IsolatedStorageFileStream isfs = new IsolatedStorageFileStream("game.txt",FileMode.Create, isf);
   2: using (StreamWriter writer = new StreamWriter(isfs))
   3: {
   4: ...
   5: }
   6: isfs.Close();

Esto se puede extrapolar a cualquier tipo de fichero que queramos. Además, las operaciones de cargar datos o tratamiento de ficheros son exactamente iguales que en el caso normal.

lunes, 18 de octubre de 2010

[XNA] Tratando sprites como quads empleando shaders

Una de las novedades de XNA 4.0 radica en la posibilidad de usar un SpriteBatch como si de un quad se tratara. La principal ventaja que tiene este método es por un lado que nos evitamos la necesidad de programar y gestionar el quad y por otro la posiblidad de tratar dicho sprite con Shaders.

Para ello vamos a emplera el shader básico que soporta XNA, es decir, el BasifEffect. Con este shader vamos a pinta el sprite. Por lo tanto, lo primero que tenemos que hacer es declarar las variables e instanciarlas:

   1: private SpriteBatch sprite;
   2: private BasicEffect basicEffect;
   1: public override void Initialize()
   2: {
   3:     this.sprite = new SpriteBatch(this.Game.GraphicsDevice);
   4:     this.basicEffect = new BasicEffect(this.Game.GraphicsDevice);
   5:     base.Initialize();
   6: }   

En el método Draw la estructura cambia un poco. Antes, para dibujar un sprite se tenía la siguiente estructura:

sprite.Begin(params….);

sprite.Draw(…);

sprite.End();

Al usar el shader, en primer lugar debemos configurarle los parámetros adecuados. En este caso, como lo vamos a tratar como un quad necesitamos indicarle la matriz de vista y la matriz de proyección. Posteriormente trataremos el sprite como se mostró anteriormente, pero insertándole el shader:

   1: this.basicEffect.View = game.WorldEngine.Camera.ViewMatrix;
   2: this.basicEffect.Projection = game.WorldEngine.Projection;
   3: sprite.Begin(0, BlendState.AlphaBlend, null, null, null, this.basicEffect);
   4: for (int i = 0; i < this.spriteList.Count; i++)
   5: {
   6:     this.sprite.Draw(this.spriteList[i].Texture, this.spriteList[i].Position, Color.White);
   7: }
   8: this.sprite.End();

martes, 21 de septiembre de 2010

[SilverLight] Integrando UserControls en Layouts (II), pero automático.

Volviendo a la integración, la opción del artículo anterior tiene un problema  de escalabilidad. Si tuviésemos 500 pantallas, ¿implica que deberíamos tener un manejador y una clase principal con 500 atributos privados que iremos instanciando? Entonces, si aplicamos la idea anterior y la resolvemos de un modo un poco más elegante la solución será que cada botón u acción de cambio de estado sea el responsable de internamente notificar a la aplicación cuál es ese estado para que instancie correctamente el objeto adecuado. Para ello, vamos a usar la propiedad DataContext que tiene la clase Button como sigue:

   1: <Button x:Name="buttonArticle" Content="Articles" Click="buttonArticle_Click" BorderBrush="#FF7100FF" Cursor="Hand" ClickMode="Press">
   2:                 <Button.DataContext>
   3:                     <local:Article/>
   4:                 </Button.DataContext>
   5:             </Button>
   6:             <Button x:Name="buttonAbout" Content="About" Click="buttonAbout_Click" BorderBrush="#FF7100FF" Cursor="Hand" ClickMode="Press">
   7:                 <Button.DataContext>
   8:                     <local:About/>
   9:                 </Button.DataContext>
  10:             </Button>

Estamos rellenando la propiedad DataContext con el nombre del UserControl que vamos a instanciar. Cuando hagamos click en cada botón, usaremos el Assembly para instanciarlo. Con esto nos ahorramos tener que declarar y gestionar los atributos o variables necesarias para instanciar manualmente cada una de las clases. Por ejemplo, cuando haga click en el botón de About ocurriá lo siguiente:

 

   1: private void buttonAbout_Click(object sender, System.Windows.RoutedEventArgs e)
   2: {
   3:     this.renderOnCanvas((Button)sender).DataContext);
   4: }

Y el método renderOnCanvas será el encargado de obtener la clase adecuada cuyo nombre hemos asignado dentro del DataContext e instanciarla:

   1: private void renderOnCanvas(string controlName)
   2: {
   3:     Type type = this.GetType();
   4:     Assembly assembly = type.Assembly;
   5:     UserControl newPage = (UserControl)assembly.CreateInstance(type.Namespace + "." + controlName);
   6:     canvas.Children.Clear();
   7:     canvas.Children.Add(newPage);
   8: }

De este modo evitamos el añadir manualmente los atributos o variables de cada instancia y podemos automatizarlo, además de traspasar la responsabilidad de cada sección al botón correspondiente.

[SilverLight] Integrando UserControls en Layouts (I)

En el momento de hacer una aplicación se pueden tomar varias decisiones para organizar las diferentes pantallas y opciones que tengan. Una de ellas, la más básica es tener una serie de N canvas en los cuales al pulsar en la opción correspondiente, nosotros haremos que se muestre el adecuado. Sin embargo para mí esto presenta dos grandes inconvenientes:

  1. El diseño de la interfaz se va haciendo más complejo a medida que incrementamos la cantidad de canvas a gestionar.
  2. Al instanciar la pantalla principal, estamos instanciando todas las opciones/pantallas disponibles (independientemente de que sean usadas o no).

Por lo tanto, me surge la duda de cómo se podría evitar esto y me encontrado dos opciones. La primera de ellas consiste en una extensión algo más limpia del método básico, con la diferencia de que cada una de las pantallas tendrá su propia clase y lo único que tendremos que hacer será instanciarlas cuando sean requeridas e integrarlas en nuestro canvas principal. Como por ejemplo, lo siguiente:

Disponemos de varios UserControls, entre los cuales destacan estos ( sírvase únicamente como ejemplo ):

   1: public partial class About : UserControl
   2: {}
   1: public partial class Articles : UserControl
   2: {}

En primer lugar declaro cada uno de los userControls dentro de nuestra MainPage (o donde corresponda):

   1: private About aboutUX;
   2: private Articles articlesUX;

Supongamos que disponemos de un layout de tipo Grid donde tenemos dispuesto un layout Canvas que en un alarde de originalidad lo he denominado canvas; sobre él se irán cargando los respectivos UserControls:

   1: <Canvas x:Name="canvas" Margin="1,1,0,0" Grid.Row="4"/>

La idea es sencilla. Cuando se quiera cambiar a cada pantalla, podemos gestionarlo de modo que sólo instancie el user control si no está creado aún:

   1: private void changeToAbout()
   2: {
   3:     if ( this.aboutUX == null )
   4:     {
   5:         this.aboutUX = new About();
   6:     }
   7:     //borramos el contenido del canvas
   8:     this.canvas.Childrens.Clear();
   9:     //añadimos el nuevo user control
  10:     this.canvas.Childrens.Add(this.aboutUX);
  11: }

Así podemos controlar levemente la gestión de UserControls.

miércoles, 15 de septiembre de 2010

[WCF] Codificaciones de texto y controlando la serialización mediante eventos

El motor de serialización de WCF está limitado a unos cuantos formatos de codificación y por defecto siempre codifica el tipo string en el formato UTF8. A priori no hay ningún problema, pero te puede ocurrir el siguiente escenario:

Tenemos una serie de entidades serializadas con WCF en las que se almacena texto. Por ejemplo, objetos de negocio como artículos en una tienda en la que algunos de sus campos son la descripción, el nombre del artículo, además de todos aquellos que se crean necesarios. Partamos de la base de que hemos serializado nuestro objeto siguiendo el artículo correspondiente. En un momento determinado, comienzas a escribir la información de los objetos en el XML, de un modo a como sigue:

<ArticleManager xmlns="http://schemas.datacontract.org/2004/07/Logic" xmlns:i="http://www.w3.org/2001/XMLSchema-instance">

<Articles>

<Article>

<Name>Halo Reach</Name>

<Description>La apasionante última entrega de la exitosa saga de shooters. </Description>

</Article>

</ArticleManager>

Para una codificacion en UTF-8 no tendríamos ningún problema en deserializar el campo Name, puesto que todos los caracteres son válidos en bytes desde el punto de vista de esa codificación. Sin embargo, no ocurre lo mismo con el campo Description. Este campo incluye caracteres que no son válidos para esa coficación, como por ejemplo los caracteres con tildes (en última) y otros propios del castellano (como el caracter ñ). En el momento en que se deserializa ese dato, provoca una excepción en XmlSerializer por no encontrarse dentro del esquema de UTF8 y similares. ¿Qué podemos hacer? Muchas cosas, como por ejemplo establecer un XmlSerializer propio para nuestras clases de modo que los campos de tipo string se codifiquen y decodifiquen empleando las codificaciones que queramos, crear un decodificador propio exclusivo para manejar texto o bien lo que propongo aquí, que consiste en almacenar la información con bytes de cara al fichero XML pero mostrándola al usuario como string controlando mediante eventos los procesos de serialización y deserialización. Veamos cómo se puede hacer:

Nuestra clase artículo tiene el siguiente juego de atributos:

   1: [DataContract()]
   2: public class Article
   3: {
   4:     [DataMember()]
   5:     public string Name;
   6:     [DataMember()]
   7:     public string Description;
   8: }

Y lo que vamos a hacer es transformar y encapsular los campos miembros serializables dentro de arrays de bytes:

   1: [DataContract()]
   2: public class Article
   3: {
   4:     [DataMember()]
   5:     public byte[] ByteName;
   6:     [DataMember()]
   7:     public byte[] ByteDescription;
   8:  
   9:     public string Name;
  10:     public string Description;
  11:     ...
  12: }

La idea es sencilla y se expuso anteriormente pero la recuerdo. Vamos a guardar los campos en el xml como bytes y al cargar el fichero, automáticamente se transforma en string. Para ello se proporcionan una serie de etiquetas de eventos que se pueden asociar a funciones de la clase que sufre el proceso para que actúen de manejadores del propio proceso. Estas etiquetas son:

  • OnDeserialized: Se invoca cuando el objeto ya se ha deserializado.
  • OnDeserializing: Se invoca justo antes de deserializar el objeto.
  • OnSerialized: Se invoca cuando el objeto ya ha pasado por el proceso de serialización.
  • OnSerializing: Se invoca justo antes de serializar el objeto.

Debemos asociar posteriormente estas etiquetas a un método que nos sirva de manejador. Este método debe tener un único parámetro de tipo StreamingContext cuyo cometido es controlar el origen y destino de la secuencia de serialización. Dicho de otro modo más llano y campechano indica qué objeto es quien inicia la secuencia de datos (en este caso para la seriailzación/deserialización) a través de Context y con State indica cuál es el origen o destino de los datos. Esto último puede servir para especificar si es para un fichero, remoto, serializado, otra máquina, otro proceso, etc. Sabiendo esto, ya sólo nos falta escribir el método para deserializar:

   1: [OnDeserialized()]
   2: private void onDeserialize(StreamingContext context)
   3: {
   4:     this.Name = Encoding.Unicode.GetString(this.ByteName);
   5:     this.Description = Encoding.Unicode.GetString(this.ByteDescription);
   6: }

Para habilitar caracteres con tildes, ñ y otros, he usado la codificación de Unicode que si permite usarlos. Como se aprecia, se invoca una vez el objeto ya está serializado y se han leído los miembros (los campos ByteName y ByteDesription tendrás sus oportunos valores). Ahora vayamos a por la serialización, que invocaremos antes de serializar para preparar los arrays de bytes:

   1: [OnSerializing()]
   2: private void onSerialize(StreamingContext context)
   3: {
   4:     this.ByteName = Encoding.Unicode.GetBytes(this.Name);
   5:     this.ByteDescription = Encoding.Unicode.GetBytes(this.Description);
   6: }

Con este sencillo procedimiento no tendremos problemas de conversiones entre cualquier tipo de codificaciones.