Super Control List View
[Outlook List View]

Fecha: 28/Jun/2005 (28-Junio-2005)
Autor: Ewing Morales (ewingmarx@yahoo.com)

 


Volviendo una vez al tema de los controles soñados (bonitos a la vista pero difíciles de imitar) y por supuesto, los que la herramienta de desarrollo no provee de manera natural. Estos controles de los que los programadores les gustaría disponer en el afán de entregar un espectacular resultado y con los cuales se tienen que atener a veces, o a pagar por ellos o a prescindir de ellos.

¿Quien no ha querido presentar una lista de datos; llámese esto una tabla, un archivo de registros, correos, links, etc., y se ha tenido que conformar con los controles de presentación de datos que por omisión trae el VS .Net? ¿Quien no ha querido poner en su despliegue de registros imágenes o combos, etiquetas, en fin cualquier objeto Windows y se ha visto en la necesidad de aguantarse o bajar algún control shareware de segunda o tercera?

Espero que con este tutorial este problema llegue a su fin (o por lo menos se tenga una buena y real alternativa), y que los programadores que quieran mostrar su información de registros en el formato que quieran y como quieran lo puedan hacer sin la necesidad de ser unos superexpertos en C# o VC++. Por supuesto que debes de entender algo de C#, y sobre todo debes estar buscando alguno de los temas que vas a ver acá.


[Primera Parte] El Control

Puede sonar pretencioso llamarle "supercontrol", ya que no hace nada "super". El nombre mas bien tiene que ver con que sea un contenedor y que la información útil (datos) se almacene en sus controles hijos (En este caso el texto se despliega con Labels y las imágenes con Panels). Primero hay que diseñar el Control Windows e identificar cada uno de sus componentes. Como se han dado cuenta, en este ejemplo pretendo simular la lista de correos del MS Outlook, para lo cual mi control debe tener el siguiente diseño.

Con esta primera definición de elementos, se van "clonando" por medio de código todo el contenedor, sus componentes y ala vez se le asocian los métodos, eventos y propiedades para que funcionen. En si el ListView es un arreglo de objetos Panel, cada uno con su identificador (que también se guarda en un ArrayList aparte), y después un par de hashes de etiquetas y paneles. Todos en común tienen el mismo ID con que se define un registro, así es mas fácil identificar cada elemento de la lista sin perderlos. Este identificador se almacena en el atributo Name de cada objeto, concatenado con alguna palabra que defina su utilidad (significado) en el programa, por ejemplo el panel que permite definir si un correo esta marcado o no es PFlag, así que un panel de estos podría llamarse "PFlag.correo001".

En todo esto existe un truco debido a que a los controles Label y Panel no se les puede asociar el foco (no pueden seleccionarse). Para eso se añade también un objeto TextBox, y el se encarga de controlar los eventos de teclado para navegar, además de permitir que el scroll funcione correctamente y sobre todo poder manejar el foco (focus) de cada registro. Este control no se ve, pues esta muy a la derecha del control, fuera del área de diseño, pero es bien importante que no se omita.

Propiedades

Para este control se definen únicamente tres propiedades personalizadas: Color de Etiquetas, Color de Fondo de Contenedor y color de Marco o Borde.

La creación de controles Windows por medio del VS no permiten de manera natural que estos puedan ser transparentes, sin embargo la definición de las siguientes líneas en la definición del constructor lo hacen posible.

public OutlookList()
{
    // This call is required by the Windows.Forms Form Designer.
    InitializeComponent();

    // TODO: Add any initialization after the InitComponent call
    SetStyle(System.Windows.Forms.ControlStyles.SupportsTransparentBackColor,true);
}

Eventos

Si quieren que dada una acción sobre el control se ejecute algún procedimiento que ustedes definan, como por ejemplo mostrar más información, llamar un ejecutable externo, etc., es necesario que trabajen con la respuesta del control a alguno de los siguientes eventos.

resultClick: Cuando se selecciona el registro con el ratón.

resultDoubleClick: Cuando damos doble click sobre un registro (siempre y cuando no sea sobre la bandera de marcar el correo ya que en ese caso solo se desmarca y marca el mismo).

resultEnterPress: Cuando presionamos la tecla enter y tenemos seleccionado algún registro.

resultInCtrl: Cuando entramos a algún registro seleccionándolo ya sea por medio de un click de ratón o cualquier tecla de navegación.

resultOutCtrl: Cuando abandonamos cualquier registro, es decir cuando hacemos que este pierda su foco.

Todos estos eventos envían los mismos atributos al exterior: (string panelName, string[] panelData). Lo que significa que panelName contiene el identificador del Panel, con lo cual podemos llamar algún otro método del control si así lo necesitamos. Por su parte panelData es un arreglo de cadenas que trae el contenido de las etiquetas (en este caso: From, Date, Subject, Detail) y los datos relacionados con el estado de las imágenes (Read/UnRead, Attachment, Container(siempre es EMPTY) y Flag Mark).

Métodos

Para poder llenar o limpiar la lista se deben tener definidos los métodos correspondientes, estos métodos son básico y deben ser personalizados al gusto de ustedes con tal de que cumplan los requisitos de sus controles (acuérdense que este proyecto no es mas que un tutorial para que ustedes sepan crear sus controles). Además de estos métodos, con el objetivo de simular el comportamiento de una lista de Correos Outlook, he agregado algunos métodos para cambiar las imágenes (iconos) contenidos por cada registro. En fin, pasemos a ver la definición de métodos.

Add [public void Add(string NombrePanel)]: Recibe en una cadena el nombre de un registro, el cual se aplicara como sufijo al atributo NAME de cada elemento del registro. Este método se debe dedicar básicamente a clonar los componentes definidos por el usuario, el proceso de clonación también debe estar configurado por el usuario.

Clear [public void Clear()]: Libera de memoria todos los objetos creados para representar la lista especifica. ¿Para que podría servir? Pues por si se quiere relacionar el mismo listview a alguna otra carpeta de mail por ejemplo. En un caso particular se usa para presentar resultados de búsqueda en internet en una aplicación que posteriormente publicaré.

setValues [public void setValues(string panelName, string[] panelData)]: Asigna valores a cada uno de los componentes del control. Los valores son pasados a este método como un arreglo de cadenas en el parámetro panelData. Además, este método recibe el nombre del identificador del registro previamente agregado.

readIcon [public void readIcon(string panelName, TagValue tagVal)],
attachIcon [public void attachIcon(string panelName, TagValue tagVal)],
flagIcon [public void flagIcon(string panelName, TagValue tagVal)]: Estos tres métodos son exclusivos de este control en particular, puesto que permiten cambiar el icono de mail leído a mail no leído y viceversa, de archivo con attachment o sin el y el de mail con marca o sin marca. Lo particularmente bueno de estos métodos es que además de recibir el nombre de identificación de registro sobre el cual se quiere realizar la afectación, también reciben un tipo definido por la clase en forma de enumeración del tipo:

Así que ustedes podrán saber crear sus propios enumeradores y definir parámetros de un tipo especifico.

[Segunda Parte] La aplicación

Dado que el código del SuperControl es my extensa, intentaré que conozcan sus bondades y características a través de una aplicación sencilla que hace uso del mismo.

Antes que nada se debe tener la dll del supercontrol compilada (OutlookListCtrl.dll). Ahora se debe crear un nuevo proyecto de VS del estilo "Aplicación Windows" y posteriormente se debe agregar en la barra de herramientas el nuevo control que hemos definido, a su vez, se debe agregar en las referencias del proyecto la referencia al nuevo control (en ambos casos elegimos la dll compilada).

A continuación personalize el formulario para que luzca como se muestra en la imagen de la derecha, con un control OutlookListCtrl (outlookList) y tres controles TextBox (SalioDe, EntroA, ClickEn).

 

Objetivo de la aplicación

Esta aplicación intenta leer un archivo de texto en formato "Texto Separado por Tabuladores" que contiene una serie de renglones con información referente a correos electrónicos (simulados), cuyos campos representan cada uno de los campos mas importantes del propio correo y que serán mapeados a las etiquetas o a los iconos del control. Un ejemplo de estos registros a continuación:

Jonh Smith (jsmith@mail.com) -> 01/02/2005 -> Download the Visual Studio 2005 Beta 2 -> "Get the new Visual Studio 2005 Beta 2 release, which includes the latest tools for building Windows, Web, and mobile applications, as well as the first beta release of Visual Studio 2005 Team System." -> OPEN -> ATTACH -> FLAG

Entonces, la parte principal del código de la aplicación (método Load del Form) debe quedar como a continuación se presenta:

private void Form1_Load(object sender, System.EventArgs e)
{
    TextReader tr = new StreamReader("mail.txt");
    string linea, idMail;
    string[] mailData = new string[7];
    char[] separador = {'\t'};
    int i = 1;
    while((linea = tr.ReadLine()) != null)
    {
        System.Console.Out.WriteLine(linea);
        mailData = linea.Split(separador);
        idMail = "mail" + String.Format("{0:000}",i);
        outlookList.Add(idMail);
        outlookList.setValues(idMail, mailData);

        if(mailData[4].Equals("OPEN"))
        outlookList.readIcon(idMail, OutlookListCtrl.OutlookList.TagValue.OPEN);
        else
        outlookList.readIcon(idMail, OutlookListCtrl.OutlookList.TagValue.CLOSE);

        if(mailData[5].Equals("ATTACH"))
        outlookList.attachIcon(idMail, OutlookListCtrl.OutlookList.TagValue.YES);
        else
        outlookList.attachIcon(idMail, OutlookListCtrl.OutlookList.TagValue.NO);

        if(mailData[6].Equals("FLAG"))
        outlookList.flagIcon(idMail, OutlookListCtrl.OutlookList.TagValue.YES);
        else
        outlookList.flagIcon(idMail, OutlookListCtrl.OutlookList.TagValue.NO);

        i++;
    }
}

Como pueden darse cuenta, existe un contador de líneas que al final viene siendo el identificador del registro presentado, hacemos uso del método Add para crear el registro correspondiente, y después definimos valores del registro haciendo uso del método setValues. Como pueden ver, previamente se ha separado el renglón leído en un arreglo de cadenas, usando como separador el tabulador y guardando el resultado en el arreglo mailData. Cuando pasamos el arreglo al método setValues, solo los primeros 4 elementos son considerados para asignarlos a cada etiqueta. Los tres restantes, como pueden ver, se procesan con lógica particular para cada uno.

A continuación mostramos los eventos asociados al control y el código de los mismos.

private void outlookList_resultClick(string panelName, string[] panelData)
{
    ClickEn.Text = "Click en: " + panelName;
}

private void outlookList_resultDoubleClick(string panelName, string[] panelData)
{
    MessageBox.Show("Doble Click en: " + panelName + Environment.NewLine +
    "Valor en panelData[0]: " + panelData[0]);
    if(panelData[4].Equals("OPEN"))
    outlookList.readIcon(panelName, OutlookListCtrl.OutlookList.TagValue.CLOSE);
    else
    outlookList.readIcon(panelName, OutlookListCtrl.OutlookList.TagValue.OPEN);
}

private void outlookList_resultEnterPress(string panelName, string[] panelData)
{
    MessageBox.Show("Presiono Enter en: " + panelName + Environment.NewLine +
    "Valor en panelData[0]: " + panelData[0]);
}

private void outlookList_resultInCtrl(string panelName, string[] panelData)
{
    EntroA.Text = "Entro a: " + panelName;
}

private void outlookList_resultOutCtrl(string panelName, string[] panelData)
{
    SalioDe.Text = "Salio de: " + panelName;
}

A través de estos métodos ustedes pueden identificar el registro al cual se esta ingresando o saliendo, cual es al que se le está haciendo click o doble click o sobre cual se presiona la tecla enter.

Comentarios finales

Si todo funciona bien, el resultado puede ahorrarles mucho trabajo, dolores de cabeza y hasta dinero. Ya saben, que cualquier duda o comentario pueden dirigirla a mi correo.

Aún habrá más

Les había platicado de una aplicación a la que he llamado Google OneStep. De un solo paso, obtiene todos los resultados posibles de una búsqueda Google. Para crearla fue necesario el supercontrol, la utilización de hilos de procesamiento, la obtención de resultados Web y la depuración de tags html. Aquí un screenshot:

Eso será hasta la próxima entrega.


Espacios de nombres usados en el código de este artículo:

System.IO


Fichero con el código de ejemplo: ewing_Outlook_ListView.zip - 197 KB


ir al índice