Agenda telefónica desde XML en C#

Leer una agenda telefónica desde un archivo XML que actúa como origen de los datos mostrándolos en un control DataGridView personalizado cuyos registros se pueden filtrar con sentencias SQL, en Visual Studio 2017 con C#

En este ejercicio se diseña una sencilla aplicación de agenda telefónica que lee los datos desde un archivo XML en lugar de hacerlo desde una base de datos. Las principales características del ejercicio son:

  1. Lectura desde un archivo XML, configurado como origen de los datos
  2. Mostrar los datos en un control DataGridView personalizado
  3. Filtrar registros utilizando sentencias SQL desde objetos DataView
  4. Actualizar el origen de los datos con los cambios realizados en DataGridView.

El control DataGridView ha reemplazado al control DataGrid cuyas características eran muy potentes y ahorraban mucho trabajo al programador pero el nuevo control tiene nuevas funcionalidades y en varios aspectos supera al anterior. Se mantiene la compatibilidad por ahora y las aplicaciones hechas con versiones anteriores de Visual Studio que utilizan DataGrid pueden ser abiertas y modificadas con Visual Studio 2017 pero a la hora de crear el control desde cero sólo se permite DataGridView.

En un artículo anterior hay un ejercicio similar a este pero realizado en Visual Basic .NET. En él se detallan las características principales del control DataGrid, muchas de ellas aplicables al control DataGridView, puedes consultarlo aquí. En el ejercicio actual sólo se comentan las diferencias principales entre ambos controles. Respecto a DataGrid, DataGridView:

  • puede mostrar varios tipos de columna que además son más fáciles de modificar
  • existen nuevas formas de mostrar los datos, la clásica del origen de datos externo y nuevas como el almacenamiento de datos independientes en el control
  • más opciones de personalización cambiando la apariencia de celdas, filas o columnas
  • nuevas opciones de configuración del comportamiento de celdas, filas, columnas, cabeceras, etc.

Si has trabajado previamente con DataGrid no te costará utilizar DataGridView pero recuerda que no son el mismo control y que hay diferencias entre ellos.

Conectar con los datos XML al iniciar la aplicación

Al mostrar el formulario se conecta con el archivo XML sin intervención del usuario a condición de que esté localizado en la misma carpeta que el ejecutable. Para crear la cadena con la ruta se utiliza el método Combine de la clase Path, lo que hace es combinar 2 cadenas en una única ruta de acceso.

// ruta al archivo XML, en la carpeta del ejecutable
ruta = Path.Combine(Application.StartupPath, "500empresas.xml");
// DataSet: contiene una copia de los datos en esquema XML, independiente del proveedor, con sus elementos, tablas y relaciones, es el verdadero almacén de datos desconectados
datos = new DataSet();
// Rellenar el DataSet
datos.ReadXml(ruta);
// asignar Dataset  y tabla a DataGridView
this.Grid.DataSource = this.datos;
this.Grid.DataMember = "AgendaTb";

Opciones de personalización

Algunas características de la rejilla se pueden configurar por código aunque la página de propiedades del editor de Visual Studio tiene numerosos ítems que también se pueden modificar desde allí con menos esfuerzo.

// ancho de las columnas de DataGridView para que se ajusten al contenido de las celdas incluyendo las cabeceras
this.Grid.AutoResizeColumns();
this.Grid.AutoSizeColumnsMode = DataGridViewAutoSizeColumnsMode.Fill;
// configurar por código el tamaño de la primera y última columnas de DataGridView
this.Grid.Columns[0].Width = 260;
this.Grid.Columns[3].Width = 50;

Es posible personalizar las celdas en general, o las filas alternas, o las celdas de las cabeceras, también fondo y letra de las celdas seleccionadas, etc. Se crean estilos o grupos de propiedades (DataGridViewCellStyle) que se aplican a los elementos elegidos. Por ejemplo para aplicar un estilo a las cabeceras definiendo el fondo negro y las letras amarillas en negrita:

// creación de estilo para las cabeceras de DataGridView 
DataGridViewCellStyle estilo2;
estilo2 = new DataGridViewCellStyle
	{
	BackColor = Color.Black,
	ForeColor = Color.Yellow,
	Alignment = DataGridViewContentAlignment.MiddleCenter,
	Font = new Font("Calibri", 10, FontStyle.Regular)
	};
// asignar estilo a las cabeceras
this.Grid.ColumnHeadersDefaultCellStyle = estilo2;

DataView vista conectada y personalizable de un objeto DataTable

Los datos de DataGridView se muestran mediante DataView que contiene los datos de una tabla en modo conectado, DataView constituye una vista personalizada de la tabla y con ella es más sencillo realizar tareas de selección y ordenación que directamente con DataGridView. Para mostrar por ejemplo los registros que empiecen por la letra A ordenados según el campo ID, la tarea es muy sencilla sobre DataView con sentencias SQL.

// abrir la agenda por la letra A (registros ordenados)
this.TxFiltro.Text = "A"; filtro SQL: registros que empiecen por el texto escrito en el TextBox, para ello se utiliza el objeto DataView que es una vista personalizada conectada de una DataTable apta para ordenar, filtrar, navegar, etc.
DataView vista = new DataView
{
    Table = datos.Tables["Agendatb"],
    RowFilter = ("Nombre LIKE \'"
	    + (this.TxFiltro.Text + "%\'")),
    Sort = "ID"
};
// nuevo origen del DataDrid: la vista personalizada
this.Grid.DataSource = vista;


Es fácil saber el número de registros que se muestran contando las filas visibles de DataGridView o las filas de DataView.

 // contar el número de registros, se pueden contar las filas visibles de DataGridView o los registros mostrados por DataView
int n;
//n = Grid.Rows.GetRowCount(DataGridViewElementStates.Visible);
n = vista.Count;
LbRegistros.Text = ("Número de registros: " + n.ToString());

Se han creado botones de comando como letras del alfabeto para filtrar los registros seleccionando solamente aquellos que empiezan por la letra elegida (equivale a las páginas por letras de las agendas telefónicas). El mecanismo de filtrado es sencillo: se utiliza una sentencia SQL de selección que incluye el operador LIKE junto a la letra del botón pulsado (cada botón tiene su propiedad Text asignada a una letra del alfabeto). La búsqueda puede hacerse al principio del campo o en cualquier lugar del campo, dependiendo de la sentencia utilizada.

// buscar al principio de Nombre
RowFilter = ("Nombre LIKE \'" + (letra + "%\'"))
// buscar en cualquier lugar de Nombre
RowFilter = ("Nombre LIKE \'%" + (letra + "%\'"))

Además de ello se ha creado otro modo de filtrado escribiendo texto en una caja de texto por si se desea buscar según patrones de más de una letra.

Actualizar el origen de los datos

Desde otro botón se puede actualizar el origen XML de los datos con los cambios que ha recibido el DataSet.

// salir del modo edición de DataGridView
this.Grid.EndEdit();
// escribir el DataSet al archivo XML
datos.WriteXml(ruta);
datos.Clear();
datos.ReadXml(ruta);

Copiar las celdas seleccionadas

Copiar las celdas seleccionadas al portapapeles es un proceso muy sencillo utilizando los métodos SetDataObject y GetClipboardContent de la clase Clipboard. Después se pueden pegar en un archivo de texto, una hoja de Excel, etc.

private void BtCopy_Click(object sender, EventArgs e)
{
    // si hay celdas seleccionadas
    if (this.Grid.GetCellCount(DataGridViewElementStates.Selected) > 0)
    {
	    // Añadir la selección al portapapeles
	    Clipboard.SetDataObject(this.Grid.GetClipboardContent());
	    MessageBox.Show("Celdas copiadas al portapapeles", "Copiar");
    }
}

Puedes descargar el proyecto completo desde aquí.

Nota: el ajuste automático de escala del formulario que muestra la ventana de la aplicación se puede configurar de varias maneras, lo tienes en las propiedades del formulario como AutoScaleMode, la opción con la que obtengo el mejor resultado es con Dpi.