Enlace de datos personalizado

18 de Julio de 2005

Publicado: 9 de noviembre de 2004

Michael Weinhardt

http://www.mikedub.net/

Por enlace

Resumen: Michael Weinhardt explica varias nuevas mejoras y adiciones del diseñador que permitirán convertir un tipo personalizado único en un origen de datos de lista completo sin necesidad de escribir ni una sola línea de código. También se examinarán las futuras mejoras de la versión beta 2. (15 páginas impresas.)

Descargar el archivo winforms11162004_sample.msi..

En esta página

Introducción Introducción
Captura de los datos Captura de los datos
Visualización de los datos Visualización de los datos
Enlace de datos Enlace de datos
Todo lo grande proviene de lo pequeño Todo lo grande proviene de lo pequeño
Automático para los desarrolladores Automático para los desarrolladores
Algunos ajustes de última hora Algunos ajustes de última hora
Mejoras de Windows Forms 2.0 Beta 2 Mejoras de Windows Forms 2.0 Beta 2
¿Dónde estamos? ¿Dónde estamos?
Agradecimientos Agradecimientos
Referencias Referencias

En esta página

Introducción Introducción
Captura de los datos Captura de los datos
Visualización de los datos Visualización de los datos
Enlace de datos Enlace de datos
Todo lo grande proviene de lo pequeño Todo lo grande proviene de lo pequeño
Automático para los desarrolladores Automático para los desarrolladores
Algunos ajustes de última hora Algunos ajustes de última hora
Mejoras de Windows Forms 2.0 Beta 2 Mejoras de Windows Forms 2.0 Beta 2
¿Dónde estamos? ¿Dónde estamos?
Agradecimientos Agradecimientos
Referencias Referencias

Introducción

Hace poco tuve la suerte de disfrutar de la oportunidad de visitar a un amigo de Taipei, en Taiwán, lo que estuvo muy bien, ya que me encanta viajar. En concreto, me gusta mucho intentar aprender un poco del idioma local, tanto porque supone una muestra de respeto como porque hace que el viaje resulte más interesante. En Taipei, el idioma es una versión moderna y estandarizada del chino, con un toque taiwanés (no soy lingüista, por lo que les pido que disculpen mis errores).

Uno de los temas por los que comienzo es por los números, como imagino que hace casi todo el mundo. Intenté llegar al punto de poder decir los números sin tener que traducir internamente de inglés a chino. Para ello, buscaba nuevas y entretenidas maneras de practicarlos. Un ejemplo puede ser esperar en un paso de peatones. En Taipei, la mayoría de las señales de los semáforos no sólo muestran hombres rojos y verdes, sino también una cuenta atrás de números que indican el tiempo que les queda a los peatones para cruzar. Concretamente, intentaba decir los números en chino a medida que iban apareciendo en la cuenta atrás y, al principio, se me daba increíblemente mal.

Figura 1. Cuenta atrás de un paso de peatones taiwanés

Inspirado por el juego de las señales de los pasos de peatones, decidí escribir una aplicación de Windows Forms con un estilo similar de juego informático para una gran variedad de palabras, números y frases. La idea era conseguir que la aplicación mostrase una secuencia aleatoria de éstos y que el usuario intentase traducir al chino hablado.

Captura de los datos

El primer paso consistía en determinar los datos necesarios para cada palabra, número o frase. Al final me decidí por la representación en inglés (del que se traduce), la versión Pinyin (lo que se está traduciendo también), una imagen del carácter o los caracteres en chino (cómo se escriben en chino) y la pronunciación (para que se pueda oír como suena).

Nota. Pinyin es un sistema de deletreo del chino mediante el alfabeto latino, que facilita que personas interesadas por los idiomas como yo puedan aprenderlo. Consulte http://en.wikipedia.org/wiki/Pinyin (en inglés) si desea obtener más información.

Con esta idea en la cabeza, comencé a crear una interfaz de usuario que capturase los datos deseados del juego. Desde el punto de vista de la portabilidad, no quería utilizar una base de datos en la que persistiese esta información. Por el contrario, tomé la decisión de utilizar un tipo Word personalizado para capturar la información, que en algún momento usaría las posibilidades de serialización que ofrece .NET Framework:

[Serializable]
public class Word {

  // Constructor
  public Word(string english, string pinyin, 
              Image character, byte[] pronunciation);

  // Properties
  public string English { get; set; }
  public string Pinyin { get; set; }
  public Image Character { get; set; }
  public byte[] Pronunciation { get; set; }
}

Visualización de los datos

texto texto texto

texto texto texto

Como tendremos la necesidad de varias palabras, números y frases, finalmente necesitaremos un origen de datos de lista compuesto de cero o más elementos de datos Word. Por tanto, deberemos determinar el estilo de la interfaz de usuario más adecuada para mostrar una lista de Words. Hay dos modelos de interfaces de usuario habituales para mostrar orígenes de datos de lista:

  • Interfaz de usuario de vista de detalles

  • Interfaz de usuario de vista de lista

Las interfaces de usuario de vistas de detalles normalmente muestran un elemento de datos de lista en cada momento, donde cada propiedad pública del elemento de datos está asociada con su propio control. Al ser visible únicamente un elemento de datos en un momento determinado, las interfaces de usuario de vistas de detalles normalmente incluyen comandos de exploración para desplazarse por la lista. Los mecanismos habituales para exponer estos comandos en la interfaz de usuario consisten en una barra de menús y una barra de herramientas similares a las de un aparato de vídeo. En la figura 2 se muestra una interfaz de usuario de vista de detalles.

Figura 2. Interfaz de usuario de vista de detalles

Las interfaces de usuario de vista de listas, como su propio nombre indica, muestran varios elementos de datos mediante un control similar a una cuadrícula de datos o a una lista, como el formulario que aparece en la figura 3.

Figura 3. Interfaz de usuario de vista de lista

Las interfaces de usuario de vista de detalles son muy útiles para la exploración de casos concretos en los que sencillamente hay demasiadas propiedades de los elementos de datos para mostrarlos adecuadamente en una lista. También resultan útiles cuando es necesario disponer de una mayor flexibilidad y control sobre la interfaz de usuario de lo que habitualmente permiten los controles de lista. Por otra parte, las interfaces de usuario de vista de lista son adecuadas para la entrada de datos, especialmente a la hora de especificar de forma secuencial filas de datos relevantes de una vez. Consideré que sería necesario introducir bastantes datos para el juego y, por tanto, que era preferible una interfaz de usuario de vista de lista.

De este modo, para crear la interfaz de usuario, necesitamos convertir un tipo Word único en un origen de datos de lista para, a continuación, mostrarlo mediante un control de lista o de cuadrícula de datos adecuado. En este caso, considero que el control DataGridView es un excelente candidato. A continuación, necesitamos asegurarnos de que cualquier modificación se sincronizará entre el DataGridView y el origen de datos de la lista. Al prever que habrá una gran cantidad de datos, será necesario un control similar a un aparato de vídeo para facilitar la exploración. Este control deberá asegurar que la posición de lista actual se sincroniza entre él y DataGridView cuando se realiza una exploración en cualquiera de ellos.

Enlace de datos

El mecanismo subyacente diseñado para cumplir todos estos requisitos es el enlace de datos, que consiste en la capacidad de conectar, o enlazar, un origen de datos con un control de la interfaz de usuario. Una vez enlazados, la tarea principal del enlace de datos consiste en administrar automáticamente la sincronización entre la exploración y las modificaciones.

En Windows Forms 1.x, los orígenes de datos de lista necesitan implementar como mínimo System.ComponentModel.IList antes de que sea posible enlazarlos. Para los desarrolladores, esto implica que tienen que construir sus propias implementaciones IList con tipos inflexibles o, al menos, utilizar una de las implementaciones de IList predefinidas por .NET Framework, como matrices de tipos simples y ArrayList. Una vez creada, la propiedad DataSource de un control de lista se debe definir en la implementación de IList para establecer el enlace. Si el origen de datos es un conjunto de datos con tipo (.xsd), se puede realizar un enlace de forma declarativa si se agrega un componente DataSet al formulario que exponga el conjunto de datos con tipo a los controles de enlace de datos del formulario. El enlace propiamente dicho se logra estableciendo correctamente las propiedades DataSource y DataMember, como se muestra en la figura 4.

Figura 4. Enlace de datos declarativo anterior a Windows Forms 2.0

También se podría optar por hacerlo mediante programación:

// Bind DataGrid to list data source
this.northwindDataGrid.DataMember = "Employees";
this.northwindDataGrid.DataSource = this.northwind;

Desgraciadamente, todas estas sutilezas no funcionan demasiado bien cuando se trata de orígenes de datos de lista basados en tipos personalizados. En primer lugar, estos orígenes de datos sencillamente no son visibles para el diseñador de Windows Forms 1.x. En segundo lugar, aunque se puede enlazar una implementación IList a un control de lista mediante la propiedad DataSource, no se pueden aprovechar las posibilidades del DataGrid para detectar y crear automáticamente las columnas en el momento del diseño. Y no sólo eso, posiblemente necesite implementar varias interfaces más para asegurarse de que el origen de datos de lista admitirá las funciones básicas de modificación y exploración.

Como se puede suponer, una vez que se ha creado el tipo personalizado en Windows Forms 1.x, deberá dedicarse a convertirlo en un origen de datos de lista útil. Así es, a menos que utilice Windows Forms 2.0, en cuyo caso bastará con utilizar mínimamente el mouse (ratón) y, tal vez, tardará un minuto.

Todo lo grande proviene de lo pequeño

Para convertir un tipo personalizado en un origen de datos de lista completo, enlazarlo a un DataGridView y agregar una exploración similar a la de un aparato de vídeo, primero es necesario configurar el proyecto para que reconozca el tipo personalizado como un origen de datos.

Adición de un origen de datos al proyecto

Se puede registrar un tipo personalizado como un origen de datos de dos maneras. La primera consiste en seleccionar Visual Studio .NET 2005 | Data | Add New Data Source, etc. La segunda consiste en abrir la ventana de herramientas Data Sources (Visual Studio .NET 2005 | Data | Show Data Sources) y hacer clic en el vínculo Add New Data Source si no existe ningún origen de datos, como se muestra en la figura 5.

Figura 5. Ventana de herramientas sin orígenes de datos

Si el proyecto ya incluye orígenes de datos en Visual Studio .NET 2005 Beta 1, deberá dirigirse al menú contextual de la ventana de herramientas Data Sources y seleccionar Add New Data Source o hacer clic en el icono de la barra de herramientas situado en el extremo izquierdo. Independientemente de la manera elegida para agregar un nuevo origen de datos, iniciará el asistente Data Source Configuration Wizard y, para empezar, deberá especificar un tipo de origen de datos, como se muestra en la figura 6.

Figura 6. Cuadro de diálogo para seleccionar un tipo de origen de datos

Como puede ver en la figura 6, hay cuatro tipos de orígenes de datos entre los que se puede elegir, incluidos servidores de bases de datos (como Microsoft SQL Server), archivos de bases de datos locales (como archivos Microsoft Access .mdb), servicios Web con métodos Web que devuelven objetos adecuados (como DataSets u objetos personalizados como Word) y objetos. Y cuando dicen "objeto", realmente se refieren a objeto (o en realidad se refieren a "tipo"). Se puede elegir cualquier tipo en el siguiente paso del asistente. En la figura 7 se muestra cómo se selecciona el tipo personalizado Word.

Figura 7. Selección del objeto Word

Aunque aquí se utiliza Business Object, en realidad es posible registrar objetos que no sean de negocio, como la configuración de la aplicación del proyecto, que también se muestra en la figura 7. Por supuesto, seleccionaremos Word, haremos clic en el botón Next y nos quedaremos con el origen de datos esperado, cómodamente agregado a la ventana de herramientas Data Sources para nuestro deleite, como vemos en la figura 8.

Figura 8. Ventana de herramientas Data Sources con el origen de datos de objeto seleccionado

El DataConnector

El siguiente paso es muy importante: convertir nuestro origen de datos de tipo único en un origen de datos de lista adecuado con el que se puedan enlazar controles de lista de enlace de datos. Para ello, utilizamos el nuevo componente DataConnector. Basta con arrastrar uno de Toolbox al formulario y establecer su propiedad DataSource para el origen de datos de lista apropiado, que se muestra en la figura 9.

Figura 9. Adición y configuración de un componente DataConnector

De este modo, el diseñador Windows Forms 2.0 generará el siguiente código para InitializeComponent:

private void InitializeComponent() {
  ...
  // 
  // wordDataConnector
  //
  ...
  this.wordDataConnector.DataSource = 
    typeof(winforms11162004_DataBinding.Word);
  ...
}

Al ejecutar esta línea de código, DataConnector examina el origen de datos para determinar si implementa IList. Si no es así, DataConnector crea una implementación interna de IList que incluye el tipo personalizado. Concretamente, crea una implementación genérica de BindingList de la que se tratará más adelante en este artículo. Tenga cuenta que si el DataConnector está enlazado a una implementación de IList, estos pasos no serán necesarios, pero los resultados serán los mismos. Por último, DataConnector consume el tipo personalizado y mediante un proxy, lo representa en los controles enlazadas de datos como un origen de datos de lista válido mediante su propia implementación de IList. Realmente es un truco interesante.

Enlace del DataGridView mediante proxy

Ahora podemos arrastrar un DataGridView al formulario y establecer su propia propiedad DataSource. Como tal vez haya adivinado, esa propiedad se establecerá en el propio DataConnector, lo que le permitirá desempeñar diligentemente su función como origen de datos de lista mediante proxy. Esto se muestra en la figura 10.

Figura 10. DataGridView enlazado al DataConnector

A continuación, se puede ver el código InitializeComponent generado:

private void InitializeComponent() {
  ...
  // 
  // wordDataGridView
  //
  ...
  this.wordDataGridView.DataSource = this.wordDataConnector;
  ...
}

Como DataConnector presenta un origen de datos de lista completo a un mundo repleto de controles enlazables a datos, con la adecuada información de descriptor de elemento de datos, controles como DataGridView pueden examinar y utilizar dicha información para generar automáticamente las columnas apropiadas, otra característica que se muestra en la figura 10 anterior.

El DataNavigator

La última característica que deseábamos implementar era la exploración con los controles de un aparato de vídeo. Afortunadamente para nosotros, la utilidad del DataConnector no se acaba aquí porque también resulta ser un administrador de actualidad. Por tanto, este DataConnector realiza un seguimiento del elemento de datos actual e implementa los métodos, las propiedades y los eventos con los que se puede escribir código para explorar mediante programación el origen de datos de lista.

public class DataConnector : ... {
  ...
  // Properties
  public int Position { get; set; }
  public object Current { get; }
  ...
  // Methods
  public void MoveFirst();
  public void MoveLast();
  public void MoveNext();
  public void MovePrevious();
  ...
  // Events
  public event EventHandler CurrentItemChanged;
  public event EventHandler PositionChanged;
  public event AddingNewEventHandler AddingNew;
} 

La exploración básica mediante estos miembros tiene un aspecto similar al siguiente:

private void firstToolStripMenuItem_Click(object sender, EventArgs e) {
  this.wordDataConnector.MoveFirst();
}
private void prevToolStripMenuItem_Click(object sender, EventArgs e) {
  this.wordDataConnector.MovePrevious();
}
private void nextToolStripMenuItem_Click(object sender, EventArgs e) {
  this.wordDataConnector.MoveNext();
}
private void lastToolStripMenuItem_Click(object sender, EventArgs e) {
  this.wordDataConnector.MoveLast();
}

Al llamar a cualquiera de estos métodos se asegura que el DataConnector se moverá al elemento de datos de lista deseado y se sincronizará esta exploración con todos los controles enlazados al DataConnector para que puedan actualizar sus interfaces de usuario de la manera adecuada.

Por otra parte, puede que decida que arrastrar un DataNavigator al formulario y enlazarlo con el DataConnector, de nuevo mediante la propiedad DataSource, es una solución más sencilla porque se consigue el mismo efecto. Y no sólo eso, se obtiene de regalo la funcionalidad Add y Delete. El DataNavigator es un control de franja de herramientas especializadas que se puede ampliar para exponer una gran variedad de actividades relacionadas con los orígenes de datos de lista, como cargar y guardar.

Tras agregar DataNavigator, la interfaz de usuario completa tiene el aspecto de la figura 11.

Figura 11. Interfaz de usuario completa sin código

Para llegar hasta aquí, he creado un tipo Word personalizado (no una lista), utilizado un asistente, agregado un DataConnector, un DataGridView y un DataNavigator, y he establecido tres propiedades. En general, resulta bastante sencillo.

Automático para los desarrolladores

Pero, ¿por qué perder el tiempo en arrastrar, colocar y configurar los componentes DataConnector y DataNavigator? Una manera aún más fácil consiste, sencillamente, en arrastrar el origen de datos personalizado al formulario, como vemos en la figura 12.

Figura 12. Arrastre de un origen de datos del proyecto a un formulario

Todo el trabajo que acabamos de hacer para crear la interfaz de usuario lo realiza automáticamente el diseñador de Windows Forms 2.0 cuando se utiliza esta técnica y ni siquiera tendrá tiempo de pensar en hacerse un café y aún menos en bebérselo. Además, el código que se genera para realizar esta tarea es básicamente el mismo que necesitaríamos escribir nosotros.

Nota. En el momento de redactar este artículo, este método sólo creó tres de las cuatro columnas esperadas. Espero que ambos métodos descritos en este artículo sean coherentes con la versión Windows Forms 2.0, si no ocurre en la versión beta 2.

La ventana de herramientas Data Sources permite, en realidad, realizar unos cuantos trucos de magia más. Por ejemplo, puede configurar el origen de datos para generar una interfaz de usuario de detalles o de lista (DataGridView) al arrastrarlo a un formulario. También puede especificar el tipo de control que se debe generar correctamente para cada propiedad del elemento. La figura 13 muestra estos dos elementos en acción.

Figura 13. Ejemplo de generación de interfaces de usuario

Por último, si selecciona Customize en cualquiera de los menús mostrados en la figura 13, puede agregar y quitar elementos de la lista de opciones predeterminadas.

Algunos ajustes de última hora

Por supuesto, el diseñador sólo puede trabajar con lo que genera. Por ejemplo, no puede adivinar el orden deseado de las columnas y, como una matriz de bytes puede corresponder a una gran variedad de formatos de datos, hizo lo mejor que pudo al interpretar nuestra propiedad de matriz de bytes Pronunciation como una imagen, aunque queríamos almacenar un sonido. Para solucionar el primer problema basta con utilizar el editor de columnas de DataGridView para actualizar el orden de las columnas. Para solucionar el segundo, fue necesario crear una implementación columna/celda de pronunciación personalizada DataGridView que permitiese que con un sólo clic del mouse se escuchase la pronunciación. Los detalles de implementación se apartan del objetivo de este artículo, pero puede consultar la entrega de febrero de 2004 de las maravillas de Windows Forms (en inglés) para obtener más información, teniendo en cuenta que, aunque hay algunos cambios entre la versión alfa que escribí y la beta 1, los conceptos son los mismos.

Por último, para simplificar el proceso de agregar mapas de bits y archivos wav a Character y Pronunciation, incluí compatibilidad en ambos casos con arrastrar y colocar. Examine el ejemplo de código para obtener más información acerca de los detalles de implementación.

Tras realizar estos pequeños arreglos, el aspecto final del formulario es el del ejemplo que vimos en la figura 3.

Mejoras de Windows Forms 2.0 Beta 2

La tecnología que se trata en este artículo se basa en Windows Forms 2.0 Beta 1. Aunque lo mencionaré, no hace falta decir que las cosas pueden cambiar. Afortunadamente, Joe Stegman y Steve Lasker de Microsoft tuvieron la gentileza de emplear su tiempo en enumerar el conjunto de actualizaciones y mejoras que estarán disponibles en el enlace de datos en la versión beta 2.

En primer lugar, unos cuantos nombres han cambiado, concretamente DataConnector se convertirá en BindingSource y DataNavigator se convertirá BindingNavigator. Personalmente, considero que los nuevos nombres representan con mayor precisión la función de estos componentes, es decir, representan y trabajan con orígenes de enlace en vez de con los orígenes de datos que exponen.

En segundo lugar, se han mejorado la ventana de herramientas Data Sources y el asistente Data Source Configuration Wizard. La ventana de herramientas Data Sources actualizada ofrece mayor ayuda directamente, como se muestra en la figura 14.

Figura 14. Ventana Data Sources de la versión beta 2

Lo mismo se aplica al asistente Data Source Configuration Wizard. Las opciones del paso para seleccionar un origen de datos (Choosing a Data Source) se han simplificado y cada opción proporciona información descriptiva acerca de lo que puede hacer. En la figura 15 se puede ver esto aplicado a la selección de un origen de datos de objeto.

Figura 15. Selección de orígenes de datos más simplificada e informativa

El siguiente paso del asistente, seleccionar el tipo con el que se establecerá el enlace, se ha mejorado en tres aspectos, como muestra la figura 16.

Figura 16. Selección del enlace de tipos más funcional

Puede ocultar o mostrar los ensamblados "Microsoft." y "System.", los comentarios XML asociados con el tipo se muestran como texto descriptivo y los guiones de subrayado que siguen a los espacios de nombres de la lista, como se muestra en la figura 7, ahora aparecen correctamente como puntos.

Por último, al arrastrar un origen de datos a un formulario como un DataGridView se generarán nombres correctos de DataGridView y DataGridViewColumn. Actualmente, el nombre se basa sencillamente en el nombre del tipo de columna DataGridView con mayúsculas al principio de cada palabra y un número (dataGridViewTextBoxColumn1, por ejemplo). Probablemente en la versión beta 2, los nombres sean similares al nombre del campo de origen de datos junto con el nombre del tipo de la columna DataGridView.

No olvide que los conceptos descritos en este artículo no cambiarán, aunque lo haga la tecnología. Aún así, le animaría a familiarizarse con la versión beta 1, si es posible.

¿Dónde estamos?

Aunque todavía no he terminado la aplicación, parece que va por buen camino aunque sólo he trabajado en la parte correspondiente a la entrada de datos del proyecto, ya que he conseguido pedir en chino diez bolitas de cerdo sabor barbacoa. Además, ha resultado una manera estupenda de explorar dos técnicas para convertir un tipo personalizado único en un auténtico origen de datos de lista, lo que dependía principalmente del nuevo caballo de batalla de enlace de datos, el DataConnector. Los DataConnectors aseguran que las modificaciones se sincronizan entre los controles enlazados y los orígenes de datos de lista y, junto con el DataNavigator similar a un aparato de vídeo, garantizan que también se sincroniza la exploración. El costo total del desarrollo fue menor, por no decir mínimo, especialmente al compararlo con Windows Forms 1.x.

Sin embargo, si juega con el ejemplo, puede que note que faltan algunas características, entre ellas la posibilidad de ordenar, buscar y filtrar. Aunque podríamos implementar estas funciones en un formulario, tiene más sentido desde el punto de vista de la reutilización implementarlos en el origen de datos de lista propiamente dicho, asegurando por tanto que cualquier control enlazado de datos puede utilizarlo. En la próxima entrega, veremos cómo agregar las funciones de ordenación y búsqueda mediante la implementación de IBindingList, que se puede llevar a cabo con facilidad si se deriva del nuevo tipo genérico BindingList<T>. También examinaremos cómo agregar funciones avanzadas de ordenación y filtrado mediante la implementación de IBindingListView. Por último, agregaremos persistencia con las posibilidades de serialización de .NET Framework para finalizar la parte de administración de datos del juego de la aplicación.

Agradecimientos

Me gustaría expresar mi agradecimiento a los habitantes de Taipei por ser tan generosos, cálidos, respetuosos y considerados. Concretamente, me gustaría dar las gracias a la persona que hizo posible este viaje. Por último, un agradecimiento muy especial a Steve Lasker y Joe Stegman de Microsoft por su tiempo y contribución a este artículo.

Referencias

Object Binding por Steve Lasker, CoDe Magazine, septiembre/octubre de 2004 (en inglés)

Michael Weinhardt trabaja actualmente a jornada completa en varios trabajos de redacción sobre .NET que incluyen el ser el coautor de Windows Forms Programming in C#, 2nd Edition (Addison Wesley) con Chris Sells y escribir esta columna. Michael adora .NET en general y Windows Forms en particular. También se le ha acusado con frecuencia de sobrevalorar la calidad de la música de los ochenta, un período que el considera como el más innovador de la historia moderna. Visite http://www.mikedub.net/ (en inglés) si desea obtener más información.

Mostrar: