Aquí veremos, aunque sea de forma simple, cómo usar el control ListView, (en formato detalle o informe), pero espero que al menos te sirva de base para que investigues un poco más... y aprendas por tu cuenta, ya que, aunque no te lo creas, cuando investigas por tu cuenta aprendes un poco más que si todo te lo dan "hecho".
Espero que te sea de utilidad.
Sí, ya se que me he repetido con este comentario... (si has visto la página del Treeview), pero es que es verdad.
Lo que veremos en este artículo (o ejemplo sobre el ListView), será:
- Añadir elementos (items) al ListView
- Sustituir el contenido de un elemento
- Saber el elemento en el que el usuario ha hecho click
- Saber el texto anterior y nuevo de un elemento cuando el usuario lo modifica
- Buscar un elemento del ListView (en el texto o en los subitems)
- Clasificar según la columna en la que se ha pulsado (se tiene en cuenta si los datos son numéricos, de fecha, etc.)
Con respecto a lo de clasificar el contenido según la columna pulsada, he de aclararte que el ListView por defecto clasifica siempre como si fuesen cadenas, por tanto, si tenemos los números 10, 11, 20 y 100, el orden que le daría sería el siguiente: 10, 100, 11, 20. En el código de este artículo, se clasificarán de forma correcta.
Lo mismo ocurrirá con las fechas, también se clasificarán bien.Tal como comento en el propio código, en un principio iba a usar un código de la KB de Microsoft, en el cual se muestra cómo clasificar los elementos de un ListView (de tipo fecha), pero al final no lo he usado, por la sencilla razón de que el código es poco útil, al menos si queremos usarlo de forma genérica.
Ese código no sería válido, (salvo que hicieramos bastantes cambios y comparaciones), en el código aquí mostrado, en el cual se llena el ListView de dos formas distintas y con distinto número de columnas y además con los datos en columnas diferentes... me refiero a que pulsando en un botón se crean cuatro columnas, siendo la primera de tipo alfanumérico, la segunda de tipo fecha, la tercera y cuarta de tipo numérico (uno con enteros y otro con decimales); mientras que pulsando otro botón se crean tres columnas, siendo la primera con datos de fechas, la segunda con datos alfanuméricos y la tercera con datos numéricos con decimales.
Como te decía, usando el sistema que yo he estado usando hasta ahora, se puede clasificar sin importar cuantas columnas hay y en que orden están los datos... sólo hay que seguir unas pequeñas normas, que por otro lado en el ejemplo de la KB también habría que seguir... me refiero a que hay que indicarle de alguna forma al programa qué tipo de datos contiene cada columna, para ello uso unas constantes, (las cuales puedes ampliar o modificar a tu gusto), cuyo valor se asigna al TAG de cada columna, para indicar el tipo de datos que contiene.
También uso unas constantes para el formato que tendrán los datos, al menos si son datos de fechas o de moneda, para que indiques el que más te apetezca, cuando veas el código lo comprenderás mejor.Primero te mostraré el aspecto del formulario y a continuación el código.
Al final hay un link al código completo de este ejemplo.
Hay que tener en cuenta que el control usado es el que se incluye con Visual Basic 6 (creo que instalado con el Service Pack 4), por tanto puede ser que necesites cambiarlo si no tienes el VB6. De hecho el que se incluye con el Visual Basic 5.0 es otro...Este es el aspecto del formulario
Aspecto del formulario en tiempo de diseñoEste es el código de ejemplo:
'------------------------------------------------------------------------------ ' Prueba de Listview (06/Oct/02) ' Este código puede servir para saber cómo realizar tareas comunes con un Listview ' El tipo de control usado es el incluido en: ' Microsoft Windows Common Controls 6.0 (SP4) (MsComCtl.ocx) ' ' Tenía previsto mostrar cómo clasificar las columnas usando API y ' llamadas Callback (con AddressOf), pero el ejemplo sólo serviría para ' casos muy concretos y por tanto... pues no me vale, al menos a mí. ' Si estás interesado, te diré que el artículo de la KB es: ' HOWTO: Sort a ListView Control by Date (Q170884) ' En dicho artículo se clasifica un listview con dos columnas, ' si tenemos más columnas, como en el ejemplo que aquí muestro, ' habría que añadirle más código y además siempre deberían estar ' los tipos de datos en las posiciones usadas en dicho código... ' y como a mi el código que sólo vale para una vez no me es de utilidad... ' pues "paso" de él... ' ' Así que, el código mostrado es el que yo uso y con "pequeños" cambios ' es válido para cualquier cantidad de columnas y formatos... ' Tal como puedes comprobar pulsando en cualquiera de los dos botones ' que añaden datos de ejemplo. ' ' Ver las constantes cFormatoXXX para los formatos a aplicar. ' Sólo cambiándolos o añadiendo los que quieras, ' podrás usarlo con prácticamente todo tipo de datos. ' ' ©Guillermo 'guille' Som, 2002 '------------------------------------------------------------------------------ Option Explicit ' Constantes para el tipo de datos de cada columna del listview Const cTexto As String = "Texto" Const cNumero As String = "Número" Const cMoneda As String = "Moneda" Const cFecha As String = "Fecha" ' Los formatos a aplicar según el tipo de datos que contiene Const cFormatoFecha As String = "dd/mm/yyyy" Const cFormatoNumero As String = "###,###" ' "###" Const cFormatoMoneda As String = "###,###.00" ' "###.00" ' La cantidad de cifras a tener en cuenta en los números Const cCuantasCifras As Long = 20& Private Sub chkLabelAuto_Click() ' Cambiar la forma de editar el texto de un nodo If chkLabelAuto.Value = vbChecked Then ListView1.LabelEdit = tvwAutomatic Else ListView1.LabelEdit = tvwManual End If End Sub Private Sub cmdAdd_Click() ' Añadir un nuevo elemento ' Los subitems se añadirán del contenido del Text4, ' los cuales estarán en líneas separadas, (una para cada subitem) Dim i As Long Dim aSubItems() As String ' ' Creamos un array con el contenido del Text4 aSubItems = Split(Text4, vbCrLf) ' para que aSubItems tenga el mismo número que los subitems i = ListView1.ColumnHeaders.Count - 2 ReDim Preserve aSubItems(i) ' Añadimos un nuevo elemento a la lista With ListView1.ListItems.Add(, , Text1) For i = 2 To ListView1.ColumnHeaders.Count .SubItems(i - 1) = aSubItems(i - 2) Next End With End Sub Private Sub cmdBorrarItem_Click() Dim i As Long ' ' ' Si sólo se permitiera la selección simple (un solo elemento) ' ' Averiguamos el índice del nodo que está seleccionado ' i = ListView1.SelectedItem.Index ' ListView1.ListItems.Remove i ' ' Si se permite la selección de varios elementos ' recorrerlos desde el final para que no cambie el número de elementos ' mientras borramos... ' realmente si cambia, pero al examinarlos desde el final, ' no altera el contenido de los primeros For i = ListView1.ListItems.Count To 1 Step -1 ' si está seleccionado If ListView1.ListItems(i).Selected Then ' lo borramos ListView1.ListItems.Remove i End If Next End Sub Private Sub cmdBuscar_Click() ' Buscar el texto indicado en los elementos del listview Dim tItem As ListItem Dim lvwFind As ListFindItemHowConstants Dim lvwWhere As ListFindItemWhereConstants Dim i As Long Dim s As String ' ' si tenemos que buscar la palabra exacta o parcial ' (aunque esto parece ser que no funciona... ' en los subitems siempre busca la cadena completa, ' y en el Text, la búsqueda parcial sólo es desde el principio) If chkExacta.Value = vbChecked Then lvwFind = lvwWhole Else lvwFind = lvwPartial End If ' si tenemos que buscar en el texto o en los subitems If chkSubitems.Value = vbChecked Then lvwWhere = lvwSubItem Else lvwWhere = lvwText End If ' realizamos la búsqueda Set tItem = ListView1.FindItem(Text1, lvwWhere, 1, lvwFind) ' ' Si tItem es Nothing, es que no existe... If Not tItem Is Nothing Then ' quitamos la selección anterior 'ListView1.SelectedItem.Selected = False ' (si hubiera más de uno, habría que hacer un bucle) For i = 1 To ListView1.ListItems.Count If ListView1.ListItems(i).Selected Then ListView1.ListItems(i).Selected = False End If Next ' ' Seleccionamos el hallado tItem.Selected = True ' ' Mostramos el contenido del que hemos hallado Text1 = tItem.Text ' Mostramos cada uno de los subitems s = "" For i = 2 To ListView1.ColumnHeaders.Count s = s & tItem.SubItems(i - 1) & vbCrLf Next Text4 = s Else Text4 = "No se ha hallado el elemento buscado" End If End Sub Private Sub cmdLlenarLvw_Click() Dim i As Long ' ' Eliminar las cabeceras ListView1.ColumnHeaders.Clear ' ' Asignar las cabeceras With ListView1.ColumnHeaders.Add(, , "Nombre", 1620) .Tag = cTexto End With With ListView1.ColumnHeaders.Add(, , "Fecha", 1110, lvwColumnRight) .Tag = cFecha End With With ListView1.ColumnHeaders.Add(, , "Tamaño", 840, lvwColumnRight) .Tag = cNumero End With With ListView1.ColumnHeaders.Add(, , "Importe", 840, lvwColumnRight) .Tag = cMoneda End With ' ListView1.ListItems.Clear ' Asignar algunos datos aleatorios Randomize For i = 1 To 10 With ListView1.ListItems.Add(, , "Nombre número " & CStr(i)) ' Cada subitem debe corresponder con cada una de las cabeceras ' la segunda cabecera es el Subitems(1) y así sucesivamente .SubItems(1) = Format$(Now + Int(Rnd * 30) + 1, cFormatoFecha) ' Si quieres probar con números con decimales '.SubItems(2) = CStr(i * (Rnd * 1500) + i) ' usando el formato de número indicado .SubItems(2) = Format$(i * Int(Rnd * 1500) + i, cFormatoNumero) ' El formato moneda .SubItems(3) = Format$((Rnd * 150000 + 1) / 100, cFormatoMoneda) End With Next End Sub Private Sub cmdOtroFormato_Click() Dim i As Long ' ' Eliminar las cabeceras ListView1.ColumnHeaders.Clear ' ' Asignar las cabeceras ' (la primera columna SIEMPRE debe estar alineada a la izquierda) With ListView1.ColumnHeaders.Add(, , "Fecha", 1110) .Tag = cFecha End With With ListView1.ColumnHeaders.Add(, , "Nombre", 1620) .Tag = cTexto End With With ListView1.ColumnHeaders.Add(, , "Importe", 1110, lvwColumnRight) .Tag = cMoneda End With ' ListView1.ListItems.Clear ' Asignar algunos datos aleatorios Randomize For i = 1 To 11 '1100 With ListView1.ListItems.Add(, , Format$(Now + Int(Rnd * 30) + 1, cFormatoFecha)) .SubItems(1) = "Nombre número " & CStr(i) .SubItems(2) = Format$((Rnd * 150000 + 1) / 100, cFormatoMoneda) End With Next End Sub Private Sub cmdSubst_Click() ' Substituir el último elemento seleccionado ' Los subitems se añadirán del contenido del Text4, ' los cuales estarán en líneas separadas, (una para cada subitem) Dim i As Long Dim aSubItems() As String ' ' Creamos un array con el contenido del Text4 aSubItems = Split(Text4, vbCrLf) ' para que aSubItems tenga el mismo número que los subitems i = ListView1.ColumnHeaders.Count - 2 ReDim Preserve aSubItems(i) ' ' Vamos a asignar en el elemento que esté seleccionado ' (o que se haya pulsado el último, ' por ejemplo al quitar la selección...) With ListView1.SelectedItem .Text = Text1 For i = 2 To ListView1.ColumnHeaders.Count .SubItems(i - 1) = aSubItems(i - 2) Next End With End Sub Private Sub Form_Load() ' Asignar lo valores predeterminados With ListView1 ' Las pruebas serán en modo "detalle" .View = lvwReport ' al seleccionar un elemento, seleccionar la línea completa .FullRowSelect = True ' Mostrar las líneas de la cuadrícula .GridLines = True ' No permitir la edición automática del texto .LabelEdit = lvwManual ' Permitir múltiple selección .MultiSelect = True ' Para que al perder el foco, ' se siga viendo el que está seleccionado .HideSelection = False End With ' ' Asignar las cabeceras ' Este mismo código se repite en el procedimiento cmdLlenarLvw_Click, ' pero lo dejo para saber cómo eliminar tanto las cabeceras ' como los elementos propiamente dichos. ' ' El valor asignado al TAG es para saber que tipo de clasificación ' hay que realizar, el ListView siempre los ordena como cadenas With ListView1.ColumnHeaders.Add(, , "Nombre", 1620) .Tag = cTexto End With With ListView1.ColumnHeaders.Add(, , "Fecha", 1110, lvwColumnRight) .Tag = cFecha End With With ListView1.ColumnHeaders.Add(, , "Tamaño", 840, lvwColumnRight) .Tag = cNumero End With With ListView1.ColumnHeaders.Add(, , "Importe", 840, lvwColumnRight) .Tag = cMoneda End With ' Text1 = "" Text2 = "" Text3 = "" Text4 = "" ' ' Añadir algunos datos de prueba cmdLlenarLvw_Click End Sub Private Sub ListView1_AfterLabelEdit(Cancel As Integer, NewString As String) ' El nuevo texto después de editarlo manualmente Text3 = NewString End Sub Private Sub ListView1_BeforeLabelEdit(Cancel As Integer) ' El texto antes de editarlo ' Los Listview sólo permiten editar el texto del elemento ' no los Subitems... Text2 = ListView1.SelectedItem.Text End Sub Private Sub ListView1_Click() ' ' Mostramos el contenido del último item seleccionado ' ' (esto también se puede hacer, pero el ListView tiene un evento ' ' para saber cuando se ha pulsado en un elemento, ver: ListView1_ItemClick) ' Dim tItem As ListItem ' Dim i As Long ' Dim s As String ' ' ' Set tItem = ListView1.SelectedItem ' Text1 = tItem.Text ' ' Mostramos cada uno de los subitems del item seleccionado ' s = "" ' For i = 2 To ListView1.ColumnHeaders.Count ' s = s & tItem.SubItems(i - 1) & vbCrLf ' Next ' Text4 = s End Sub Private Sub ListView1_ColumnClick(ByVal ColumnHeader As MSComctlLib.ColumnHeader) ' ' NOTA: Al tener cargado dos proyectos, me daba error si ' tenía el MSComctlLib. delante del ColumnHeader ' Dim i As Long, col As Long Dim s As String ' col = ColumnHeader.Index - 1 ' If ListView1.SortOrder = lvwAscending Then ListView1.SortOrder = lvwDescending Else ListView1.SortOrder = lvwAscending End If ' ' Según el tipo de datos, asignar el formato correspondiente: Select Case ColumnHeader.Tag Case cNumero, cMoneda ' Para clasificar el tipo momenda y el número normal no importa For i = 1 To ListView1.ListItems.Count If col = 0 Then s = ListView1.ListItems(i).Text Else s = ListView1.ListItems(i).SubItems(col) End If ' Añadimos unos cuantos ceros al principio ' para que se clasifique correctamente s = Right$(String$(cCuantasCifras, "0") & s, cCuantasCifras) If col = 0 Then ListView1.ListItems(i).Text = s Else ListView1.ListItems(i).SubItems(col) = s End If Next Case cFecha For i = 1 To ListView1.ListItems.Count If col = 0 Then s = ListView1.ListItems(i).Text Else s = ListView1.ListItems(i).SubItems(col) End If ' Las fechas se indicarán empezando con el año s = Format$(s, "yyyy/mm/dd") If col = 0 Then ListView1.ListItems(i).Text = s Else ListView1.ListItems(i).SubItems(col) = s End If Next End Select ListView1.SortKey = col ListView1.Sorted = True ' ' Restaurar los valores al formato original ' aquí es donde entran en juego las constantes de formato... Select Case ColumnHeader.Tag ' ' si no queremos usar un formato en particular, ' ' podemos usar esta forma de restaurar los valores, ' ' aunque es un poco más lenta... ' Case cNumero, cMoneda ' For i = 1 To ListView1.ListItems.Count ' If col = 0 Then ' s = ListView1.ListItems(i).Text ' Else ' s = ListView1.ListItems(i).SubItems(col) ' End If ' ' Quitamos los ceros que haya a la izquierda ' Do While Left$(s, 1) = "0" ' s = Mid$(s, 2) ' Loop ' If col = 0 Then ' ListView1.ListItems(i).Text = s ' Else ' ListView1.ListItems(i).SubItems(col) = s ' End If ' Next Case cNumero For i = 1 To ListView1.ListItems.Count If col = 0 Then s = ListView1.ListItems(i).Text Else s = ListView1.ListItems(i).SubItems(col) End If s = Format$(s, cFormatoNumero) If col = 0 Then ListView1.ListItems(i).Text = s Else ListView1.ListItems(i).SubItems(col) = s End If Next Case cMoneda For i = 1 To ListView1.ListItems.Count If col = 0 Then s = ListView1.ListItems(i).Text Else s = ListView1.ListItems(i).SubItems(col) End If s = Format$(s, cFormatoMoneda) If col = 0 Then ListView1.ListItems(i).Text = s Else ListView1.ListItems(i).SubItems(col) = s End If Next Case cFecha For i = 1 To ListView1.ListItems.Count If col = 0 Then s = ListView1.ListItems(i).Text Else s = ListView1.ListItems(i).SubItems(col) End If s = Format$(s, cFormatoFecha) If col = 0 Then ListView1.ListItems(i).Text = s Else ListView1.ListItems(i).SubItems(col) = s End If Next End Select ' End Sub Private Sub ListView1_ItemClick(ByVal Item As MSComctlLib.ListItem) ' Mostramos el contenido del último item seleccionado Dim i As Long Dim s As String ' Text1 = Item.Text ' Mostramos cada uno de los subitems del item seleccionado s = "" For i = 2 To ListView1.ColumnHeaders.Count s = s & Item.SubItems(i - 1) & vbCrLf Next Text4 = s End Sub
El código de ejemplo: Listview.zip 6.08KB
Nos vemos.
Guillermo