Comunidad .NET de Cd. Juárez

Más que un foro virtual. Más que preguntas y respuestas. Somos una comunidad
que se ayuda para producir desarrolladores profesionales de alto rendimiento.
Welcome to Comunidad .NET de Cd. Juárez Sign in | Join | Help
in Search

Compilando el mundo

  • Impresora PDF por e-mail

    De casualidad me encontré por ahi una nueva forma de sacarle partido a los correos electrónicos.

    A más de uno nos habrá pasado que tenemos un fichero y queremos convertirlo en formato PDF y no tenemos a mano ninguna herramienta que nos los convierta.

    Pues simplemente necesitas tener en tu lista de contactos estas direcciones que te sacaran de algun que otro apuro }:-)
    1. pdf@koolwire.com: Si envías un correo a esta dirección, te devuelve el adjunto convertido en pdf.
    2. pdf2txt@adobe.com: El archivo pdf ajunto enviado a esta dirección se te devuelve como texto plano.
  • Invocar métodos dinámicamente conocidos en tiempo de ejecución

        La mayoria de ocasiones sabremos qué métodos invocar antes de la compilación, por lo tanto, podemos invocar directamente al método con sus parámetros, pero habrá ocasiones en las que esto no sea así y necesitemos invocarlos dinámicamente en tiempo de ejecuación.

        Por ejemplo, imaginemos que tenemos una clase llamada Calculadora que contiene un método para cada tipo de operación (Suma, Resta, Multiplicacion y Division) y solo sabremos durante la ejecución de nuestro programa el método que tenemos que llamar. ¿Qué harias si en un momento necesitamos ejecutar el método Suma con los parámetros 2 y 3, sabiendo unicamente el string "Suma" y el int 2 y 3 ?

        Este problema lo podemos solucinar utilizando el espacio de nombres Reflection, mediante el uso del método InvokeMember en el objeto Type.

        El método InvokeMember acepta los siguientes parámetros: nombre del método a invocar, una seria de flags de invocación, un atributo con una seria de propiedades de la misma, el objeto sobre el que se realiza la invocación y un arraya de object que contienen los parámetros del método. Para más información de los parámetros ver aquí.

        Un ejemplo seria el siguiente:

    string metodo = Console.ReadLine(); //Recogemos por teclado el nombre del método a ejecutar.

    int op1 = Convert.ToInt32(Console.ReadLine()); //Recogemos el primer operando

    int op2 = Convert.ToInt32(Console.ReadLine()); //Recogemos el segundo operando

    Type tipoObjeto = typeof(Calculadora); //Obtenemos el tipo del objeto calculadora.

    object[] parametros = new object[] { op1, op2 };

    int resultado = (int)tipoObjeto.InvokeMember(metodo, BindingFlags.DeclaredOnly | BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.Instance | BindingFlags.InvokeMethod, null, pedroCalculadora, parametros);

    Console.WriteLine(resultado); //Imprimimos por pantalla el resultado

     

          Del mismo modo podemos trabajar con las propiedades de un objeto, simplemente llamaremos al método GetProperty el cual acepta como parámetro el nombre de la propiedad y nos retorna un PropertyInfo a y con este objeto ya podemos acceder al metodo get de esta propiedad. Como mejor lo veremos es con un ejemplo. Imaginemos que en la clase Calcualdora del ejemplo anterior tenemos una propiedad para el nombre llamada Nombre y queremos acceder al Get de la misma en ejecuación simplemente conociendo el nombre de la propiedad:

     

    Type tipoObjeto = typeof(Calculadora); //Obtenemos el tipo del objeto calculadora.

    PropertyInfo propiedadInfo= tipoObjeto.GetProperty("Nombre");//Obtenemos la propiedad que queremos.

    MethodInfo metodoInfo = propiedadInfo.GetGetMethod();//Obtenemos el método de la propiedad.

    string resultado = (string)metodoInfo.Invoke(pedroCalculadora, new object[0]);

    Console.WriteLine(resultado); //Imprimimos por pantalla el resultado

     

    El resultado será el nombre de la calculadora y lo hemos obtenido conociendo el nombre de la propiedad.

  • Jugando con la información de los XML´s ---> xsd.exe (Parte 2)

    En el post anterior comentamos cómo deserializar un fichero XML para obtener una serie de instancias de unas clases que lo representan. 

    Ahora vamos a realizar lo contrario, a partir de una instancia de la clase Libro, vamos a generar un XML asociado que se ajusta al esquema.

     

    Nos creamos un objeto de la clase libro y le añadimos la información del libro a través de las propiedades de la clase que se proporcionan. A continuación con creamos un fichero con la clase FileStream y posteriormente con la clase XmlSerializer serializamos el objeto de la clase Libro que nos hemos creado anteriormente.

     

     

    static void Main(string[] args)

    {

        Libro milibro = new Libro();

     

        milibro.precio = (double)30;

        milibro.precioSpecified = true;

        milibro.Titulo = "Los tres cerditos";

        milibro.Autores = new string[] {"Luis","Manolo","Laura" };

        milibro.Editorial = "MiEditorial";

     

        FileStream fs = new FileStream("prueba.xml", FileMode.Create);

        XmlSerializer xs = new XmlSerializer(typeof(Libro));

        xs.Serialize(fs, milibro);

        fs.Close();

    }

    Nota:  Si nos fijamos en la clase Libro que se ha generado podemos ver como para el atributo precio hay un atributo en la clase que representa al mismo y otro de tipo boleano asociado. Esto se debe a que para los elementos que pueden o no aparecer (minOccurs=0) o para el caso de los atributos, que nunca son obligatorios, debemos indicar si queremos que se serialicen o no. Para ello en este caso indicamos que precioSpecified=true.

     

    El resultado del ejemplo anterior seria el siguiente documento XML:

     

    <?xml version="1.0"?>

    <Libro xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:xsd="http://www.w3.org/2001/XMLSchema" precio="30">

     

      <Titulo>Los tres cerditos</Titulo>

      <Autores>Luis</Autores>

      <Autores>Manolo</Autores>

      <Autores>Laura</Autores>

      <Editorial>MiEditorial</Editorial>

    </Libro>

     

    Y esto ha sido todo para serializar y deserializar objetos en ficheros XML.

  • Jugando con la información de los XML´s ---> xsd.exe (Parte 1)

    Muchas veces necesitamos crear o leer información de un fichero XML que se debe ajustar a un esquema XSD, para ello podemos utilizar la librería que viene con el Framework (System.Xml) y echar mano de las clases XmlElement, XmlNode, etc.

     

    Hoy día estoy utilizando otra opción que me ha impresionado mucho por la potencia que te ofrece para trabajar con este tipo de documentos. El .Net Framework nos ofrece una herramienta llamada xsd, esta herramienta genera clases de esquemas XML o de Common Language Runtime a partir de archivos XDR, XML y XSD, o a partir de clases de un ensamblado de motor de tiempo de ejecución.

    Esto significa que podemos generar a partir de un fichero xsd (esquema) una estructura de clases (en cualquier lenguaje de programación soportado por el CLR). Con la estructura de clases que nos genera podemos deserializar un documento XML con XmlSerializer de este modo nos instanciamos una serie de objetos de estas clases con la información del documento. Del mismo modo si serializamos objetos de estas clases obtenemos de forma directa un documento XML que se corresponde con ese esquema.

     

    Veamos un ejemplo de como utilizar esta herramienta:

     

    Tenemos un XSD para representar un libro que tiene un precio, un titulo, varios autores y una editorial con la siguiente estructura:

     

     

    <?xml version="1.0" encoding="UTF-8"?>

    <xsd:schema xmlns:xsd="http://www.w3.org/2001/XMLSchema">

         <xsd:element name="Libro">

             <xsd:complexType>

                <xsd:sequence>

                     <xsd:element name="Titulo" type="xsd:string"/>

    <xsd:element name="Autores" type="xsd:string" maxOccurs="10"/>

                     <xsd:element name="Editorial" type="xsd:string"/>

                </xsd:sequence>

                <xsd:attribute name="precio" type="xsd:double"/>

            </xsd:complexType>

       </xsd:element>

    </xsd:schema>

     

     

    Ahora con xsd generamos las clases asociadas a este esquema. Para ello nos vamos a la consola de Visual Studio (Inicio/Todos los programas/Microsoft Visual Studio 2005/Visual Studio Tool/Símbolo del sistema de Vidual Studio 2005), accedemos a la carpeta donde se encuentra nuestro xsd y ejecutamos el siguiente comando:

     

    xsd  –c  esquema.xsd

     

    Para más información sobre los parámetros de xsd.exe mira aquí.

    Una vez ejecutado este comando ya nos habrá creado un fichero con la extensión correspondiente al lenguaje por defecto que le tengamos indicado al Visual Studio, en mi caso se ha generado un fichero esquema.cs.

    Este fichero contendrá una clase llamada Libro que representa al elemento compuesto libro, esta clase contiene para cada elemento que contiene el elemento libro un atributo y su propiedad asociada, por ejemplo para titulo tenemos:

     

                private string tituloField;

          public string Titulo {

            get {

                return this.tituloField;

            }

            set {

                this.tituloField = value;

            }

          }

    De este modo ya podemos serializar y deserializar un XML y trabajar con tu contenido.

    Ahora vamos a ver como acceder a la información de un documento XML, utilizando la clase generada deserializando previamente.

     

    Dado un XML que se ajusta al XSD esquema.xsd:

     

     

    <?xml version="1.0" encoding="UTF-8"?>

    <Libro xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="libro.xsd" precio="20">

            <Titulo>Caperucita roja</Titulo>

            <Autores> Manolo Pérez</Autores>

            <Autores>María García</Autores>

            <Editorial>Editorial</Editorial>

    </Libro>

     

    Si ejecutamos este código:

     

     

    static void Main(string[] args)

    {

         FileStream fs = new FileStream("ejemplo.xml", FileMode.Open);

         XmlSerializer xs = new XmlSerializer(typeof(Libro));

         Libro milibro = (Libro)xs.Deserialize(fs);

         fs.Close();

     

         Console.WriteLine("Precio: " + milibro.precio);

         Console.WriteLine("Titulo: "+milibro.Titulo);

         foreach (string titulo in milibro.Autores)

         {

              Console.WriteLine("Autor: "+titulo);

         }

         Console.WriteLine("Editorial: " + milibro.Editorial);

         Console.ReadLine();

    }

    La salida es:

     

    Precio: 20

    Titulo: Caperucita roja

    Autor: Manolo Pérez

    Autor: María García

    Editorial: Editorial

     

    De esta forma tan sencilla podemos recoger la información de un XML en una estructura de objetos y de este modo acceder a la misma.

    En un proximo post comentaremos cómo hacer lo contrario, una vez que tenemos los objetos de estas clases instanciados serializarlos y crear un documento XML.

                                                                    

                                                           To be continue...

     

  • Generación Dinámica de Código II

    En un post anterior comenté brevemente como se trabaja con la generación de código utilizando las librerías System.CodeDom y System.CodeDom.Compiler. En este post extenderemos un poco más los ejemplos visto y veremos cómo podemos construir otro tipo de estructuras utilizadas comúnmente en nuestros programas.

     

    Declarar un atributo:

     

    La declaración de atributos consta de tres parte principales, la visibilidad, el tipo de atributo y el nombre.

     

     

    //Declaramos el atributo con el nombre y tipo

    CodeMemberField atributo = new CodeMemberField(("String","MiAtrib");

     

    //Indicamos la visibilidad

    atributo.Attributes = MemberAttributes.Private;

     

     

               

    //Comentarios para documentación

    atributo.Comments.Add(new CodeCommentStatement("<summary>", true));

    atributo.Comments.Add(new CodeCommentStatement("comentario", true));

    atributo.Comments.Add(new CodeCommentStatement("</summary>", true));

     

               

    La salida de este ejemplo será un fragmento de código como el siguiente:

                   

          /// <summary>

          /// comentario

          /// </summary>

          public string MiAtrib;

     

     

    Crear una propiedad:

     

     

    Una propiedad como ya sabemos está compuesta de su visibilidad, el tipo, el nombre y opcionalmente un set y get asociados a un atributo.

     

     

    CodeMemberProperty propiedad = new CodeMemberProperty();

    // Indicamos el nombre de la propiedad.

    propiedad.Name = "MiPropiedad";

    // Indicamos el tipo de la propiedad.

    propiedad.Type = new CodeTypeReference("String");

    // Establecemos la visibilidad de la propiedad.

    propiedad.Attributes = MemberAttributes.Public;

     

     

    //Comentarios para documentación

    propiedad.Comments.Add(new CodeCommentStatement("<summary>", true));

    propiedad.Comments.Add(new CodeCommentStatement("comentario", true));

    propiedad.Comments.Add(new CodeCommentStatement("</summary>", true));

     

     

    // Creamos el return del get

    CodeMethodReturnStatement cmReturn = new CodeMethodReturnStatement(new CodeFieldReferenceExpression(new CodeThisReferenceExpression(), nombreAtributoAsociado));

     

    //Añadimos el get

    propiedad.GetStatements.Add(cmReturn);

     

    // Creamos el contenido del set

    CodeAssignStatement caSet = new CodeAssignStatement(new CodeFieldReferenceExpression(new CodeThisReferenceExpression(), nombreAtributoAsociado), new CodePropertySetValueReferenceExpression());

     

    // Añadimos el set

    propiedad.SetStatements.Add(caSet);

               

    Nota: nombreAtributoAsociado sería el atributo que está asociado a la propiedad.

    La salida de este ejemplo será un fragmento de código como el siguiente:

     

     

                                         /// <summary>

          /// comentario.

          /// </summary>

          public string Mipropiedad

          {

              get { return MiAtrib; }

              set { MiAtrib = value; }

          }

     

     

     

     

    Crear una enumeración:

     

    Para declarar una enumeración debemos especificar la visibilidad, tipo, nombre y los elementos de la misma.

     

    //Declaramos el nodo para la enumeracion.

    CodeTypeDeclaration enumerac = new CodeTypeDeclaration("MiEnum");

     

    //Debemos indicar que el CodeTypeDeclaration va a ser tipo enumeración.

    enumerac.IsEnum = true;

     

    //Indicamos la visibilidad.

    enumerac.Attributes = MemberAttributes.Public;

     

    //Comentarios.

    enumerac.Comments.Add(new CodeCommentStatement("<summary>", true));

    enumerac.Comments.Add(new CodeCommentStatement("comentario", true));

    enumerac.Comments.Add(new CodeCommentStatement("</summary>", true));

     

    //Representa un campo.

    CodeMemberField miembro;

     

    //Vamos creando los valores de la enumeracion y los vamos añadiendo a la misma.

    List<string> cadenasAtributos =new List<string>("elem1","elem2");

    for (int i = 0; i < cadenasAtributos.Count; i++)

    {

     

         miembro = new CodeMemberField();

         //Le damos un nombre al campo.

         miembro.Name = cadenasAtributos[ i ];

         //Añadimos el campo a la enumeración.

         enumerac.Members.Add(miembro);

    }

     

     

     

    La salida de este ejemplo será un fragmento de código como el siguiente:

           

     

    /// <summary>

    /// comentario.

    /// </summary>

    public enum MiEnum {

               

           elem1,

               

           elem2,

    }

  • Generación Dinámica de Código I

    Un tema en el que estoy últimamente muy metido por cuestiones del trabajo es en la generación dinámica de código y la forma en que esta se puede automatizar para crear ficheros de código fuente en lenguajes soportados por el CLR.

     Para conseguir esto necesitamos básicamente apoyarnos en dos pilares, por un lado debemos generar un árbol en el que se recoja toda la parte sintáctica y semántica del programa fuente que queremos construir (independiente del lenguaje de programación) para ello utilizaremos las clases que podemos encontrar bajo el espacio de nombre System.CodeDom. Por otro lado, una vez que ya tenemos el árbol podemos pasar a generar el código asociado al mismo en un lenguaje especifico de programación soportado por el CLR ( de este modo, en un solo paso podemos generar el mismo programa en varios lenguajes como veremos más adelante).

    En el espacio de nombres System.CodeDom podemos encontrar clases que nos representan cada una de las estructuras posibles que nos podemos encontrar en cualquier programa (Clases, Métodos, Propiedades, Enumeraciones, Parámetros, estructuras repetitivas, asignaciones, etc. etc...)

    La raíz de nuestro arbol será un objeto del tipo CodeCompileUnit el cual recogerá una serie de objetos del tipo CodeNamespace el cual a su vez contiene una seria de Imports (objetos CodeNamespaceImport, utlizados para "importar" namespaces) y Types (una colección de objetos CodeTypeDeclaration, los cuales pueden representar código de clases, estructuras, interfaces y enumeraciones)

    Finalmente para la generación de un programa en un lenguaje determinado necesitamos un generador de código, para ello utilizaremos cualquier clase que implemente la interface ICodeGenerator como es la clase CodeDomProvider. (localizada en el espacio de nombres System.CodeDom.Compiler). Para cada lenguaje especifico tenemos un proveedor (CSharpCodeProvider, VBCodeProvider,  JScriptCodeProvider, VJSharpCodeProvider).

     Veamos una serie de ejemplos de como podemos explotar esta funcionalidad:

     Crear un espacio de nombres:

    // Declara el NameSpace indicando el nombre del mismo.

    CodeNamespace espacioNombres = new CodeNamespace("MiEspacioNombres");

     

    //Espacios de nombres a importar

    CodeNamespaceImport import = new CodeNamespaceImport("System.CodeDom");

     

    //Añadimos el espacio de nombres

    espacioNombres.Imports.Add(import);

     

     

    Crear una clase:

    CodeTypeDeclaration clase = new CodeTypeDeclaration("MiClase");

     

    //Indicamos que es clase.

    clase.IsClass = true;

     

    //Indicamos la visibilidad

    clase.Attributes = MemberAttributes.Public;

               

    //Añadimos a la clase sus elementos (atributos, propiedades, métodos…)

    clase.Members.Add(new CodeTypeMember());

     

    //Añadimos la clase al espacio de nombres.

    espacioNombres.Types.Add(clase);

     

     

     

    Ahora pasaremos a ver como generar el código y crear un fichero con el mismo.

    Este ejemplo lo haremos para lenguaje C#, para cualquier otro habría simplemente que cambiar el proveedor.

    CodeCompileUnit ccu = new CodeCompileUnit();

     

    //Generador de codigo que vamos a utilizar para un determinado lenguaje.

    CodeDomProvider proveedor;

    ccu.Namespaces.Add(espacioNombres);

     

    //Generador de codigo que vamos a usar

    proveedor = new CSharpCodeProvider();

     

    //Guardamos la extension que debe tener el archivo

    string extension = proveedor.FileExtension;

     

    //Creamos un TextWriter con el cual vamos a guardar el codigo generado

    TextWriter t = new StreamWriter(rutaDestino+"\\"+nombreFichero+"."+extension, false);

     

    //Finalmente generamos el codigo

    proveedor.GenerateCodeFromCompileUnit(ccu, t, new CodeGeneratorOptions());

     

    //Cerramos el fichero.

    t.Close();

     

    En proximos post comentaremos como utilizar más de estas estructuras...

  • Asociar el evento de un control hijo al contenedor padre.

    Es típico tener controles personalizados en nuestras aplicaciones, estos controles normalmente estan compuestos de una serie de elementos los cuales permiten tratar sus eventos asociando un método que se ejecutará cuando el evento se dispare.

    Este método debe estar dentro del ámbito del control personalizado de modo que cuando utilicemos nuestro control en aplicaciones que desarrollemos no podremos modificar el código asociado a los eventos de los elementos hijos de dicho control.

    En este post explicaré una forma sencilla de solucionar este problema.

    El ejemplo que pretendo mostrar consta de lo siguiente: un proyecto windows form donde tendremos en nuestro formulario principal un control personalizado, el control personalizado consta de un simple botón. Se pretende que desde el código del formulario principal podamos asociar código que se ejecutará cuando se dispare por ejemplo el evento de Click del botón que contiene el control personalizado.

    Los pasos a seguir con los siguientes:

    1. Nos creamos un proyecto de Windows Form.
    2. Añadimos sobre la solución un Control de usuario llamado MiControl.
    3. Al control de usuario le añadimos un botón llamado MiControl.
    4. Seleccionamos el botón y en el cuadro de propiedades dentro de eventos hacemos doble click en el evento Click, de este modo le asociamos un método a dicho evento.
    5. Añadimos el código siguiente:

     

    //Declaramos un delegado para el evento de clickado.

    public delegate void ClickEventHandler(object sender, System.EventArgs e);

     

    //Asociamos el evento a una categoria, en concreto a la categoria Action

    [Category("Action")]

     

    //Descripcion del evento

    [Description("Ocurre cuando se clicka sobre el boton MiBoton.")]

     

    //Asignamos al delegado el nombre del método que responderá al evento fuera del control de usuario

    public event ClickEventHandler Click_boton_interno;

     

    //Método que responde al evento de click dentro del control de usuario

    private void MiBoton_Click(object sender, EventArgs e)

    {

    //Comprueba que desde fuera del control se ha añadido el evento.

    if (Click_boton_interno != null)

    {

    Application.DoEvents();

    Click_boton_interno(this, e);

    }

    }