el Guille, la Web del Visual Basic, C#, .NET y más...

Orden de las columnas DataGridView al asignarle una consulta de LINQ (en VB9)

 
Publicado el 05/Mar/2008
Actualizado el 05/Mar/2008
Autor: Guillermo 'guille' Som

Ejemplo para indicar el orden de las columnas de un DataGridView al asignarle una consulta LINQ, ya que en Visual Basic 9.0 siempre se ordenan las propiedades de los tipos anónimos y el DataGridView usa ese orden al crear las columnas automáticamente. Se proponen dos alternativas para solucionar este 'problemilla'.



 

Introducción:

Cuando Visual Basic 9.0 (ó 2008) crea un tipo anónimo en una consulta de LINQ (siempre crea un tipo anónimo para el valor devuelto por la cláusula Select), se ve que las propiedades las clasifica por orden alfabético, pero en el orden de los nombres de esas propiedades.

Nota:
Este artículo lo escribo a raíz de una consulta de erickorlando en mis foros.
Si quieres ver ese hilo, pulsa en este link:
http://foros.elguille.info/Mensajes.aspx?ID=39024 

Esto en principio no es problema, pero cuando el resultado de esa consulta se asigna a un DataGridView, ese control crea automáticamente las columnas (cabeceras) a partir de las columnas de los datos que se le asigna, y en el caso de asignarle el resultado de una consulta LINQ, esas columnas no son otra cosa que las propiedades del tipo anónimo creado en la orden Select. Y debido a que VB ordena las propiedades/columnas por el nombre de las mismas, resulta que es posible que lo que se muestre en el DataGridView no sea lo deseado.

Por ejemplo, si tenemos una consulta como la de este código:

' Listado 1
Dim cust = From c In northwind.Customers _
           Where c.CompanyName.StartsWith("L") _
           Order By c.CustomerID, c.CompanyName, c.ContactName _
           Select c.CustomerID, c.CompanyName, c.ContactName

' En DataGridView se clasifica como CompanyName, ContactName, CustomerID
DataGridView1.DataSource = cust

El DataGridView genera las columnas tal como se ve en la figura 1.
Fíjate que en vez de mostrarse tal como se indica en el Select, se ha clasificado por el nombre de las propiedades.

Figura 1. Las columnas del grid están por orden alfabético
Figura 1. Las columnas del grid están por orden alfabético

La solución que proporciona erickorlando en el hilo que te he comentado antes, es creando un tipo anónimo que tenga los nombres de las columnas de forma que se clasifiquen bien y se muestren los datos como se quiere... es decir, primero el ID del cliente, después la empresa y por último el nombre del contacto.
Este es el código que hace eso:

' Listado 2
Dim cust = From c In northwind.Customers _
           Where c.CompanyName.StartsWith("L") _
           Order By c.CustomerID, c.CompanyName, c.ContactName _
           Select New With {Key .Cliente = c.CustomerID, _
                            Key .Compañia = c.CompanyName, _
                            Key .Contacto = c.ContactName}

DataGridView1.DataSource = cust

Ese mismo código se puede escribir también de esta forma:

' Listado 3
Dim cust = From c In northwind.Customers _
           Where c.CompanyName.StartsWith("L") _
           Order By c.CustomerID, c.CompanyName, c.ContactName _
           Select Cliente = c.CustomerID, _
                  Compañia = c.CompanyName, _
                  Contacto = c.ContactName

DataGridView1.DataSource = cust

La diferencia entre el listado 2 y el 3, es que en el segundo las propiedades son de solo lectura (Key indica que es de solo lectura) y en el listado 3, son de lectura y escritura, además de que, para generar el tipo anónimo no es necesario indicarlo explícitamente. Es la ventaja de VB, je, je, que hasta en esto nos facilita el trabajo.

Los dos listados anteriores mostrarán los datos como en la figura 2.

Figura 2. Las columnas ordenadas como queremos
Figura 2. Las columnas ordenadas como queremos

 

El problema de esta solución es que si esos nombres de las propiedades indicadas después de Select no están en el orden que nos gustaría, pues... que no se mostrarían como queremos.

Por tanto, la solución que "casi" nos puede solucionar la papeleta es decirle al DataGridView que no genere automáticamente los nombres, de las columnas.
Pero si le decimos eso... pues... ¡no veremos nada! (o casi), así que... tenemos que crear las columnas manualmente, indicar qué queremos que se muestre en la cabecera del DataGridView... pero lo más importante es que le tenemos que indicar qué columna de los datos corresponde con cada columna del DataGridView.
Los nombres de las columnas de los datos son los nombres de las propiedades del tipo anónimo que creamos en la consulta de LINQ, para ser más concreto los nombres que usemos en el Select.

Este es el código que no genera los nombres de las columnas del DataGridView y que puedes usar para que estén en el orden que te interese:

' Listado 4
Dim cust = From c In northwind.Customers _
           Where c.CompanyName.StartsWith("L") _
           Order By c.CustomerID, c.CompanyName, c.ContactName _
           Select c.CustomerID, c.CompanyName, c.ContactName

DataGridView1.AutoGenerateColumns = False
DataGridView1.Columns.Clear()

DataGridView1.Columns.Add("CustomerID", "CustomerID")
DataGridView1.Columns.Add("CompanyName", "CompanyName")
DataGridView1.Columns.Add("ContactName", "ContactName")
DataGridView1.Columns(0).DataPropertyName = "CustomerID"
DataGridView1.Columns(1).DataPropertyName = "CompanyName"
DataGridView1.Columns(2).DataPropertyName = "ContactName"

DataGridView1.DataSource = cust

Fíjate que lo que hay en la cláusula Select es lo mismo que en el listado 1, es decir, los nombres de las columnas de la tabla Customers de la base de datos Northwind.

Esos nombres de las columnas de la tabla son los que el compilador de Visual Basic creará para los nombres de las propiedades del tipo anónimo que se crea siempre con Select. Pero como ya vimos que el VB ordena alfabéticamente esas propiedades, lo que tenemos que hacer es asignar al DataGridView lo que queremos que se muestre, y en el orden que nos interese. En este caso, las columnas se muestran en el mismo orden que están indicadas después de Select, y el resultado sería el de la figura 3.

Figura 3. Las columnas ordenadas manualmente
Figura 3. Las columnas ordenadas manualmente

El código del listado 3, hace lo siguiente:

  1. Genera el código de la consulta de LINQ.
  2.  Le asignamos un valor False a la propiedad AutoGenerateColumns del DataGridView para que no genere las columnas.
  3.  Eliminamos las columnas que tuviera, ya que si antes hemos mostrado los datos, pues habría más columnas de la cuenta, tal como puedes ver en la figura 4.

Figura 4. Si no borramos las columnas, se van acumulando
Figura 4. Si no borramos las columnas, se van acumulando

  1. Añadimos cada una de las columnas que queremos tener.
    El primero parámetro del método Add es el nombre de la columna, y el segundo es lo que queremos que se muestre en esa columna. Si quisiéramos que se mostrara como en la figura 5, tendríamos que crear esas columnas tal como se muestra en el listado 5.

Figura 5. Cabeceras personalizadas
Figura 5. Cabeceras personalizadas

' Listado 5
DataGridView1.Columns.Add("CustomerID", "ID")
DataGridView1.Columns.Add("CompanyName", "Empresa")
DataGridView1.Columns.Add("ContactName", "Contacto")
  1. Como te decía antes, la parte más importante es asignar a la propiedad DataPropertyName de cada columna el campo al que queremos enlazar los datos, ya que ese es el "dato" que tiene en cuenta el DataGridView para saber qué se debe mostrar en cada columna de cada fila.

Nota:
Si el código del listado 4 lo pones junto con otros códigos que manipulen el contenido del DataGridView, debes borrar siempre el contenido de las columnas: DataGridView1.Columns.Clear() antes de asignar los datos al DataGridView por medio de la asignación a la propiedad DataSource. Al menos si no quieres que haya columnas huérfanas, al estilo de la figura 4.

Y si siempre vas a usar esas columnas con el DataGridView, puedes escribir todo el código que hay después de crear la consulta LINQ y antes de asignar el valor al DataSource en el evento Load del formulario o en cualquier otro sitio, ya que si siempre vas a asignar los mismos datos (las mismas columnas/propiedades), pues... te evitas estar repitiendo ese código.

 

Tal como te comento, todo este "follón" solo es necesario hacerlo con Visual Basic, ya que en C# no se ordenan las propiedades de los tipos anónimos generados en la cláusula select.
En el siguiente código tienes lo que habría que escribir en C# para tener algo parecido a la figura 3.

// Listado 6
var cust = from c in northwind.Customers 
           where c.CompanyName.StartsWith("L") 
           orderby c.CustomerID, c.CompanyName, c.ContactName 
           select new {c.CustomerID, c.CompanyName, c.ContactName};

DataGridView1.DataSource = cust;

 

En el ZIP tienes dos proyectos, uno para Visual Basic y el otro para C#.

En esos proyectos se usan objetos LINQ to SQL, que están conectados a la base de datos Northwind, por tanto, debes tener esa base de datos y seguramente tendrás que cambiar la cadena de conexión, ya que la que yo uso es:
Data Source=(local)\sqlexpress; Initial Catalog=Northwind; Integrated Security=True.

 

Nos vemos.
Guillermo

P.S.
Si quieres saber más sobre todo lo referente a LINQ y otras novedades de Visual Basic 9.0 (o Visual Basic 2008), te recomiendo que compres mi libro electrónico Las Novedades de Visual Basic 9.0. ;-))))

 

Este artículo también está publicado en La Cueva de Desarrollo de Solid Quality Mentors.


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

System.Windows.Forms
System.Linq
 


Código de ejemplo (comprimido):

Fichero con el código de ejemplo: clasificarPropiedadesTipoAnonimoSelect.zip - 35.70 KB

Contiene los dos proyectos, el de Visual Basic y el de C#.

(MD5 checksum: 269B625F96289768A995570326EA4617)


 


La fecha/hora en el servidor es: 21/12/2024 16:47:12

La fecha actual GMT (UTC) es: 

©Guillermo 'guille' Som, 1996-2024