Archivo de la categoría '.NET'


Cómo firmar un documento PDF desde C# con iTextSharp

Jueves, 24 jun, 2010 @ 18:40 | Por Gustavo Cantero (The Wolf) | .NET Framework, Certificados Digitales, Seguridad

Muchas veces tenemos que firmar un PDF utilizando un certificado X.509, y el iTextSharp (una excelente librería) nos puede ayudar a realizar esta tarea. Para esto les dejo un método que utilizo para hacer esto, que seguramente les va a ser de utilidad.

Para poder utilizar este código deben bajarse la librería iTextSharp (http://sourceforge.net/projects/itextsharp), y referenciar esta DLL y “System.Security” desde su proyecto.

A continuación les dejo el código:

using System;
using System.Collections;
using System.IO;
using System.Security.Cryptography;
using System.Security.Cryptography.Pkcs;
using iTextSharp.text;
using iTextSharp.text.pdf;
using Org.BouncyCastle.X509;
using SysX509 = System.Security.Cryptography.X509Certificates;

/// <summary>
/// Helper para el firmado de PDFs con la librería iTextSharp
/// </summary>
public static class PDF
{
    /// <summary>
    /// Firma un documento
    /// </summary>
    /// <param name="Source">Documento origen</param>
    /// <param name="Target">Documento destino</param>
    /// <param name="Certificate">Certificado a utilizar</param>
    /// <param name="Reason">Razón de la firma</param>
    /// <param name="Location">Ubicación</param>
    /// <param name="AddVisibleSign">Establece si hay que agregar la firma visible al documento</param>
    public static void SignHashed(string Source, string Target, SysX509.X509Certificate2 Certificate, string Reason, string Location, bool AddVisibleSign)
    {
        X509CertificateParser objCP = new X509CertificateParser();
        X509Certificate[] objChain = new X509Certificate[] { objCP.ReadCertificate(Certificate.RawData) };

        PdfReader objReader = new PdfReader(Source);
        PdfStamper objStamper = PdfStamper.CreateSignature(objReader, new FileStream(Target, FileMode.Create), '\0');
        PdfSignatureAppearance objSA = objStamper.SignatureAppearance;

        if (AddVisibleSign)
            objSA.SetVisibleSignature(new Rectangle(100, 100, 300, 200), 1, null);

        objSA.SignDate = DateTime.Now;
        objSA.SetCrypto(null, objChain, null, null);
        objSA.Reason = Reason;
        objSA.Location = Location;
        objSA.Acro6Layers = true;
        objSA.Render = PdfSignatureAppearance.SignatureRender.NameAndDescription;
        PdfSignature objSignature = new PdfSignature(PdfName.ADOBE_PPKMS, PdfName.ADBE_PKCS7_SHA1);
        objSignature.Date = new PdfDate(objSA.SignDate);
        objSignature.Name = PdfPKCS7.GetSubjectFields(objChain[0]).GetField("CN");
        if (objSA.Reason != null)
            objSignature.Reason = objSA.Reason;
        if (objSA.Location != null)
            objSignature.Location = objSA.Location;
        objSA.CryptoDictionary = objSignature;
        int intCSize = 4000;
        Hashtable objTable = new Hashtable();
        objTable[PdfName.CONTENTS] = intCSize * 2 + 2;
        objSA.PreClose(objTable);

        HashAlgorithm objSHA1 = new SHA1CryptoServiceProvider();

        Stream objStream = objSA.RangeStream;
        int intRead = 0;
        byte[] bytBuffer = new byte[8192];
        while ((intRead = objStream.Read(bytBuffer, 0, 8192)) > 0)
            objSHA1.TransformBlock(bytBuffer, 0, intRead, bytBuffer, 0);
        objSHA1.TransformFinalBlock(bytBuffer, 0, 0);

        byte[] bytPK = SignMsg(objSHA1.Hash, Certificate, false);
        byte[] bytOut = new byte[intCSize];

        PdfDictionary objDict = new PdfDictionary();

        Array.Copy(bytPK, 0, bytOut, 0, bytPK.Length);

        objDict.Put(PdfName.CONTENTS, new PdfString(bytOut).SetHexWriting(true));
        objSA.Close(objDict);
    }

    /// <summary>
    /// Crea la firma CMS/PKCS #7
    /// </summary>
    private static byte[] SignMsg(byte[] Message, SysX509.X509Certificate2 SignerCertificate, bool Detached)
    {
        //Creamos el contenedor
        ContentInfo contentInfo = new ContentInfo(Message);

        //Instanciamos el objeto SignedCms con el contenedor
        SignedCms objSignedCms = new SignedCms(contentInfo, Detached);

        //Creamos el "firmante"
        CmsSigner objCmsSigner = new CmsSigner(SignerCertificate);

        // Include the following line if the top certificate in the
        // smartcard is not in the trusted list.
        objCmsSigner.IncludeOption = SysX509.X509IncludeOption.EndCertOnly;

        //  Sign the CMS/PKCS #7 message. The second argument is
        //  needed to ask for the pin.
        objSignedCms.ComputeSignature(objCmsSigner, false);

        //Encodeamos el mensaje CMS/PKCS #7
        return objSignedCms.Encode();
    }
}

Descargar proyecto de ejemploAquí les dejo un proyecto de ejemplo donde se pide un PDF a firmar, luego donde escribir PDF firmado y toma el primer certificado personal que posea clave privada y lo utiliza para firmar el PDF.

Espero que este código les sea de utilidad.
Suerte!

Artículos relacionados

VN:F [1.7.3_972]
Rating: 10.0/10 (1 voto cast)

Pósters de tecnologías y productos de Microsoft

Martes, 08 jun, 2010 @ 13:56 | Por Gustavo Cantero (The Wolf) | .NET Framework, Visual Studio

Visual Studio

Visual Studio 2010 Quick Reference Guidance
Guía de referencia rápida de Visual Studio 2010. También en español.

Visual Studio 2010 Keybinding Cards
Configuración de las teclas de Visual Studio 2010 para Visual Basic, Visual C#, Visual C++ y Visual F#.

Visual C# 2008 Keybinding Reference Poster
Configuración de las teclas de Visual Studio 2008 y Visual Studio Express para Visual C#.

Visual Basic 2008 Keybinding Reference Poster
Configuración de las teclas de Visual Studio 2008 y Visual Studio Express para Visual Basic.

Visual C++ 2008 Keybinding Reference Poster
Configuración de las teclas de Visual Studio 2008 y Visual Studio Express para Visual C++.

Visual C# 2005 Keyboard Shortcut Reference Poster
Configuración de las teclas de Visual Studio 2005 para Visual C#.

Visual Basic 2005 Keyboard Shortcut Reference Poster
Configuración de las teclas de Visual Studio 2005 para Visual Basic.

Visual C++ 2005 Keyboard Shortcut Reference Poster
Configuración de las teclas de Visual Studio 2005 para Visual C++.

.NET Framework

.NET Framework 4.0 Namespaces and Types Poster
Poster con las más importantes clases y Namespaces de .NET Framework 4.0.

.NET Framework 3.5 Common Namespaces and Types Poster
Poster con las clases y Namespaces más utilizados de .NET Framework 3.5.

.NET Framework 3.0 Namespaces and Types Poster
Poster con las clases y Namespaces más utilizados de .NET Framework 3.0.

.NET Framework 2.0 Commonly used Types and Namespaces
Poster con las clases y Namespaces más utilizados de .NET Framework 2.0.

Silverlight

Microsoft Silverlight 2 Developer Reference Poster
Poster de referencia de Silverlight 2.0

Microsoft Silverlight 1.1 Developer Reference Poster
Poster de referencia de Silverlight 1.1

BizTalk Server

BizTalk Server 2009 Runtime Architecture Poster
Detalle del flujo típico de mensajes y datos y referencias de BizTalk 2009.

BizTalk Server 2009 Capabilities Poster
Lista de las capacidades de BizTalk Server 2009.

BizTalk Server 2009 Scale-out Configurations Poster
Este póster describe las configuraciones típicas de las configuraciones y opciones de BizTalk Server 2009.

BizTalk Server 2009 Database Infrastructure Poster
Descripción de la base de datos, componentes asociados, trabajos, servicios, UI y eventos.

BizTalk Server 2009 BAM Poster
Este poster permite comprender los conceptos, procesos y gestión del BAM.

BizTalk ESB Toolkit Architecture Poster
Arquitectura del ESB Toolkit.

BizTalk Adapter Pack 2.0/WCF LOB Adapter SDK Poster
Este póster describe la funcionalidad, componentes, arquitectura y escenarios de uso/hosting de BizTalk Adapter Pack 2.0 y el WCF LOB Adapter SDK.

BizTalk Server 2006 R2 Runtime Architecture Poster
Detalle de los módulos y componentes de la arquitectura de BizTalk Server 2006 R2, junto con su flujo típico de mensajes y datos.

BizTalk Server 2006 R2 Scale-Out Configurations
Configuraciones y opciones de BizTalk Server 2006 R2.

BizTalk Server 2006 R2 Capabilities Poster
Lista de las capacidades de BizTalk Server 2006 R2.

BizTalk Server 2006 R2 Database Infrastructure Poster
Descripción de la base de datos, componentes asociados, trabajos, servicios, UI y eventos.

BAM Poster for Microsoft BizTalk Server 2006 R2
Este poster permite comprender los conceptos, procesos y gestión del BAM.

Exchange Server

Exchange Component Architecture Poster
Arquitectura y características de Microsoft Exchange Server 2007.

Windows Server

Windows Server 2008 R2 Feature Components Poster
Este póster proporciona una referencia visual de las tecnologías clave en Windows Server 2008 R2.

Windows Server 2008 R2: Hyper-V Component Architecture
Este póster proporciona una referencia visual para las tecnologías clave de comprensión en Windows Server 2008 R2 Hyper-V.

Windows Server 2008 Component Posters
Póster de componentes de Windows Server 2008, originalmente impreso en la edición de julio de 2007 de TechNet Magazine.

Windows Server 2008 Component Posters
Póster con los componentes de Windows Server 2008, originalmente impreso en el TechNet Magazine de Julio de 2007.

TechNet Magazine Active Directory Component Jigsaw Poster
Póster de Active Directory Component, originalmente impresor en el TechNet Magazine de marzo-abril de 2006.

Office

2007 Microsoft Office System Logical Architecture
Arquitectura de Office 2007.

Developer Map for the 2007 Microsoft® Office system
Mapa de desarrollo de Microsoft Office 2007,

Microsoft® Office InfoPath® 2007 Managed Object Model
Mapa con el modelo de objetos de Microsoft InfoPath 2007.

Developer Map for SharePoint® Products and Technologies
Mapa de desarrollador para SharePoint productos y tecnologías

P&P

PnP Overview Poster
Póster de introducción de patrones y prácticas

Smart Client Poster
Póster de patrones y prácticas de Smart Client

Microsoft CRM

Microsoft CRM 3.0 Logical Database Diagrams
Diagrama de la base de datos de Microsoft CRM 3.0.

Microsoft CRM 1.2 Logical Database Diagram
Diagrama de la base de datos de Microsoft CRM 1.2.

VN:F [1.7.3_972]
Rating: 0.0/10 (0 votos cast)

Parámetros opcionales en C# 4.0

Lunes, 24 may, 2010 @ 11:37 | Por Gustavo Cantero (The Wolf) | .NET Framework

Aquellos que trabajamos en Visual Basic y C# extrañamos algunas veces, al utilizar este segundo lenguaje, la posibilidad de tener parámetros opcionales que si nos permite VB. La buena noticia es que con la llegada de C# 4.0 ahora es posible hacerlo desde este lenguaje.
Veamos un ejemplo, supongamos que tenemos este método:

public void Prueba(string param1, int param2, bool param3) { }

Si queremos que se lo pueda llamar utilizando parámetros opcionales simplemente tenemos que definir los valores por defecto de cada uno, por ejemplo, de esta manera:

public void Prueba(string param1 = null, int param2 = 0, bool param3 = false) { }

Luego, al llamarlo se pueden obviar los parámetros que no queremos proporcionar, por ejemplo, en el siguiente ejemplo sólo le pasamos el valor del parámetro “param1”:

Prueba("cadena de ejemplo");

Pero esto no es todo, si quisiéramos pasar el valor del segundo parámetro sin especificar un valor para el primero lo podríamos hacer así:

Prueba(param2 : 200);

Y si quisiéramos pasar el segundo y tercero podríamos hacer esto:

Prueba(param2 : 200, param3 : true);

Como pueden ver su uso es muy similar a la forma de definir los valores de las propiedades de los atributos de las clases o métodos, aunque en ese caso se usa el igual (=) en lugar del signo “dos puntos” (:).
Espero que este pequeño “tip” les haya sido útil.
Suerte!

VN:F [1.7.3_972]
Rating: 3.5/10 (2 votos cast)

Enlace via código de grillas en ASP.NET

Lunes, 03 may, 2010 @ 14:28 | Por Dario Krapp | ASP.NET

Prólogo e inicios

En un artículo previo denominado Enlace de elementos en controles de lista en ASP.NET vimos las posibilidades que ASP.NET ofrecía para enlazar orígenes de datos a controles de lista y realizamos enlaces vía código y vía los elementos SqlDataSource y ObjectDataSource sin codificar ni una línea en los dos últimos casos.

En este artículo, daremos un próximo paso y no solo mostraremos información de un origen de datos en un control ASP.NET, sino que también veremos la forma de transferir los cambios realizados por el usuario sobre dichos datos nuevamente hacia el origen de datos. Para realizar estas tareas la funcionalidad proporcionada por los controles de lista no nos será suficiente, por lo que aprovecharemos la oportunidad para conocer uno de los controles más populares de ASP.NET, el control GridView el cual renderizará, como no debería sorprendernos, una grilla con datos.

Debido a que el control GridView es extremadamente flexible y ofrece una considerable variedad de posibilidades, como no queremos que el artículo se extienda demasiado, nos ocuparemos ver todos estos temas mencionados realizando el enlace vía código, ya que no hay tantos ejemplos (o al menos eso me parece), como los que pueden encontrarse contra enlaces a los objetos SqlDataSource y ObjectDataSource.

Para comenzar, dado que el artículo trata sobre enlace, diremos que en nuestro escenario contaremos con:

  • Un origen de datos (datos a enlazar).
  • Un control ASP.NET (GridView) destinado a enlazar y mostrar
    en la interfaz de usuario a los elementos del origen de datos.

Nuestro interés será en esta etapa simplemente enlazar el origen de datos al control GridView.
Para poder realizar esta tarea, el control GridView ofrecerá una propiedad denominada DataSource en la cual deberá establecerse el origen de datos y todo quedará casi listo, un detalle a considerar es que podrán enlazarse elementos que implementen algunas de las siguientes interfaces: ICollection, IEnumerable o IListSource.
Seguramente una duda razonable será como se renderizará el control al efectuarse el enlace. En el caso de enlazar un objeto que implemente ICollection generará una fila por cada elemento de la colección y una columna por cada propiedad pública que el elemento posea.

A continuación y a modo de ejemplo enlazaremos una lista de elementos personalizados a un control GridView, utilizaremos como origen de datos un elemento del tipo List

<asp:GridView runat="server" ID="GV1" />
public class MiElem
    {
        public string Valor1 { set; get; }
        public int Valor2 { set; get; }
        public DateTime Valor3 { set; get; }
        public bool Valor4 { set; get; }
        public string Valor5 { set; get; }
        public string Valor6 { set; get; }
    }

protected override void OnPreRender(EventArgs e)
{
    base.OnPreRender(e);
    BindGrid(GV1);
}

private void BindGrid(GridView Grd)
{
    List<MiElem> Lst = new List<MiElem>();
    Lst.Add(new MiElem()
    {
         Valor1 = "V1",
         Valor2 = 1,
         Valor3 = DateTime.Now.AddDays(1),
         Valor4 = true,
         Valor5 = "imagen1.gif",
         Valor6 = @"~\Imagenes\imagen1.gif"
     });
     Lst.Add(new MiElem()
     {
         Valor1 = "V2",
         Valor2 = 2,
         Valor3 = DateTime.Now.AddDays(2),
         Valor4 = true,
         Valor5 = "imagen1.gif",
         Valor6 = @"~\Imagenes\imagen1.gif"
     });
     Lst.Add(new MiElem()
     {
         Valor1 = "V3",
         Valor2 = 3,
         Valor3 = DateTime.Now.AddDays(3),
         Valor4 = true,
         Valor5 = "imagen1.gif",
         Valor6 = @"~\Imagenes\imagen1.gif"
     });
     Grd.DataSource = Lst;
     Grd.DataBind();
 }

Como puede verse:



El control GridView ha renderizando una grilla y ha generando, como anticipamos, una fila por cada elemento enlazado y una columna por cada propiedad pública del mismo.

Un detalle a mencionar es que el método DataBind() será quien realizará el traspaso de datos desde el origen de datos hacia el control, si el mismo es obviado los datos no serán transferidos y la grilla quedará vacía. Por otra parte cualquier modificación que se realice sobre la lista “Lst” posterior a la invocación al método DataBind() no será transferida al control y no será reflejada en el mismo.

Volviendo al ejemplo, cabe mencionar que lo que ha hecho el control es utilizar reflection para conocer las propiedades públicas de los elementos que componen la lista Lst (en realidad del primer elemento de la lista) y ha autogenerado una columna para cada una de ellas, además ha enlazado a cada elemento en una fila (ubicando el valor de cada propiedad en la columna correspondiente).

Luego de la impresión inicial, es claro que este comportamiento no nos será demasiado útil en la mayoría de los casos, al menos eso me parece, ya que posiblemente si deseamos enlazar un grupo de elementos, en muchos casos se deseará visualizar solo algunas de sus propiedades (públicas).

En un artículo previo vimos que los controles de lista poseían la propiedad DataTextField para indicar que propiedad (o campos en el caso de enlazar el control contra un DataSet, un DataTable o un DataReader) mostraría el control. Pero el caso del GridView, que como ya hemos visto, permitirá enlazar varias propiedades o campos en forma simultánea, el esquema será más complejo y más aún si consideramos otros factores como por ejemplo, el orden de las columnas. Es entonces esperable que exista una manera de definir cómo será la visualización de las propiedades a mostrar en las columnas de la grilla que considere todos estos factores.

VN:F [1.7.3_972]
Rating: 8.0/10 (3 votos cast)

Poster con las clases de .NET Framework 4.0

Martes, 27 abr, 2010 @ 15:00 | Por Gustavo Cantero (The Wolf) | .NET Framework

Este poster posee las clases del .NET Framework 4.0, diferenciando las que son de comunicación, workflow, cliente, identity, web, datos y del core. También diferencia cuales están en el client profile y cuales en Silverlight.
El poster en formato PNG lo pueden descargar pulsando sobre la imagen, y en PDF pulsando en este enlace.

VN:F [1.7.3_972]
Rating: 0.0/10 (0 votos cast)

Especificación de los lenguajes C# 4.0 y Visual Basic 10.0

Martes, 20 abr, 2010 @ 10:38 | Por Gustavo Cantero (The Wolf) | .NET Framework

Visual Studio
En el día de ayer Microsoft® liberó la especificación del lenguaje C# 4.0 (C# Language Specification 4.0) y la de Visual Basic 10.0 (Visual Basic Language Specification 10.0). En estos documentos tendremos la definición de las expresiones, clases base, estructuras, interfaces, estructura del léxico, Namespaces, delegados, etc., de la nueva versión de estos lenguajes.

Los documentos se pueden descargar en formato HTML o DOC de los siguiente enlaces:

Suerte!

VN:F [1.7.3_972]
Rating: 0.0/10 (0 votos cast)

Tamaño de los parámetros en string.Format

Sábado, 17 abr, 2010 @ 17:10 | Por Gustavo Cantero (The Wolf) | .NET Framework

Hay una característica en el método string.Format que es poco conocida, la cual nos permite establecer el tamaño mínimo de caracteres que ocupará el valor de los parámetros.
Pero mejor que explicarlo con palabras es mostrar un ejemplo: supongamos que queremos concatenar un número a un texto, pero éste debe estar alineado a la derecha, seguramente contaremos la cantidad de caracteres que ocupa, se lo restaremos al tamaño del espacio asignado para ese valor y le agregamos esa cantidad de espacios a su izquierda.
Otra forma, mucho más práctica, es utilizar el string.Format y pasarle el tamaño, por ejemplo:

string.Format("Precio:{0,10}", 1234);

Este código nos va a devolver la siguiente cadena:

Precio:      1234

Nótese que antes del número se agregaron 6 espacios, para así completar los 10 caracteres mínimos que especificamos en {0,10}.
Si en cambio en lugar de establecer un valor positivo le pasamos un valor negativo el texto se alineará a la izquierda, pero se seguirá llenando con espacios a su derecha hasta completar la cantidad de caracteres establecidos. Si este valor es menor al tamaño del parámetro simplemente se ignorará.
A continuación les muestro un ejemplo, el código siguiente define y guarda cadenas en tres variables:

string strTexto1 = string.Format("Texto 1: {0,12}!", "Scientia");
string strTexto2 = string.Format("Texto 2: {0,-12}!", "Scientia");
string strTexto3 = string.Format("Texto 3: {0,5}!", "Scientia");

Éstas variables, strTexto1, strTexto2 y strTexto3, valdrán los siguientes valores:

Texto 1:     Scientia!
Texto 2: Scientia    !
Texto 3: Scientia!

Espero que este tip les sea de utilidad.
Suerte!

VN:F [1.7.3_972]
Rating: 10.0/10 (1 voto cast)

101 ejemplos de Visual C# 2010 y Visual Basic 2010

Miércoles, 14 abr, 2010 @ 16:04 | Por Gustavo Cantero (The Wolf) | .NET Framework, Visual Studio

Visual Studio
Microsoft ha publicado un paquete gratuito con 101 ejemplos y documentación de C# 4.0 para usarse con Visual Studio 2010 RTM (el cual fue liberado antes de ayer). Los ejemplos incluyen proyectos que muestran el uso de Dynamics con Office, IronPython, Threading, Partial Types, seguridad, sobrecarga de operadores, código no manejado (Unsafe), delegados anónimos (Anonymous Delegates), métodos condicionales, interfaces explícitas, LINQ to Objects, LINQ to XML, QueryVisualizer, Reflection, DynamicQuery, XQuery y muchos más.

También publicaron otro paquete con otros 101 ejemplos y documentación pero en este caso de Visual Basic 10.0. Los ejemplos de este paquete incluyen ClickOnce, TCPRemoting, ejemplo para Tablet PC, WCF, WinForms, seguridad, LINQ, XQuery y muchos otros más.

Los enlaces para más información y descarga de los paquetes se los dejo a continuación:

Espero que esta información les sea de utilidad.
Suerte!

VN:F [1.7.3_972]
Rating: 9.6/10 (5 votos cast)

Enlace de elementos en controles de lista en ASP.NET

Miércoles, 24 mar, 2010 @ 23:43 | Por Dario Krapp | ASP.NET

Estoy seguro que será difícil encontrar en la actualidad algún programador que no se haya visto en la necesidad enviar información a la interfaz de usuario, y eventualmente manejar las modificaciones que un usuario haya realizado en la misma. La intención de este artículo es dar un primer paso a los conceptos de enlace en ASP.NET (una tecnología que nos permitirá resolver este tipo de problemas) para un escenario muy especifico pero frecuente que es cuando se requiere mostrar en un control ASP.NET un grupo de elementos como una lista. ASP.NET ofrecerá para este tipo de casos un grupo de controles (que llamaremos controles de lista para identificarlos de una forma sencilla) que nos permitirán realizar esta tarea en forma simple y clara.

En la década de los noventa, con ASP, lo que se acostumbraba hacer era identificar un control HTML adecuado para el grupo de datos que se deseaba mostrar y escribir los tags HTML intercalados junto con los datos para crear un fragmento de código HTML con la funcionalidad deseada, esta forma de trabajo, si bien funcionaba, daba como resultado un código poco claro y difícil de mantener.
ASP.NET y el enlace a datos permitirá obtener un resultado similar con un código de rápido desarrollo y de bajo costo de mantenimiento.

Antes que nada creo que lo más importante es definir quienes serán los actores que intervendrán en un escenario de enlace de datos y sin importar cuánto avancemos o donde comencemos, nos encontraremos con dos actores principales, el primero se estará compuesto por el grupo de datos con los que deseamos trabajar (al cual, para utilizar la nomenclatura habitual, llamaremos “Origen de datos” o “Data Source”) y el segundo por el elemento que permitirá que los mismos sean mostrados en la interfaz de usuario, y eventualmente sean modificados desde la misma, aunque esta opción quedará fuera del alcance de este artículo.
En el caso de ASP.NET ese elemento será un control y los controles que se ajustarán a las necesidades propuestas en este articulo serán los siguientes:

• BulletedList
• CheckBoxList
• DropDownList
• ListBox
• RadioButtonList

En WebControls y el control HtmlSelect en HtmlControls.

Aunque cada uno de ellos renderizará la lista de elementos a mostrar de una forma diferente, el enlace se efectuará de la misma forma para todos, por lo que en nuestros ejemplos optaremos por utilizar un control DropDownList.

Para comenzar, seremos sumamente triviales y supondremos que nuestro origen de datos es un grupo de elementos simples, por ejemplo, un grupo de cadenas de caracteres compuesta por los valores “I1”, “I2” e “I3” y nuestra intención será mostrar dichos valores en un control DropDownList.

El enlace

En nuestra primera suposición ya hemos conocido a ambos actores (el origen de datos compuesto por la cadena de caracteres “I1”, “I2” e “I3” y el control ASP.NET DropDownList), Deberemos finalmente realizar el enlace entre ambos.
Esta tarea será muy sencilla ya que los controles de lista contarán con la siguiente propiedad:

• DataSource

La propiedad DataSource representará al grupo de elementos (u origen de datos) que se enlazará al control, claro está que el grupo de elementos a enlazar no podrá ser tan arbitrario como deseemos ya que de alguna forma ASP.NET deberá saber de qué manera acceder a los elementos que componen dicho grupo, por lo que éste deberá implementar alguna de las siguientes interfaces: IEnumerable, ICollection o IListSource.
Sabemos ahora que para nuestro ejemplo, lo que llamábamos “grupo” deberá implementar alguna de las interfaces mencionadas.

Creo que de todas las comentadas, la interfaz ICollection nos será la más familiar y, para quien no la recuerde, sin usar mucho la imaginación, seguramente le será un sinónimo de Colecciones (o Collections). En base a esto podríamos reformular el problema para intentar enlazar una colección compuesta por las cadenas “I1”, “I2” e “I3” con un control DropDownList.

Los pasos para esta tarea constarán de crear una colección, de crear el control ASP.NET y establecer su propiedad DataSource, veamos un ejemplo concreto:

<asp:DropDownList runat="server" ID="DDL1"/>
List<string> Lst = new List<string>();
Lst.Add("I1");
Lst.Add("I2");
Lst.Add("I3");
DDL1.DataSource = Lst;

Si ejecutamos este código veremos que el enlace no funciona, esto se debe a que aún nos falta un detalle más, si modificamos el código de la siguiente forma:

List<string> Lst = new List<string>();
Lst.Add("I1");
Lst.Add("I2");
Lst.Add("I3");

DDL1.DataSource = Lst;
DDL1.DataBind();

Veremos que todo funciona según lo esperado, el método DataBind es el que ejecutará el pasaje de datos desde el origen de datos (Lst) hacia el control (DDL1). Es más si realizamos la siguiente prueba:

List<string> Lst = new List<string>();
Lst.Add("I1");
Lst.Add("I2");
Lst.Add("I3");

DDL1.DataSource = Lst;
DDL1.DataBind();

Lst.Add("I5");
Lst.Add("I6");

Notaremos que los valores “I5” e “I6” no serán transferidos al control.
En este primer ejemplo hemos realizado el enlace que nos habíamos propuesto y con muy poco esfuerzo.

Los controles de lista contarán también con la propiedad DataTextFormatString que permitirá establecer el formato de los datos a mostrar, para continuar con el ejemplo que ya habíamos comenzado, reemplazaremos la lista de cadenas de caracteres por una lista de fechas, el ejemplo es tan simple como antes, una opción es utilizar las siguientes líneas de código:

List<DateTime> Lst = new List<DateTime>();
Lst.Add(DateTime.Now);
Lst.Add(DateTime.Now.AddHours(1));
Lst.Add(DateTime.Now.AddHours(2));
DDL1.DataSource = Lst;
DDL1.DataBind();

donde al ejecutarlo veremos que en el DropDownList DDL1 las fechas se desplegarán como esperábamos, aunque para darle un poco de emoción al ejemplo y poner en practica lo que comentamos hace apenas unos parrafos, podríamos desear mostrar solamente la fecha pero no la hora en el formato dia/año/mes. De más está decir que una opción es modificar el código para realizar este ajuste programáticamente, pero no es la idea ya que la propiedad DataTextFormatString nos permitirá ahorrarnos las líneas de código.
A continuación se muestra el ejemplo:

<asp:DropDownList runat="server" ID="DDL1" DataTextFormatString="{0:dd/yyyy/MM}" />

Me parece que una pregunta que podríamos formularnos en este punto es ¿por qué el control está mostrando los valores adecuadamente?, si prestamos atención veremos que en ambos casos, tanto como cuando utilizamos una lista de cadenas de caracteres o una lista de fechas el control DropDownList ha podido mostrar el valor esperado sin que nosotros hayamos tenido que hacer ningún tipo de especificación, podríamos preguntarnos ¿qué pasará si utilizamos una lista de otro tipo de elementos?, por ejemplo un tipo de datos creado por nosotros mismos, como el siguiente:

public class MiElem
{
   public string Valor1 { set; get; }
   public int Valor2 { set; get; }
}

Si modificamos nuevamente el código previo de la siguiente forma

List<MiElem> Lst = new List<MiElem>();
Lst.Add(new MiElem() { Valor1 = "V1", Valor2 = 1 });
Lst.Add(new MiElem() { Valor1 = "V2", Valor2 = 2 });
Lst.Add(new MiElem() { Valor1 = "V3", Valor2 = 3 });

DDL1.DataSource = Lst;
DDL1.DataBind();

Notaremos la magia ya no funciona y que el DropDownList en este caso está mostrando por cada elemento un valor que no se encuentra en ninguna de sus propiedades. Lo que está haciendo el control es mostrar el valor de la representación de cadena de caracteres del elemento, o sea, simplemente está mostrando el resultado del método ToString() de cada elemento que carga. Si sobreescribimos el método ToString() de la clase MiElem nos convenceremos rápidamente de este hecho.
Ahora queda claro porque con cadenas de caracteres y fechas todo funcionaba sin problemas pero también está claro que con otros tipos de datos nos podríamos encontrar con complicaciones, ya que no siempre podremos ajustar el método ToString() a nuestra conveniencia. En este punto toma importancia la propiedad DataTextField de los controles de lista. DataTextField indicará el nombre de la propiedad o el campo (veremos más adelante a que nos referimos con campo) que el control mostrará. Si modificamos el código de la siguiente forma:

List<MiElem> Lst = new List<MiElem>();
Lst.Add(new MiElem() { Valor1 = "V1", Valor2 = 1 });
Lst.Add(new MiElem() { Valor1 = "V2", Valor2 = 2 });
Lst.Add(new MiElem() { Valor1 = "V3", Valor2 = 3 });

DDL1.DataSource = Lst;
DDL1.DataTextField = "Valor1";
DDL1.DataBind();

Notaremos que el control DDL1 mostrará el valor de la propiedad Valor1 de cada instantancia de MiElem.

De forma similar, la propiedad DataValueField indicará el nombre de la propiedad o el campo que el control utilizará como valor para cada ítem mostrado, ya que es frecuente el hecho de tener la necesidad de mostrar un dato, pero utilizar otro dato del elemento para realizar alguna tarea.
Si eventualmente se desea utilizar la misma propiedad para mostrarla en control tanto como para utilizar su valor, puede omitirse el uso de DataValueField, que es un detalle que permite mantener el código más simple.

Aunque en nuestro ejemplo hemos utilizado un control DropDownList enlazado a un objeto List podríamos haber utilizado cualquier otro control de lista (como mencionamos previamente), tanto como cualquier otra clase que implementase la interfaz ICollection.

Si volvemos a la propiedad DataSource, recordaremos que también pueden enlazarse objetos que implementan la interfaz IListSource, y podríamos preguntarnos ¿por qué?, la interfaz IListSource quizás no nos sea tan familiar como ICollection, pero es todo un indicio comentar que tanto la clase DataTable, como DataSet la implementan. Sabiendo esto quizás podríamos preguntarnos:
¿será entonces posible enlazar un DataTable o un DataSet a uno de estos controles?

La respuesta, como no podría ser de otra manera, es que si es posible, en tal caso en las propiedades DataTextField y DataValueField se deberá establecer el nombre de los campos a enlazar del DataTable, de esta forma el esquema de funcionamiento se mantendrá inalterable y el enlace podrá utilizarse para clases que implementen la interfaz ICollection tanto como IListSource. Por otra parte la interfaz IEnumerable (que era la tercera interfaz soportada por la propiedad DataSource) permitirá el enlace a objetos como por ejemplo un SqlDataReader donde los datos se van obteniendo de a uno a la vez. A continuación se muestra un ejemplo de enlace contra un DataSet, un DataTable y un SqlDataReader:

<asp:DropDownList
runat="server"
ID="DDL1"
DataTextField="Descripcion">
</asp:DropDownList>
<asp:DropDownList
runat="server"
ID="DDL2"
DataTextField="Descripcion">
</asp:DropDownList>
<asp:DropDownList
runat="server"
ID="DDL3"
DataTextField="Descripcion">
</asp:DropDownList>
private void DataReaderBinding()
{
        string cnstr = ...;
        SqlConnection SCon = new SqlConnection(cnstr);
        SqlCommand SCom = new SqlCommand("SELECT top 11 * FROM Datos1", SCon);
        SCom.Connection.Open();
        SqlDataReader SRead = SCom.ExecuteReader(System.Data.CommandBehavior.CloseConnection);
        DDL1.DataSource = SRead;
        DDL1.DataBind();
}

private void DataSetAndDataTableBinding()
{
        string cnstr = ...;
        SqlConnection SCon = new SqlConnection(cnstr);
        SqlCommand SCom = new SqlCommand("SELECT top 11 * FROM Datos1", SCon);

        SqlDataAdapter SDa = new SqlDataAdapter(SCom);
        DataSet DSet = new DataSet();
        SDa.Fill(DSet, "Datos1");

        DDL2.DataSource = DSet.Tables[0] ;

        DDL3.DataSource = DSet;
        DDL3.DataMember = "Datos1";  

        DataBind();
}

En el ejemplo puede observarse el uso de la propiedad DataMember para el caso del enlace contra un objeto DataSet, la propiedad DataMember deberá utilizarse cuando sea necesario indicar el nombre del DataTable que se enlazará al control. También en el ejemplo puede observarse el uso de un método existente en la página denominado DataBind() que ejecutará todos los bindeos necesarios automáticamente ahorrandonos un poco de código.

VN:F [1.7.3_972]
Rating: 9.0/10 (5 votos cast)

Utilizar Autenticación de ASP.NET desde Silverlight

Martes, 23 mar, 2010 @ 21:38 | Por Gustavo Cantero (The Wolf) | ASP.NET, Seguridad, Silverlight, WCF

Hace unos meses Darío escribió un artículo donde explica cómo utilizar los servicios de autenticación y roles que posee ASP.NET en nuestras páginas (http://www.programandoamedianoche.com/2009/10/autenticacion-por-formularios-en-asp-net), tanto desde el servidor como utilizando AJAX (http://www.programandoamedianoche.com/2009/10/autenticacion-por-formularios-en-asp-net/3). Para complementarlo se me ocurrió escribir un artículo que explique cómo utilizar este servicio de autenticación desde Silverlight.
Para comenzar vamos a crear un proyecto Silverlight en nuestro Visual Studio, junto con su proyecto web de prueba (el cual va a exponer los servicios de autenticación).
Para utilizar estos servicios desde Silverlight (o desde cualquier aplicación externa) necesitamos publicarlos a través de WCF (Windows Communication Foundation). Para esto primero necesitamos configurar el servicio en el web.config, en la sección “Services”, apuntando a la clase “System.Web.ApplicationServices.AuthenticationService”. Cabe mencionar que necesitamos establecer la compatibilidad del servicio con ASP.NET, ya que sólo es soportado con http.

<system.serviceModel>
  <services>
    <service name="System.Web.ApplicationServices.AuthenticationService"
             behaviorConfiguration="AuthenticationServiceTypeBehaviors">
      <endpoint contract="System.Web.ApplicationServices.AuthenticationService"
                binding="basicHttpBinding" bindingConfiguration="http"
                bindingNamespace="http://asp.net/ApplicationServices/v200"/>
    </service>
  </services>
  <bindings>
    <basicHttpBinding>
      <!--En producción es aconsejable utilizar https-->
      <binding name="http">
        <security mode="None"/>
      </binding>
    </basicHttpBinding>
  </bindings>
  <behaviors>
    <serviceBehaviors>
      <behavior name="AuthenticationServiceTypeBehaviors">
        <serviceMetadata httpGetEnabled="true"/>
      </behavior>
    </serviceBehaviors>
  </behaviors>
  <serviceHostingEnvironment aspNetCompatibilityEnabled="true"/>
</system.serviceModel>

Luego necesitamos habilitar los servicios web de autenticación a través de las siguientes líneas:

<system.web.extensions>
  <scripting>
    <webServices>
      <authenticationService enabled="true" requireSSL="false"/>
    </webServices>
  </scripting>
</system.web.extensions>

Y el último cambio que necesitamos hacer al web.config es para establecer la autenticación en “Forms”, utilizando:

<authentication mode="Forms" />

Bien, ahora que ya tenemos configurada nuestra aplicación debemos crear el archivo que exponga el servicio de autenticación. Para esto simplemente tenemos que agregar un archivo de tipo texto (“Text File”), para que el Visual Studio no le cree el code behind, y llamarlo, por ejempo, Authentication.svc. En este archivo vamos a agregar solamente una línea que apunte a la clase de ASP.NET que implementa el servicio de autenticación:

<%@ ServiceHost Service="System.Web.ApplicationServices.AuthenticationService" %>

Ahora ya tenemos una aplicación web que utiliza los servicios de autenticación de ASP.NET y los expone a través de servicios web, por lo tanto ahora, en el proyecto Silverlight, vamos a agregar la referencia al servicio creado en el paso anterior. Para esto pulsamos el botón derecho del mouse sobre el proyecto y elegimos la opción “Add Service Reference”, luego en el diálogo que se muestra pulsamos en “Discover” para que busque los servicios publicados en nuestra solución, elegimos “Authentication.svc” de la lista, cambiamos el namespace donde queremos que cree las clases (en nuestro ejemplo lo dejamos como está: “ServiceReference1”), y pulsamos “OK”. Esto nos creará las clases proxy para que nuestra aplicación Silverlight se conecte y utilice los servicios de autenticación expuestos por nuestra aplicación web.

Agregar servicio web de Autenticación

La clase cliente que creamos, llamada en nuestro ejemplo “ServiceReference1.AuthenticationServiceClient”, tiene tres métodos importantes: LoginAsync, LogoutAsync e IsLoggedInAsync. Nótese que todos los métodos de los servicios web de Silverlight utilizan “Async” como sufijo debido a que siempre son asincrónicos, o sea, cuando los llamamos no se detiene la ejecución de la aplicación hasta obtener el valor solicitado, sino que se ejecutan de forma paralela y luego se llama a un evento determinado al finalizar la consulta, en nuestro caso, LoginCompleted, LogoutCompleted y IsLoggedInCompleted respectivamente.
Para nuestro ejemplo vamos a crear una grilla en la página principal de nuestra aplicación donde agregaremos tres botones para realizar el login, logout y consultar si el usuario está logeado:

<Grid x:Name="LayoutRoot">
    <Grid.ColumnDefinitions>
        <ColumnDefinition />
        <ColumnDefinition />
    </Grid.ColumnDefinitions>
    <Grid.RowDefinitions>
        <RowDefinition/>
        <RowDefinition/>
        <RowDefinition/>
        <RowDefinition/>
        <RowDefinition/>
    </Grid.RowDefinitions>

    <StackPanel Orientation="Horizontal" Grid.ColumnSpan="2">
        <TextBlock Text="Usuario:" />
        <TextBox x:Name="txtUser" />
        <TextBlock Text="Contraseña:" />
        <PasswordBox x:Name="txtPassword" />
    </StackPanel>

    <Button x:Name="btnLogin" Content="Login" Grid.Row="1" Click="btnLogin_Click" />
    <Button x:Name="btnLogout" Content="Logout" Grid.Row="2" Click="btnLogout_Click" />
    <Button x:Name="btnIsLogedIn" Content="IsLogedIn" Grid.Row="3" Click="btnIsLogedIn_Click" />

    <TextBlock x:Name="lblLogin" Grid.Column="1" Grid.Row="1" />
    <TextBlock x:Name="lblLogout" Grid.Column="1" Grid.Row="2" />
    <TextBlock x:Name="lblIsLogedIn" Grid.Column="1" Grid.Row="3" />

    <TextBlock Text="Hora último resultado:" Grid.Row="4" />
    <TextBlock x:Name="lblTime" Grid.Column="1" Grid.Row="4" />
</Grid>

Y por último vamos a agregar el código que maneja los eventos de los botones del ejemplo:

private void btnLogin_Click(object sender, RoutedEventArgs e)
{
    lblLogin.Text = string.Empty;
    ServiceReference1.AuthenticationServiceClient objWSAuth = new ServiceReference1.AuthenticationServiceClient();
    objWSAuth.LoginCompleted += new EventHandler<ServiceReference1.LoginCompletedEventArgs>(objWSAuth_LoginCompleted);
    objWSAuth.LoginAsync(txtUser.Text, txtPassword.Password, string.Empty, true);
}

void objWSAuth_LoginCompleted(object sender, ServiceReference1.LoginCompletedEventArgs e)
{
    lblLogin.Text = e.Result.ToString();
    lblTime.Text = DateTime.Now.ToLongTimeString();
}

private void btnLogout_Click(object sender, RoutedEventArgs e)
{
    lblLogout.Text = string.Empty;
    ServiceReference1.AuthenticationServiceClient objWSAuth = new ServiceReference1.AuthenticationServiceClient();
    objWSAuth.LogoutCompleted += new EventHandler<AsyncCompletedEventArgs>(objWSAuth_LogoutCompleted);
    objWSAuth.LogoutAsync();
}

void objWSAuth_LogoutCompleted(object sender, AsyncCompletedEventArgs e)
{
    lblLogout.Text = "Listo";
    lblTime.Text = DateTime.Now.ToLongTimeString();
}

private void btnIsLogedIn_Click(object sender, RoutedEventArgs e)
{
    lblIsLogedIn.Text = string.Empty;
    ServiceReference1.AuthenticationServiceClient objWSAuth = new ServiceReference1.AuthenticationServiceClient();
    objWSAuth.IsLoggedInCompleted += new EventHandler<ServiceReference1.IsLoggedInCompletedEventArgs>(objWSAuth_IsLoggedInCompleted);
    objWSAuth.IsLoggedInAsync();
}

void objWSAuth_IsLoggedInCompleted(object sender, ServiceReference1.IsLoggedInCompletedEventArgs e)
{
    lblIsLogedIn.Text = e.Result.ToString();
    lblTime.Text = DateTime.Now.ToLongTimeString();
}

Para probar este ejemplo tenemos que tener configurada nuestra aplicación para que utilice un proveedor de membership, por ejemplo, SqlMembershipProvider, el cual se explica en el artículo de Dario (http://www.programandoamedianoche.com/2009/10/autenticacion-por-formularios-en-asp-net/2). Luego, a través de la herramienta de configuración de ASP.NET (ASP.NET Configuration) a la que se puede acceder a través del menú “Project” del Visual Studio, podemos crear usuarios de prueba ingresando en “Security” y luego “Create User”.

Crear usuario

Descargar proyecto de ejemplo para utilizar Autenticación de ASP.NET desde SilverlightComo siempre les dejo el proyecto para que lo prueben.
¡Suerte!

VN:F [1.7.3_972]
Rating: 9.2/10 (5 votos cast)

Betas de exámenes de certificación de .NET 4.0

Miércoles, 17 mar, 2010 @ 18:17 | Por Gustavo Cantero (The Wolf) | .NET, Certificaciones


El día de hoy Microsoft ha lanzado una invitación a los desarrolladores a dar GRATUITAMENTE los exámenes de certificación para .NET Framework 4.0 en su modalidad beta. Quienes aprueben alguno obtendrán la certificación de la misma manera que si fueran los exámenes finales. Los exámenes disponibles son:

  • Exam 71-511, TS: Windows Applications Development with Microsoft .NET Framework 4
  • Exam 71-515, TS: Web Applications Development with Microsoft .NET Framework 4
  • Exam 71-513: TS: Windows Communication Foundation Development with Microsoft .NET Framework 4
  • Exam 71-516: TS: Accessing Data with Microsoft .NET Framework 4
  • Exam 71-518: Pro: Designing and Developing Windows Applications Using Microsoft .NET Framework 4
  • Exam 71-519: Pro: Designing and Developing Web Applications Using Microsoft .NET Framework 4

Cabe mencionar que hay un cupo limitado utilizar los código de promoción para rendir gratuitamente estas certificaciones, por lo cual los insto a inscribirse lo antes posible para no quedar afuera.

Los códigos de promoción de cada examen y demás información lo pueden encontrar en el blog de Microsoft Learning: http://borntolearn.mslearn.net/btl/b/weblog/archive/2010/03/17/register-for-visual-studio-2010-beta-exams.aspx.

Es una oportunidad que no se puede perder.

Suerte a todos!

Agregado el 19/03/2010
En el día de hoy agregaron 150 lugares más para cada examen utilizando los mismos códigos, los cuales copio acá:

Examen Beta Code
71-511 511BC
71-515 515AA
71-513 513CD
71-516 516B1
71-518 518PE
71-519 519ZS

Suerte a todos!

VN:F [1.7.3_972]
Rating: 10.0/10 (2 votos cast)

Introducción a la programación con XNA 3.1 y C#

Jueves, 04 mar, 2010 @ 14:16 | Por Gustavo Cantero (The Wolf) | XNA

XNAXNA es un conjunto de herramientas y librerías de Microsoft que facilita el desarrollo de videojuegos para PC, XBOX y Zune, y cada vez está siendo utilizado más por estudiantes y principiantes para aprender a programar utilizando C# como lenguaje.

A Simple Introduction to Game Programming With C# and XNA 3.1 En el libro “A Simple Introduction to Game Programming With C# and XNA 3.1″ se explica como desarrollar videojuegos a aquellos que no tienen ningún conocimiento de programación, enfocado en los conceptos y fundamentos de XNA.

A quien le interese puede descargar gratuitamente de http://www.lulu.com/product/download/a-simple-introduction-to-game-programming-with-c%23-and-xna-31/5438606 o, si lo prefieren, pueden consultarlo y leerlo on-line en http://xnagamemaking.com. Si prefieren tener el libro “físico”, pueden comprarlo en http://www.lulu.com/content/7658212.

El centro de desarrollo de XNA pueden encontrarlo en http://msdn.microsoft.com/es-ar/aa937791.aspx y pueden descargar el XNA Game Studio 3.1, su documentación y otras herramientas de http://creators.xna.com/es-ar/downloads

VN:F [1.7.3_972]
Rating: 8.3/10 (3 votos cast)

Microsoft Ajax Minifier 4.0

Miércoles, 17 feb, 2010 @ 10:35 | Por Gustavo Cantero (The Wolf) | AJAX, ASP.NET, JavaScript

ASP.NETUna de las tareas que podemos realizar para reducir el tiempo de descarga inicial de nuestras aplicaciones es la eliminación de comentarios, espacios, puntos y coma y demás caracteres, y renombrar los objetos y variables utilizando nombres más cortos en nuestros archivos de JavaScript, pero realizar esta tarea manualmente esta pesada, repetitiva y propensa a errores, por lo tanto se utilizan herramientas que lo hacen automáticamente. Una de estas herramientas es el Microsoft Ajax Minifier, el cual, en su nueva versión 4.0 que salió ayer, también tiene soporte para archivos CSS, o sea, también elimina espacios, comentarios y caracteres innecesarios de los archivos de estilos.

El Microsoft Ajax Minifier 4.0 lo pueden descargar del siguiente enlace: http://aspnet.codeplex.com/releases/view/40584.

VN:F [1.7.3_972]
Rating: 5.0/10 (1 voto cast)

Traductor gratuito de recursos de .NET

Jueves, 14 ene, 2010 @ 02:25 | Por Gustavo Cantero (The Wolf) | .NET Framework

Scientia® Resource TranslatorEn Scientia® Soluciones Informáticas desarrollamos una aplicación sencilla pero muy útil utilizando los servicios de traducción que ofrece BingTM: creamos un traductor de recursos de .NET. La idea es muy sencilla, abre un archivo de recursos seleccionado por el usuario (un archivo .resx) y crea los archivos de recursos en los idiomas requeridos. Por ejemplo, podemos tomar un recurso con textos en español y crear los archivos con los textos en inglés, portugués, japonés y tailandés.

Cabe mencionar que este software lo creamos sin fines de lucro, o sea, para utilizarlo y distribuirlo gratuitamente.

A continuación les dejo una captura de la pantalla:

Ventana del Scientia® Resource Translator

A modo de prueba traducimos los textos de la misma aplicación, y ahora soporta más de 20 culturas, lo cual se puede visualizar en el menú de selección de idioma:

Menú de idiomas del Scientia® Resource Translator

Para quien desee utilizarlo o probarlo lo invito a descargarlo de la página de nuestra empresa: http://www.scientia.com.ar/descargas.aspx

Espero que esta herramienta les sea de utilidad.

VN:F [1.7.3_972]
Rating: 6.7/10 (3 votos cast)

Globalización y localización en Silverlight

Viernes, 08 ene, 2010 @ 16:30 | Por Gustavo Cantero (The Wolf) | Silverlight

Si uno programó con ASP.NET y comienza a desarrollar una aplicación en Silverlight en múltiples idiomas lo primero que se pregunta es “¿Cuál es la sintaxis de XAML equivalente al <%$ Resources:xx, yy %> de ASP.NET?”, y no es lindo cuando buscamos por internet y no encontramos ninguna respuesta.  Pues bien, en este artículo voy a mostrar cómo se puede hacer para poder poner textos localizables directamente en el XAML como si se lo estuviéramos asignando a un control ASP.NET en la definición del mismo.

Antes que nada, para aquellos que no están familiarizados con los términos del título de este artículo, voy a comentar a qué nos referimos: hablamos de “globalización” cuando nos referimos a las acciones relacionadas con el desarrollo de aplicaciones que se adaptan a culturas diferentes, en cambio la localización hace referencia a todos aquellos procesos de traducción de los distintos recursos para una cultura específica.

Bien, comencemos creando los textos en distintos idiomas, para esto necesitamos crear un archivo de recursos en nuestro proyecto Silverlight al que llamaremos “Textos.resx”.  Al crear el archivo el Visual Studio® nos abre el editor de recursos y veremos que se podemos cargar varios textos, pudiendo ingresar un nombre, valor (el texto en sí) y algún comentario opcional por cada uno.  El nombre es importante porque se utiliza para crear automáticamente una propiedad para acceder al valor del texto.  Veremos en el árbol del proyecto que además del archivo “Textos.resx” se crea un “Textos.Designer.cs” o (“Textos.Designer.vb” en caso de usar Visual Basic®), aquí el IDE crea una clase para la obtener programáticamente de forma fácil los valores de los textos.  Por ejemplo, supongamos que creamos un texto con el nombre “Boton1”, al guardar el recurso vamos a ver que existe una clase llamada “Textos” con algunas propiedades estáticas, entre las cuales está “Boton1”, la cual nos devolverá el valor del texto.

Un punto a tener en cuenta es la opción “Modificador de acceso” (AccessModifier) que se encuentra en la barra de herramientas del editor de recursos, cuyo valor por defecto es “internal”, lo que significa que la clase que se crea para acceder a los recursos va a ser del tipo “interno”, o sea, no se va a poder acceder desde afuera, pero para poder utilizar nuestros recursos directamente en el XAML necesitamos cambiar este valor a “public”.

AccessModifierResource

Ahora bien, supongamos que en este archivo de recursos estamos cargando los textos en español, pero queremos que nuestra aplicación Silverlight también pueda mostrarlos en inglés, entonces vamos a crear en el mismo lugar donde está nuestro archivo de recursos, otro llamado “Textos.en.resx”.  Nótese que este nuevo archivo antes de su extensión tiene la cadena “en”, esto significa que contendrá los textos en “inglés”.  Podemos crear cuantos recursos necesitemos utilizando esta nomenclatura para el idioma, pero .NET no sólo nos permite crear recursos por idioma, sino por cultura, por ejemplo, permitiéndonos crear recursos en inglés americano (llamando al archivo “Textos.en-US.resx”) o en inglés británico (“Textos.en-GB.resx”).  Debajo del artículo les copio una tabla con las distintas culturas soportadas por .NET Framework (para más información se puede ver la documentación de la clase CultureInfo en http://msdn.microsoft.com/es-ar/library/system.globalization.cultureinfo(VS.95).aspx).

Ahora que ya tenemos creados nuestros recursos en español e inglés, debemos modificar el archivo de proyecto para especificar los lenguajes que va a soportar nuestra aplicación.  Para hacer esto necesitamos editar el archivo .csproj, para lo cual pulsaremos el botón derecho de nuestro mouse sobre el proyecto en nuestro IDE y seleccionaremos la opción “Descargar proyecto” (Unload project), lo cual hará momentáneamente inaccesible los archivos de nuestro proyecto pero nos permitirá, pulsando nuevamente el botón derecho, seleccionar la opción “Editar …” (Edit …) para poder modificar el archivo en el editor del Visual Studio®.  Una vez abierto el archivo veremos que se trata de un XML, el cual contiene una etiqueta llamada “SupportedCultures”, la cual obviamente contiene las culturas soportadas por nuestra aplicación.  Aquí escribiremos todas las culturas que queremos que soporte nuestra aplicación, en nuestro ejemplo:

<SupportedCultures>es;en</SupportedCultures>

Cabe mencionar que las culturas deben ingresarse separadas por un punto y coma, inclusive las que son del mismo idioma, por ejemplo, si quisiéramos que nuestra aplicación soporte las culturas español, español de argentina e inglés, deberíamos ingresar:

<SupportedCultures>es;es-ar;en</SupportedCultures>

Luego de grabar el archivo volvemos a pulsar el botón derecho sobre el proyecto, elegimos la opción “Recargar proyecto” (Reload project).

Bien, hasta acá establecimos los posibles lenguajes, pero vamos a ver cómo utilizar los recursos directamente en el XAML.  La idea básica es crear el objeto que devuelve los textos como un recurso de la aplicación, pero no podemos usar el que creó automáticamente el Visual Studio® ya que, aunque las propiedades estáticas son públicas, su constructor es interno.  Una de las formas de solucionar esto es modificar la clase cambiando el “internal Textos() {}” por “public Textos() {}”, pero cada vez que modifiquemos algún texto el IDE nos va a volver a generar esta clase y vamos a necesitar cambiar nuevamente el constructor.  Para evitar esto podemos crear otra clase que simplemente cree este objeto (al cual va a tener acceso porque es interno) y lo devuelva a través de una propiedad pública, como muestro a continuación:

public class Recursos {
    private static Textos objTextos = new Textos();
    public Textos Textos {
        get { return objTextos; }
    }
}

Ahora podemos agregar nuestra clase como recurso de la aplicación, para lo cual tenemos que abrir el archivo “App.xaml” de nuestra y dentro de “Application.Resources” agregar nuestro objeto de recursos:

<Application.Resources>
<local:Recursos xmlns:local="clr-namespace:Globalizacion" x:Key="Recursos" />
</Application.Resources>

Nótese que el atributo “xmlns:local” define el namespace de nuestra clase, por lo tanto, si su clase Recursos está en uno distinto deberán ingresar el correcto.

Hecho esto ya podemos utilizar nuestros recursos en cualquier XAML de nuestra aplicación haciendo “binding” entre las propiedades de nuestros controles y la del objeto de recursos con el texto a mostrar.  Por ejemplo, a continuación muestro el código para establecerle a la propiedad “Text” de un TextBlock el valor del texto “Prueba” de nuestros recursos:

<TextBlock Text="{Binding Textos.Prueba, Source={StaticResource Recursos}}" />

La sintaxis, si bien no es igual a la de ASP.NET, nos permite establecer textos localizados directamente en nuestros XAML, sin necesidad de hacerlo programáticamente.

Ahora lo único que nos queda por hacer es establecer de alguna forma el idioma que queremos mostrar en nuestra aplicación.  Una forma sería cambiar el UICulture de nuestra aplicación programáticamente de la siguiente forma:

Thread.CurrentThread.CurrentUICulture = new CultureInfo("en-GB");

Pero si queremos automatizarlo podemos hacer que nuestra página ASP.NET le pase el idioma que está utilizando, cambiando el parámetro “uiculture” del objeto Silverlight en HTML.  Si queremos que además de nuestros textos la aplicación adapte los formatos de fecha, números, etc., también debemos establecer la cultura al parámetro “cultura”.  Por ejemplo, en el siguiente código HTML le establezco la cultura inglés a nuestra aplicación:

<object data="data:application/x-silverlight-2," type="application/x-silverlight-2" width="100%" height="100%">
    <param name="source" value="ClientBin/Globalizacion.xap" />
    <param name="onError" value="onSilverlightError" />
    <param name="background" value="white" />
    <param name="minRuntimeVersion" value="3.0.40624.0" />
    <param name="autoUpgrade" value="true" />
    <param name="uiculture" value="en" />
    <param name="culture" value="en" />
    <a href="http://go.microsoft.com/fwlink/?LinkID=149156&v=3.0.40624.0" style="text-decoration: none">
        <img src="http://go.microsoft.com/fwlink/?LinkId=108181" alt="Get Microsoft Silverlight"
            style="border-style: none" />
    </a>
</object>

Como hablamos antes de “automatizar” la cultura, podemos hacer que el ASP.NET elija automáticamente la cultura a mostrar según la configuración del navegador del cliente estableciendo el valor “auto” a las propiedades “uiculture” y “culture” en la etiqueta “globalization” de nuestro web.config:

<globalization uiCulture="auto" culture="auto"/>

Si “globalization” no existe en el web.config habrá que crearlo dentro de “system.web”.

Por último, debemos pasarle al Silverlight la cultura seleccionada por el ASP.NET:

<object data="data:application/x-silverlight-2," type="application/x-silverlight-2" width="100%" height="100%">
    <param name="source" value="ClientBin/Globalizacion.xap" />
    <param name="onError" value="onSilverlightError" />
    <param name="background" value="white" />
    <param name="minRuntimeVersion" value="3.0.40624.0" />
    <param name="autoUpgrade" value="true" />
    <param name="uiculture" value="<%=System.Threading.Thread.CurrentThread.CurrentUICulture%>" />
    <param name="culture" value="<%=System.Threading.Thread.CurrentThread.CurrentCulture%>" />
    <a href="http://go.microsoft.com/fwlink/?LinkID=149156&v=3.0.40624.0" style="text-decoration: none">
        <img src="http://go.microsoft.com/fwlink/?LinkId=108181" alt="Get Microsoft Silverlight"
            style="border-style: none" />
    </a>
</object>

Y listo, nuestra aplicación ya se adapta a la configuración del navegador del usuario y cambia sus textos y formatos.

Aquí les dejo el proyecto realizado con Visual Studio® 2008 para bajar y las capturas de la página donde se ve cómo cambia la cultura entre español, inglés y español de Argentina (qué es la configuración de mi PC) al utilizar la selección automática (nótese la diferencia en el formato de hora con la cultura “español”).

Cultura en español

Cultura en inglés

Cultura en auto

Espero que el artículo les haya sido de utilidad y como siempre los insto a dejar sus consultas, dudas o sugerencias.

Culturas soportadas por .NET

Cultura Referencia
“” (cadena vacía) Referencia cultural de todos los idiomas
af Afrikaans
af-ZA Afrikaans (Sudáfrica)
sq Albanés
sq-AL Albanés (Albania)
ar Árabe
ar-DZ Árabe (Argelia)
ar-BH Árabe (Bahréin)
ar-EG Árabe (Egipto)
ar-IQ Árabe (Iraq)
ar-JO Árabe (Jordania)
ar-KW Árabe (Kuwait)
ar-LB Árabe (Líbano)
ar-LY Árabe (Libia)
ar-MA Árabe (Marruecos)
ar-OM Árabe (Omán)
ar-QA Árabe (Qatar)
ar-SA Árabe (Arabia Saudí)
ar-SY Árabe (Siria)
ar-TN Árabe (Túnez)
ar-AE Árabe (Emiratos Árabes Unidos)
ar-YE Árabe (Yemen)
hy Armenio
hy-AM Armenio (Armenia)
az Azerí
az-Cyrl-AZ Azerí (Azerbaiyán, Cirílico)
az-Latn-AZ Azerí (Azerbaiyán, Latín)
eu Vasco
eu-ES Vasco (España)
be Bielorruso
be-BY Bielorruso (Belarús)
bg Búlgaro
bg-BG Búlgaro (Bulgaria)
ca Catalán
ca-ES Catalán (España)
zh-HK Chino (Hong Kong RAE, RPC)
zh-MO Chino (Macao RAE)
zh-CN Chino (RPC)
zh-Hans Chino (simplificado)
zh-SG Chino (Singapur)
zh-TW Chino (Taiwán)
zh-Hant Chino (tradicional)
hr Croata
hr-BA Croata (Bosnia-Herzegovina)
hr-HR Croata (Croacia)
cs Checo
cs-CZ Checo (República Checa)
da Danés
da-DK Danés (Dinamarca)
dv Divehi
dv-MV Divehi (Maldivas)
nl Holandés
nl-BE Neerlandés (Bélgica)
nl-NL Neerlandés (Países Bajos)
en Inglés
en-AU Inglés (Australia)
en-BZ Inglés (Belice)
en-CA Inglés (Canadá)
en-029 Inglés (Caribe)
en-IE Inglés (Irlanda)
en-JM Inglés (Jamaica)
en-NZ Inglés (Nueva Zelanda)
en-PH Inglés (Filipinas)
en-ZA Inglés (Sudáfrica)
en-TT Inglés (Trinidad y Tobago)
en-GB Inglés (Reino Unido)
en-US Inglés (Estados Unidos)
en-ZW Inglés (Zimbabue)
et Estonio
et-EE Estonio (Estonia)
fo Feroés
fo-FO Feroés (Islas Feroe)
fa Persa
fa-IR Farsi (Irán)
fi Finlandés
fi-FI Finés (Finlandia)
fr Francés
fr-BE Francés (Bélgica)
fr-CA Francés (Canadá)
fr-FR Francés (Francia)
fr-LU Francés (Luxemburgo)
fr-MC Francés (Mónaco)
fr-CH Francés (Suiza)
gl Gallego
gl-ES Gallego (España)
ka Georgiano
ka-GE Georgiano (Georgia)
de Alemán
de-AT Alemán (Austria)
de-DE Alemán (Alemania)
de-DE_phoneb Alemán (Alemania, ordenación de la libreta de teléfonos)
de-LI Alemán (Liechtenstein)
de-LU Alemán (Luxemburgo)
de-CH Alemán (Suiza)
el Griego
el-GR Griego (Grecia)
gu Gujarati
gu-IN Gujarati (India)
he Hebreo
he-IL Hebreo (Israel)
hi Hindi
hi-IN Hindi (India)
hu Húngaro
hu-HU Húngaro (Hungría)
es Islandés
is-IS Islandés (Islandia)
id Indonesio
id-ID Indonesio (Indonesia)
it Italiano
it-IT Italiano (Italia)
it-CH Italiano (Suiza)
ja Japonés
ja-JP Japonés (Japón)
kn Kannada
kn-IN Kannada (India)
kk Kazajo
kk-KZ Kazajo (Kazajistán)
kok Konkani
kok-IN Konkani (India)
ko Coreano
ko-KR Coreano (Corea)
ky Kirguís
ky-KG Kirguís (Kirguistán)
lv Letón
lv-LV Letón (Letonia)
lt Lituano
lt-LT Lituano (Lituania)
mk Macedonio
mk-MK Macedonio (Macedonia, Ex-República Yugoslava de Macedonia)
ms Malayo
ms-BN Malayo (Estado de Brunéi Darussalam)
ms-MY Malayo (Malasia)
mr Marathi
mr-IN Marathi (India)
mn Mongol
mn-MN Mongol (Mongolia)
no Noruego
nb-NO Noruego (Bokmål, Noruega)
nn-NO Noruego (Nynorsk, Noruega)
pl Polaco
pl-PL Polaco (Polonia)
pt Portugués
pt-BR Portugués (Brasil)
pt-PT Portugués (Portugal)
pa Punjabi
pa-IN Punjabi (India)
ro Rumano
ro-RO Rumano (Rumanía)
ru Ruso
ru-RU Ruso (Rusia)
sa Sánscrito
sa-IN Sánscrito (India)
sr-Cyrl-CS Serbio cirílico (Serbia y Montenegro)
sr-Latn-CS Serbio latino (Serbia y Montenegro)
sk Eslovaco
sk-SK Eslovaco (Eslovaquia)
sl Esloveno
sl-SI Esloveno (Eslovenia)
es Español
es-AR Español (Argentina)
es-BO Español (Bolivia)
es-CL Español (Chile)
es-CO Español (Colombia)
es-CR Español (Costa Rica)
es-DO Español (República Dominicana)
es-EC Español (Ecuador)
es-SV Español (El Salvador)
es-GT Español (Guatemala)
es-HN Español (Honduras)
es-MX Español (México)
es-NI Español (Nicaragua)
es-PA Español (Panamá)
es-PY Español (Paraguay)
es-PE Español (Perú)
es-PR Español (Puerto Rico)
es-ES Español (España)
es-ES_tradnl Español (España – alfabetización tradicional)
es-UY Español (Uruguay)
es-VE Español (Venezuela)
sw Suajili
sw-KE Swahili (Kenia)
sv Sueco
sv-FI Sueco (Finlandia)
sv-SE Sueco (Suecia)
syr Sirio
syr-SY Sirio (Siria)
ta Tamil
ta-IN Tamil (India)
tt Tatar
tt-RU Tatar (Rusia)
te Telugu
te-IN Telugu (India)
th Tailandés
th-TH Tailandés (Tailandia)
tr Turco
tr-TR Turco (Turquía)
uk Ucraniano
uk-UA Ucraniano (Ucrania)
ur Urdú
ur-PK Urdú (Pakistán)
uz Uzbeko
uz-Cyrl-UZ Uzbeko (Uzbekistán, Cirílico)
uz-Latn-UZ Uzbeko (Uzbekistán, Latino)
vi Vietnamita
vi-VN Vietnamita (Vietnam)
VN:F [1.7.3_972]
Rating: 10.0/10 (1 voto cast)

Imprimir con Silverlight 4 beta

Martes, 01 dic, 2009 @ 00:37 | Por Gustavo Cantero (The Wolf) | Silverlight

Una de las características más votadas en el sitio Microsoft® Connect por los desarrolladores que utilizamos Silverlight es la posibilidad de imprimir. Esto ahora es posible utilizando Silverlight 4 beta y la nueva clase PrintDocument del espacio de nombres System.Windows.Printing.

Primero vamos a hacer una introducción rápida sobre como imprimir desde Silverlight. La clase PrintDocument básicamente envía a la impresora el contenido de cualquier objeto UIElement, el cual debe ser establecido a través de uno de los parámetros del evento PrintPage que se dispara luego de llamar al método Print. Nótese que al tratarse de objetos UIElement se puede imprimir cualquier elemento gráfico que estemos utilizando en nuestras aplicaciones, por ejemplo, Image, TextBlock o incluso grillas complejas con elementos variados o con más grillas en su interior.

Supongamos que queremos imprimir, desde un botón puesto en el XAML, la pantalla de la aplicación (sería algo así como una captura de la aplicación), para esto podríamos poner el siguiente código en el XAML:

<UserControl x:Class="Print.MainPage"
    xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
    xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
    xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
    xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
    mc:Ignorable="d"
    d:DesignHeight="300" d:DesignWidth="400">

    <StackPanel Orientation="Vertical" HorizontalAlignment="Center" VerticalAlignment="Center">
        <TextBlock Text="Texto de prueba" Margin="10" FontSize="30">
            <TextBlock.Effect>
                <DropShadowEffect />
            </TextBlock.Effect>
        </TextBlock>
        <Button Content="Imprimir" Click="Button_Click" />
    </StackPanel>
</UserControl>

Y este otro en el código:

using System.Windows;
using System.Windows.Controls;
using System.Windows.Printing;

namespace Print
{
    public partial class MainPage : UserControl
    {
        public MainPage()
        {
            InitializeComponent();
        }

        private void Button_Click(object sender, RoutedEventArgs e)
        {
            PrintDocument objDoc = new PrintDocument();
            objDoc.PrintPage += (s, args) =>
            {
                args.PageVisual = this;
            };
            objDoc.Print();
         }
    }
}

En el código de C# podemos ver tres pasos sencillos realizados para imprimir la página Silverlight completa: creamos el objeto PrintDocument, creamos un delegado anónimo para el evento PrintDocument que establezca la propiedad PageVisual (aquí es donde hay que establecer el elemento a imprimir) y por último llamamos al método Print. Al llamar a este método el sistema operativo nos muestra el diálogo de impresión.

Dialogo impresión

Para probar la impresión yo lo envié a la impresora virtual llamada “Microsoft XPS Document Writer”, para que me cree un archivo XPS y poder verlo luego desde mi navegador:

Impresión XPS

Bien, como ejemplo sirve, pero cómo hacemos para imprimir otra cosa que no sea la aplicación o para imprimir varias páginas? Eso lo voy a explicar a continuación.

La clase PrintDocument en realidad posee tres eventos: StartPrint, PrintDocument y EndPrint, los cuales se ejecutan antes, durante y después de realizar la impresión respectivamente.

El primer evento, StartPrint, se dispara luego de llamar al método Print y de que el usuario cliqueado “Imprimir” en el diálogo de impresión. Este evento es utilizado para configurar lo necesario antes de la impresión, por ejemplo, acomodar controles, ocultar elementos, etc.

El siguiente evento a ejecutar es PrintDocument, al cual se le pasa como parámetro un objeto del tipo PrintPageEventArgs. Este objeto posee tres propiedades interesantes:

  • PageVisual: en esta propiedad debemos establecer el objeto del tipo UIElement que queremos que se imprima en la página actual, por ejemplo, una grilla con información, una imagen, etc. Este elemento a imprimir no necesariamente debe estar en el XAML, podemos crearlo programáticamente y enviarlo luego a imprimir.
  • PrintableArea: aquí nos pasa el tamaño del área imprimible que disponemos. Cabe mencionar que este valor variará dependiendo de la impresora seleccionada por el usuario y del tamaño del papel elegido.
  • HasMorePages: en esta propiedad podemos establecer si luego de la página que estamos enviando a imprimir hay más páginas. En caso de que establezcamos como true el valor de esta propiedad, luego de finalizado el delegado actual, se disparará nuevamente el evento PrintDocument para imprimir las siguientes páginas.

Por último nos queda el evento EndPrint, donde podemos realizar cualquier acción que necesitemos hacer luego de finalizada (o enviada al spooler) la impresión. Este evento recibe, como uno de sus parámetros, un objeto del tipo EndPrintEventArgs, el cual tiene una propiedad llamada Error que es donde el Framework nos devuelve cualquier excepción generada al momento de la impresión. Si en el ejemplo anterior ante un error quisiéramos mostrarle al usuario el mensaje de la excepción, deberíamos modificar el código del evento Click del botón por el siguiente:

PrintDocument objDoc = new PrintDocument();
objDoc.PrintPage += (s, args) =>
{
    args.PageVisual = this;
};
objDoc.EndPrint += (s, args) =>
{
    if (args.Error != null)
        MessageBox.Show(args.Error.Message, "ERROR", MessageBoxButton.OK);
};
objDoc.Print();

Regresando a la clase PrintDocument, ésta posee también (además del método Print que ya vimos) una propiedad llamada DocumentName, donde podemos establecer el nombre de nuestro documento, el cual se verá en la cola de la impresora.

Por último les dejo el proyecto de ejemplo realizado con Visual Studio® 2010 beta 2.

VN:F [1.7.3_972]
Rating: 10.0/10 (4 votos cast)

Silverlight 4 Beta

Miércoles, 18 nov, 2009 @ 16:44 | Por Gustavo Cantero (The Wolf) | Silverlight

Microsoft® Silverlight
En el día de hoy, 18 de noviembre de 2009, en Los Angeles en el Professional Developer’s Conference 2009 (conocido como PDC09), Microsoft anuncia la disponibilidad de la beta de Silverlight® 4, justo 8 meses después de que en el MIX09 se presentara la beta de Silverlight® 3 (el 18 de marzo).

Entre las nuevas características que nos trae esta nueva versión encontramos:

  • Soporte para impresión, incluyendo una vista previa del documento
  • Más de 60 controles personalizables
  • Mejoras en la localización de los textos
  • El CLR ahora permite ejecutar el mismo código compilado en el escritorio como en Silverlight sin cambios
  • Soporte para la utilización de webcams y micrófono
  • Deep Zoom ahora utiliza la aceleración por hardware
  • Multicast networking
  • Soporte para Google® Chrome
  • Soporte Multi-touch
  • Eventos de botón derecho del mouse
  • Acceso al Portapapeles
  • Se podrán utilizar en el escritorio como “trusted applications”, permitiendo acceder a documentos, aplicaciones como Outlook® (por ejemplo, para enviar mails), a dispositivos utilizando COM automation, etc.
  • Y muchas novedades más…. (para más detalles pueden visitar el sitio de Silverlight)

Para probarlo sólo necesitan tener la beta de Visual Studio® 2010 y el SDK de Silverlight® 4.

Cabe mencionar que en el día de hoy también salió el nuevo release del Silverlight® Toolkit, el cual ahora también soporta la beta de Silverlight 4.

Esta noticia seguramente va a alegrar a varios de los desarrolladores que ya utilizamos Silverlight® en nuestras aplicaciones.

Enlaces

VN:F [1.7.3_972]
Rating: 0.0/10 (0 votos cast)

Cómo crear una clase dinámicamente y ejecutarla

Sábado, 14 nov, 2009 @ 19:13 | Por Gustavo Cantero (The Wolf) | .NET Framework

Revisando los newsgroups de Microsoft encontré una persona que preguntaba cómo hacer para “ejecutar un string”, o sea, escribir una fórmula matemática en una cadena de texto y luego obtener el resultado de la misma. En ese momento recordé que hace unos años tuve que hacer esto mismo utilizando .NET Framework 1.1, fue entonces cuando me decidí a buscar aquel código y escribir este artículo.
Para hacer esto necesitamos crear una clase, compilarla en memoria, luego instanciarla y por último ejecutar el método que devuelva el resultado en cuestión.

La clase la debemos crear en un string incluyendo los using necesarios, el namespace a utilizar y el método a ejecutar, el cual va a resolver la fórmula. Al compilador debemos pasarle varios parámetros a través de la clase CompilerParameters, donde vamos a indicarle que debe generar el ensamblado en memoria, que no debe generar un ejecutable y que no incluya (o si, depende de la necesidad) la información para debug.

CompilerParameters Parametros = new CompilerParameters()
{
    GenerateInMemory = true,
    GenerateExecutable = false,
    IncludeDebugInformation = false
};

Luego debemos crear el compilador con el método estático CreateProvider de la clase CodeDomProvider, al cual debemos pasarle el lenguaje que queremos utilizar, en nuestro ejemplo “CSharp”. Hay que tener en cuenta que el nombre del lenguaje es “case sensitive”, por lo que hay que tener cuidado de escribirlo con las mayúsculas y minúsculas correspondientes.

CodeDomProvider objCompiler = CodeDomProvider.CreateProvider("CSharp");

Una vez creado el compilador debemos pasarle la clase y los parámetros creados anteriormente para que genere el ensamblado necesario en memoria:

CompilerResults objResultados = objCompiler.CompileAssemblyFromSource(objParametros, strClase);

Por último debemos crear una instancia de la clase y llamar al método que creamos y que va a calcular la fórmula:

object objClase = objResultados.CompiledAssembly.CreateInstance("MiNamespace.MiClase", false, BindingFlags.CreateInstance, null, null, null, null);
return objClase.GetType().InvokeMember("MiMetodo", BindingFlags.InvokeMethod, null, objClase, null);

El método CreateInstance posee varios parámetros para globalización, parámetros para pasarle al método, etc., pero para nuestro ejemplo, al no necesitarlos, los vamos a establecer en null.

Una vez explicado lo que necesitamos hacer, les paso el código del método a ejecutar para que resuelva fórmulas o cualquier línea de C# (por ejemplo, búsquedas de cadenas de texto, etc.) y devuelva el valor:

namespace WebApplication1
{
    using System.CodeDom.Compiler;
    using System.Reflection;

    public static class Formula
    {
        /// <summary>
        /// Resuelve el valor de una fórmula
        /// </summary>
        /// <param name="Formula">Fórmula a resolver</param>
        /// <returns>Resultado</returns>
        /// <example>double Resultado = Formula.Resolver("2 * 8 + 3");</example>
        public static object Resolver(string Formula)
        {
            //Parámetros del compilador
            CompilerParameters objParametros = new CompilerParameters()
            {
                GenerateInMemory = true,
                GenerateExecutable = false,
                IncludeDebugInformation = false
            };

            //Clase
            string strClase =
                "using System;" +
                "namespace Scientia {" +
                "public class Formula {" +
                    "public object Ejecutar() {" +
                        "return " + Formula +
                ";}}}";

            //Compilo todo y ejecuto el método
            CodeDomProvider objCompiler = CodeDomProvider.CreateProvider("CSharp");

            //En .NET 1.1 usaba esta linea:
            //ICodeCompiler ICC = (new CSharpCodeProvider()).CreateCompiler();

            CompilerResults objResultados = objCompiler.CompileAssemblyFromSource(objParametros, strClase);
            object objClase = objResultados.CompiledAssembly.CreateInstance("Scientia.Formula",
                false, BindingFlags.CreateInstance, null, null, null, null);
            return objClase.GetType().InvokeMember("Ejecutar", BindingFlags.InvokeMethod, null, objClase, null);
        }
    }
}

Como ejemplo podemos crear una página donde el usuario pueda ingresar una fórmula y al pulsar en un botón se muestre el resultado en la misma. La página debería quedar así:

<%@ Page Language="C#" AutoEventWireup="true" CodeBehind="Default.aspx.cs" Inherits="WebApplication1._Default" %>

<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
<html xmlns="http://www.w3.org/1999/xhtml">
<head runat="server">
    <title>Ejemplo de Scientia® Soluciones Informáticas</title>
</head>
<body>
    <form id="form1" runat="server">
    <asp:Panel DefaultButton="btnCalcular" runat="server">
        <asp:TextBox ID="txtFormula" runat="server" />
        <asp:Button ID="btnCalcular" runat="server" Text="=" OnClick="btnCalcular_Click" />
        <asp:Label ID="lblResultado" runat="server" />
    </asp:Panel>
    </form>
</body>
</html>

Y en el code behind de ésta debería tener lo siguiente:

using System;

namespace WebApplication1
{
    public partial class _Default : System.Web.UI.Page
    {
        protected void Page_Load(object sender, EventArgs e)
        {
        }

        protected void btnCalcular_Click(object sender, EventArgs e)
        {
            lblResultado.Text = Formula.Resolver(txtFormula.Text).ToString();
        }
    }
}

Espero que este artículo les sea de utilidad y, como siempre, les dejo el proyecto de ejemplo para Visual Studio® 2008.Descargar proyecto de ejemplo

VN:F [1.7.3_972]
Rating: 10.0/10 (1 voto cast)

Versión final de Bing Maps Platform

Martes, 10 nov, 2009 @ 12:28 | Por Gustavo Cantero (The Wolf) | GIS, Silverlight

bing
Para aquellos que, como nosotros, utilizan la versión CTP del control de Silverlight para Virtual Earth les comento que este control funcionará hasta el 31 de diciembre de 2009 inclusive. Para poder seguir utilizando los servicios de Bing Microsoft acaba de lanzar (ayer) la versión final de Bing Maps Platform 1.0, el cual incluye la versión final del control para usarse desde Silverlight.

Para conocer más de la nueva versión del control o para conocer cómo utilizar los servicios del control de Silverlight para bing pueden leer el Getting Started Using the Silverlight Map Control.

También pueden ver el video del anuncio de esta nueva versión en Microsoft Bing Maps for Enterprise.

VN:F [1.7.3_972]
Rating: 0.0/10 (0 votos cast)

Autenticación por formularios en ASP.NET

Lunes, 19 oct, 2009 @ 10:59 | Por Dario Krapp | AJAX, ASP.NET, Seguridad

Prólogo e inicios

En algunas tecnologías web antiguas, como por ejemplo ASP (en el caso de Microsoft) la programación de la autenticación de los usuarios quedaba en manos de los programadores. A pesar de que no es una tarea extremadamente compleja, demandaba tiempo de desarrollo, pruebas y correcciones por cada sitio que se desarrollaba y aunque las técnicas utilizadas para esta funcionalidad se encontraban informalmente estandarizadas, siempre podían existir diferencias en las implementaciones (quien haya caído en la suerte de tener que modificar una aplicación legada, posiblemente sabrá a que nos referimos ), lo que influía en los tiempos y costos del desarrollo y si lo analizamos parecería tener poco sentido el gasto de esfuerzos de desarrollo en una tarea que es tan repetitiva. Quizás esa sea una de las causas por las que ASP.NET provee un sistema de autenticación ya incluido, lo que implica código, desarrollado, testeado, mantenido y listo para usar. En este artículo nos adentraremos en la autenticación por formularios desde sus comienzos en las primeras versiones de ASP.NET hasta la versión 3.5 (y diría que también hasta la versión 4.0, ya que hasta el momento no se han introducido modificaciones sobre estos ítems en la versión 4.0 y según parece, tampoco hay intenciones de hacerlo) y comentaremos algunos otros temas relacionados.

Para empezar diremos que la autenticación es el acto de confirmar que algo o alguien es quien dice ser y autorización es el acto de dar permisos (o denegárselos) a algo o alguien (ya autenticado) sobre un recurso, diremos además que algo o alguien es anónimo si no ha sido autenticado, que la acción de loguearse (login) consta de informar las credenciales a fin de poder autenticarse y la acción de desautenticarse (logout), si me permiten el término aunque no suene de lo mejor, consta de volver a un usuario autenticado al estado de anónimo.

En ASP.NET el modo de autenticación por formularios es aplicable para aplicaciones web que requieren autenticación, siempre que la misma no sea autenticación de Windows, para tales casos ASP.NET ofrecerá la opción de establecer el modo de autenticación Windows, pero ese es otro tema.

ASP.NET a través del modo de autenticación por formularios nos brindará mecanismos y funcionalidades que nos ayudarán a realizar ambas tareas: autenticación y autorización.

Para que el modo de autenticación por formularios funcione, solo debemos activarlo y la forma es simplemente ajustar la propiedad mode del tag authentication (del archivo web.config) en Forms, tal como se muestra a continuación:

<authentication mode="Forms">

El modo de autenticación por formularios creará una cookie con la información básica del usuario (obviamente encriptada), la cual será enviada por el browser en cada solicitud y el Servidor analizará la información de esta cookie para hacer los chequeos de autenticación y autorización necesarios. Este modo de funcionamiento es necesario debido a que en un entorno web las conexiones entre el cliente y el servidor se establecen y se cortan en cada solicitud. Por defecto la cookie tomará el nombre “.ASPXAUTH”.

En el siguiente esquema pueden observarse paso a paso las verificaciones de autenticación y autorización que realizará ASP.NET cuando una solicitud es procesada y se está utilizando autenticación por formularios:


Autenticación por formularios

El proceso graficado será realizado automáticamente por ASP.NET con simple hecho de haber establecido el atributo mode como Forms.
En nuestros ejemplos (que los estaremos realizando sobre Visual Studio 2008 SP1) utilizaremos una aplicación web que inicialmente contendrá la página Default.aspx, que es creada automáticamente junto con el proyecto.

Estructura del proyecto

Si en el ejemplo, luego de haber establecido el modo de autenticación por formularios, intentamos acceder a la página Default.aspx, notaremos que podremos hacerlo sin problemas, pero no debemos preocuparnos, este comportamiento se debe a que aún no hemos definido las reglas de acceso, por ahora cualquier usuario podrá acceder a cualquier recurso del sitio.

Para evitar que esto suceda, deberemos configurar las reglas de acceso. Por ahora, para simplificar lo haremos agregando las siguientes líneas dentro de la sección system.web del archivo web.config:

<authorization>
      <deny users="?"/>
</authorization>

Luego veremos en detalle las opciones disponibles para esta sección de la configuración, pero la sintaxis que utilizamos es bastante intuitiva, estaremos indicando que deberá negársele el acceso a cualquier recurso del sitio a los usuarios anónimos (identificados con el signo “?”, recordemos que un usuario anónimo es aquel que no se ha autenticado).

Si luego de esta modificación intentamos acceder nuevamente a Default.aspx (que es un recurso del sitio) veremos que somos automáticamente redireccionados a la página login.aspx, obteniendo un resultado similar al siguiente en la barra de direcciones del explorador:

http://localhost:6931/login.aspx?ReturnUrl….

Se obtendrá un error dado que la página login.aspx no existe, pero ese detalle mínimo pierde importancia ya que como podemos ver, el mecanismo está funcionando.

Lo que está haciendo ASP.NET es verificar la cookie, y al no encontrarla nos ha enviado a un formulario para que ingresemos nuestras credenciales y podamos autenticarnos.

La pagina login.aspx es la página de redirección que utiliza ASP.NET por defecto, pero es posible modificar este comportamiento definiendo un tag denominado forms dentro del tag authentication en el web.config como se muestra a continuación:

<authentication mode="Forms">
      <forms loginUrl="ingreso1.aspx"></forms>
</authentication>

Y no habrá quien se sorprenda de que luego de esta última modificación, la redirección sea a:

http://localhost:6931/ingreso1.aspx?ReturnUrl…….

Existen varias opciones aplicables al tag forms, algunas de ellas, además de loginUrl son:

  • name: Nombre de la cookie (el valor por defecto es .ASPXAUTH).
  • defaultUrl: pagina de redirección si la autenticación fue satisfactoria y el usuario había ingresado originalmente a la página de logueo (login.aspx por defecto).
  • timeout: tiempo de vida de la cookie.
  • slidingExpiration: si es verdadero, el tiempo de vida de la cookie se reiniciará cada vez que la página es reenviada.
  • cookieless: permite definir si se utilizarán cookies para mantener la información del usuario.
  • requireSSL: En verdadero indicará que el browser enviará la cookie al solo si la conexión es segura (SSL) en caso contrario de no contarse con una conexión segura, no funcionará la autenticación por formulario.
  • domain: Permitirá definir en qué dominio la cookie es válida.
    path: Permitirá definir el path de la cookie.

Para continuar con el ejemplo agregaremos la página ingreso1.aspx que será desde ahora en más nuestra página donde el usuario deberá ingresar sus credenciales para autenticarse (login).

Con estas simples modificaciones en el archivo de configuración hemos conseguido dar un primer paso a incorporarle a nuestro sitio la autenticación por formularios. Ahora que hemos conseguido que nadie pueda ingresar, debemos permitir que algunos usuarios si puedan hacerlo, y en particular aquellos a los que deseemos permitirles el ingreso. En este punto encontraremos dos opciones, una de ellas es encargarnos de autenticar al usuario por nuestra cuenta, lo que implica hacer el chequeo manualmente vía código y la otra será una opción automática con algunas particularidades.

Para el primer caso tomará importancia una clase llamada FormsAuthentication. Esta clase (como su nombre indica) nos ayudará a realizar todas las funcionalidades relacionadas con la autenticación por formularios programáticamente, como por ejemplo autenticar a un usuario y redireccionarlo a la pagina que el mismo había solicitado cuando se le denegó el acceso.

Para realizar la autenticación vía código con la ayuda de la clase FormsAuthentication escribiremos lo siguiente:

protected void Login_Click(object sender, EventArgs e)
        {
            if (Usuario.Text == "Juan" && Clave.Text == "clavedejuan")
                FormsAuthentication.RedirectFromLoginPage(Usuario.Text, false);
            else
            {
                LabelError.Text = "Usuario o clave incorrecto";
            }
        }

Hemos asumido que poseemos los cuadros de texto, Usuario, Clave y el botón Login, donde hemos utilizado el evento Click del mismo para agregar el código que puede verse en las líneas superiores. De más está decir que no hemos mencionado a los validadores con el único objetivo de simplificar el ejemplo.

Si probamos el ejemplo intentando ingresar a la página Default.aspx seremos redireccionados a la página Ingreso1.aspx donde deberemos ingresar nuestras credenciales, tal como se muestra a continuación:


Autenticación

Solo podremos acceder a Default.aspx si utilizamos como usuario a “Juan” y como clave a “clavedejuan”.

En el código que terminamos de agregar podemos ver también que nos estamos valiendo del método RedirectFromLoginPage de la clase FormsAuthentication para indicar la autenticación del usuario y la redirección a la página solicitada originalmente por el mismo.

Por otra parte podríamos incluir en la página Default.aspx un botón con la siguiente funcionalidad:

protected void LogOut_Click(object sender, EventArgs e)
        {
            FormsAuthentication.SignOut();
            FormsAuthentication.RedirectToLoginPage();
        }

Al presionarlo se ha utilizado una vez más la clase FormsAuthentication para desautenticar al usuario y reenviarlo a la página de ingreso nuevamente.

Con estas operaciones hemos conseguido que un usuario pueda realizar las operaciones de autenticación, en tal caso será simple reemplazar la sentencia Usuario.Text == “Juan” && Clave.Text == “clavedejuan” que utilizamos en el ejemplo por la llamada a un método que acceda al repositorio que más deseemos para efectuar la validación de las credenciales del usuario.

Si se observa la clase FormsAuthentication se notará que existe un método llamado Authenticate que toma por parámetros un nombre de usuario y una clave y devuelve un valor booleano, podríamos preguntarnos si este método será capaz de realizar la autenticación por nosotros, esta es la segunda opción que habíamos mencionado en algunos párrafos previos y para probar como funciona, podremos modificar el código anterior de la siguiente forma:

protected void Login_Click(object sender, EventArgs e)
        {
            if (FormsAuthentication.Authenticate(Usuario.Text, Clave.Text))
                FormsAuthentication.RedirectFromLoginPage(Usuario.Text, false);
            else
                LabelError.Text = "Usuario o clave incorrecto";
        }

Si ahora intentamos ingresar nuevamente, ya no importa que usuario y clave utilicemos, no podremos hacerlo, lo cual es un resultado bastante esperable, si consideramos le hemos delegado el control de la autenticación ASP.NET pero aún no le hemos indicado quienes serán los usuarios válidos de la aplicación. Si se desea utilizar esta opción automática debe considerarse la particularidad de que los usuarios deberán definirse en el propio archivo de configuración web.config, como se muestra en el siguiente ejemplo:

<authentication mode="Forms">
  <forms loginUrl="ingreso1.aspx" >
    <credentials passwordFormat="Clear">
      <user name ="Pepe" password ="clavedepepe"/>
      <user name ="Pedro" password ="clavedepedro"/>
    </credentials>
  </forms>
</authentication>

Luego de agregar estos valores veremos que es posible ingresar con ambos usuarios. El atributo passwordFormat del tag credentials admitirá además de la opción Clear (claves en formato limpio, o sea en texto legible para quien abra el archivo web.config) las opciones MD5 y SHA1 que permitirán utilizar el hash de la clave del usuario utilizando los algoritmos MD5 o SHA1. Claro está que en tal caso deberemos crear un mecanismo capaz de establecer dichos valores, lo bueno es que para obtener los valores hash en ambos formatos la clase FormsAuthentication ofrecerá el método HashPasswordForStoringInConfigFile que permitirá tomar una clave limpia y obtener el valor de hash correspondiente en el formato deseado. En el ejemplo anterior, si deseamos utilizar por ejemplo MD5 deberemos efectuar las siguientes modificaciones:

<authentication mode="Forms">
  <forms loginUrl="ingreso1.aspx">
    <credentials passwordFormat="MD5">
      <user name="Pepe" password="957995AA67183A4D2A91F7DE3EB9A692"/>
      <user name="Pedro" password="B447CA7CBA96E91D68D43C5867522BF0"/>
    </credentials>
  </forms>
</authentication>

Donde los valores de hash se han obtenido efectuando las siguientes llamadas:

string HashedPasswordPepe = FormsAuthentication.HashPasswordForStoringInConfigFile("clavedepepe " , "MD5");

y

string HashedPasswordJuan = FormsAuthentication.HashPasswordForStoringInConfigFile("clavedejuan " , "MD5");

Si bien todo esto funciona, creo que es inevitable pensar que se podría dar un próximo paso, ya que al fin de cuentas con este esquema que terminamos de ver, si bien ASP.NET y la clase FormsAuthentication nos han ayudado a controlar la autenticación y la autorización, la funcionalidad de la autenticación debimos codificarla, y cuando utilizamos la autenticación automática con el método Authenticate debimos definir a los usuarios en el archivo web.config. No sería nada malo que ASP.NET pudiese combinar ambas opciones y permitirnos efectuar la autenticación automáticamente sobre cualquier repositorio que deseáramos utilizar.

VN:F [1.7.3_972]
Rating: 9.8/10 (12 votos cast)