none
Agregar columnas del DataGridView a archivo .txt.

    Pregunta

  • Buenas,

    Gracias a todos los que escriben preguntas y a los que tratan de encontrar solución a los problemas planteados, me ha sido muy útil en mi introducción a Visual Basic. Ahora, les explico mi duda.

    He creado una suite de programas que sirven para la toma de tiempos parciales en eventos deportivos. A medida que el deportista ejecuta su competición (tipo contra-reloj) se van tomando tiempos de paso (entre 6 y 9 o más dependiendo de la competición) y los datos se van agregando a un DataGridView. El DataGrid muestra el nombre de los competidores, los parciales (detallo la referencia del parcial en la cabecera de la columna) y el total. Al final se guardan los datos en un .txt de manera que posteriormente el deportista pueda leer los datos en otro ordenador mediante un lector que he creado para tal efecto. 

    Este es el timer: (no me permite adjuntar imágenes aún)

    Y este el lector: (no me permite adjuntar imágenes aún)

    Aquí radica el problema: el lector lee perfectamente los parciales almacenados del .txt pero no se cómo hacer para que también pueda almacenar los títulos de las columnas de los parciales, ya que el deportista necesita conocerlos´. Quizá mi enfoque sea erróneo, así que puede que me puedan ayudar.


    • Editado XabiT domingo, 4 de septiembre de 2016 15:07 Faltaban Datos
    domingo, 4 de septiembre de 2016 15:05

Respuestas

  • "XabiT" escribió:

    > Me parece que estoy en el peor de los escenarios. Lo único que hice
    > fue agregar un control DataGridView al formulario y hacer que este
    > se rellenara a medida que iba recibiendo datos.

    ¿Y por qué no creas "manualmente" un objeto DataTable para enlazárselo al control DataGridView?

    Si deseas hacer una prueba, inserta en ese formulario los siguientes procedimientos:

        Private Function CreateDataTable()  As DataTable
    
             ' Crear el objeto DataTable llamado TiemposParciales
             '
             Dim dt  As New DataTable("TiemposParciales")
    
             ' Añadir las columnas necesarias
             '
             dt.Columns.Add(New DataColumn("IdDorsal", Type.GetType("System.Int32")))
             dt.Columns.Add(New DataColumn("TiempoParcial", Type.GetType("System.String")))
    
             ' Devolver el objeto DataTable.
             '
             Return dt
    
        End Function
    
        ''' <summary>
        ''' Crea un archivo de texto delimitado con el contenido de
        ''' un objeto DataTable.
        ''' </summary>
        ''' <param name="fileName">Ruta y nombre del archivo de texto.</param>
        ''' <param name="dt">Un objeto DataTable válido.</param>
        ''' <param name="separatorChar">El carácter delimitador de los campos.</param>
        ''' <param name="hdr">Indica si la primera fila contiene el nombre de los campos.</param>
        ''' <param name="textDelimiter">Indica si los campos alfanuméricos deben aparecer
        ''' entre comillas dobles.</param>
        ''' <returns></returns>
        ''' <remarks></remarks>
        Private Shared Sub CreateTextDelimiterFile(fileName As  String, dt As DataTable, _
                                                   separatorChar As Char, hdr  As Boolean, _
                                                   textDelimiter As Boolean)
    
            ' Si no se ha especificado un nombre de archivo,
            ' o el objeto DataTable no es válido, provocamos
            ' una excepción de argumentos no válidos.
            '
            If ((fileName = String.Empty) OrElse (dt Is Nothing)) Then
                Throw New ArgumentException("Argumentos no válidos.")
            End If
    
            Dim col As  Integer = 0
            Dim value As  String = String.Empty
    
            ' Creamos el archivo de texto con la codificación por defecto.
            '
            Using sw As New IO.StreamWriter(fileName, False, System.Text.Encoding.Default)
    
                If (hdr) Then
                    ' La primera línea del archivo de texto contiene
                    ' el nombre de los campos.
                    For Each dc As DataColumn In dt.Columns
    
                        If (textDelimiter) Then
                            ' Incluimos el nombre del campo entre el caracter
                            ' delimitador de texto especificado.
                            '
                            value &= """" & dc.ColumnName & """" & separatorChar
    
                        Else
                            ' No se incluye caracter delimitador de texto alguno.
                            '
                            value &= dc.ColumnName & separatorChar
    
                        End If
                     Next
    
                     sw.WriteLine(value.Remove(value.Length - 1, 1))
                     value = String.Empty
    
                 End If
    
                 ' Recorremos todas las filas del objeto DataTable
                 ' incluido en el conjunto de datos.
                 '
                 For Each dr As DataRow In dt.Rows
    
                     For Each dc As DataColumn In dt.Columns
    
                         If ((dc.DataType Is System.Type.GetType("System.String")) And _
                             (textDelimiter = True)) Then
    
                             ' Incluimos el dato alfanumérico entre el caracter
                             ' delimitador de texto especificado.
                             '
                             value &= """" & dr.Item(col).ToString & """" & separatorChar
    
                         Else
                             ' No se incluye caracter delimitador de texto alguno
                             '
                             value &= dr.Item(col).ToString & separatorChar
    
                         End If
    
                         ' Siguiente columna
                         col += 1
    
                     Next
    
                     ' Al escribir los datos en el archivo, elimino el
                     ' último carácter delimitador de la fila.
                     '
                     sw.WriteLine(value.Remove(value.Length - 1, 1))
                     value = String.Empty
                     col = 0
    
                Next ' Siguiente fila
    
            End Using
    
        End Sub
    
        Private Function GetFileName() As String
    
            Dim fileName As String = String.Empty
    
            Using sfd As New SaveFileDialog()
                sfd.Filter = "Archivos de texto | *.txt"
                sfd.DefaultExt = ".txt"
                Dim dr As DialogResult = sfd.ShowDialog()
                If (dr = DialogResult.OK) Then
                    fileName = sfd.FileName
                End If
            End Using
    
            If ((fileName.Length > 0) AndAlso (IO.File.Exists(sFile))) Then
                ' Si el archivo existe, solicitar confirmación para sobreescribirlo.
                '
                Dim msg As String = "Ya existe un archivo de texto con el mismo nombre." & _
                                    Environment.NewLine & "¿Desea sobrescribirlo?"
    
                If (MessageBox.Show(msg, "Crear archivo de texto delimitado", MessageBoxButtons.YesNo, _
                                    MessageBoxIcon.Information) = DialogResult.Yes) Then
                    ' Eliminar el archivo
                    IO.File.Delete(fileName)
    
                Else
                    ' No se desea sobrescribir el archivo.
                    fileName = String.Empty
    
                End If
    
            End If
    
            Return fileName
    
        End Function

    En el evento Load del formulario, crearías el objeto DataTable y se lo asignarías al control DataGridView:

        Private Sub Form1_Load(sender As Object, e As EventArgs) Handles MyBase.Load
    
            ' Enlazar el control DataGridView con un objeto DataTable
            DataGridView1.DataSource = CreateDataTable()
    
        End Using

    Y cuando desees crear el archivo de texto, ejecutarías lo siguiente:

        Private Sub SaveFileStripMenuItem_Click(sender As Object, e As EventArgs) Handles SaveFileStripMenuItem.Click
    
            Try
                ' Obtener el archivo de texto donde se desean exportar los datos
                '
                Dim sFile As String = GetFileName()
           
                If (sFile = String.Empty) Then
                    ' Se ha cancelado el cuadro de diálogo Guardar cómo;
                    ' abandonar el procedimiento
                    Return
                End If
    
                ' Referenciamos el objeto DataTable enlazado a un control DataGridView
                '
                Dim dt As DataTable = DirectCast(DataGridView1.DataSource, DataTable)
    
                ' Indicamos nuestra intención de crear un archivo de texto delimitado por
                ' puntos y comas, así como que en la primera línea aparezcan los nombres
                ' de las columnas, y que los campos alfanuméricos sean encerrados entre
                ' comillas dobles.
                '
                CreateTextDelimiterFile(sFile, dt, ";"c, True, True)
    
                MessageBox.Show("Se ha creado satisfactoriamente el archivo de texto.")
    
            Catch ex As Exception
                ' Se ha producido un error
                MessageBox.Show(ex.Message)
    
            End Try
    
        End Sub

    > He puesto un máximo de 15 columnas posibles, por si el evento lo exige.

    Fíjate que la función CreateDataTable solamente crea dos columnas, pero que le puedes añadir 15, 20 o la que estimes necesarias. ;-)


    Enrique Martínez Montejo
    [MS MVP - Visual Studio y Tecnologías de Desarrollo]

    Nota informativa: La información contenida en este mensaje, así como el código fuente incluido en el mismo, se proporciona «COMO ESTÁ», sin garantías de ninguna clase, y no otorga derecho alguno. Usted asume cualquier riesgo al poner en práctica, utilizar o ejecutar lo recomendado o sugerido en el presente mensaje.

    Si esta respuesta le ha resultado útil, recuerde marcarla como satisfactoria.

    Si usas Visual Basic .NET y deseas ser productivo y feliz, se inteligente y activa la instrucción
    Option Strict.


    lunes, 5 de septiembre de 2016 7:46
    Moderador
  • "XabiT" escribió:

    > Aquí radica el problema: el lector lee perfectamente los parciales almacenados
    > del .txt pero no se cómo hacer para que también pueda almacenar los títulos de
    > las columnas de los parciales, ya que el deportista necesita conocerlos´.

    Hola:

    No habría estado de más que hubieses publicado algo del código fuente que estás ejecutando, mayormente para hacernos una idea de lo que realmente deseas hacer.

    Si ese control DataGridView lo tienes enlazado a un objeto DataTable, en el siguiente artículo encontrarás una función para crear un archivo de texto delimitado con los nombres de las columnas que tienen los campos del objeto DataTable:

    Cómo crear un archivo de texto delimitado con los datos existentes en un objeto DataTable

    Pero si los nombres de las columnas del control DataGridView son diferentes a las del objeto DataTable, entonces tendrás que adaptar la función a tus necesidades.

    Y si dicho control no está enlazado a un objeto DataTable, entonces tendrás que recorrer todas las filas del control DataGridView, y por cada fila, tendrás que recorrer las columnas que necesites leer para grabar los datos en el archivo de texto delimitado.

    Mejor será que tengas el control DataGridView enlazado con un objeto DataTable. ;-)

    Un saludo


    Enrique Martínez Montejo
    [MS MVP - Visual Studio y Tecnologías de Desarrollo]

    Nota informativa: La información contenida en este mensaje, así como el código fuente incluido en el mismo, se proporciona «COMO ESTÁ», sin garantías de ninguna clase, y no otorga derecho alguno. Usted asume cualquier riesgo al poner en práctica, utilizar o ejecutar lo recomendado o sugerido en el presente mensaje.

    Si esta respuesta le ha resultado útil, recuerde marcarla como satisfactoria.

    Si usas Visual Basic .NET y deseas ser productivo y feliz, se inteligente y activa la instrucción
    Option Strict.

    domingo, 4 de septiembre de 2016 17:50
    Moderador

Todas las respuestas

  • "XabiT" escribió:

    > Aquí radica el problema: el lector lee perfectamente los parciales almacenados
    > del .txt pero no se cómo hacer para que también pueda almacenar los títulos de
    > las columnas de los parciales, ya que el deportista necesita conocerlos´.

    Hola:

    No habría estado de más que hubieses publicado algo del código fuente que estás ejecutando, mayormente para hacernos una idea de lo que realmente deseas hacer.

    Si ese control DataGridView lo tienes enlazado a un objeto DataTable, en el siguiente artículo encontrarás una función para crear un archivo de texto delimitado con los nombres de las columnas que tienen los campos del objeto DataTable:

    Cómo crear un archivo de texto delimitado con los datos existentes en un objeto DataTable

    Pero si los nombres de las columnas del control DataGridView son diferentes a las del objeto DataTable, entonces tendrás que adaptar la función a tus necesidades.

    Y si dicho control no está enlazado a un objeto DataTable, entonces tendrás que recorrer todas las filas del control DataGridView, y por cada fila, tendrás que recorrer las columnas que necesites leer para grabar los datos en el archivo de texto delimitado.

    Mejor será que tengas el control DataGridView enlazado con un objeto DataTable. ;-)

    Un saludo


    Enrique Martínez Montejo
    [MS MVP - Visual Studio y Tecnologías de Desarrollo]

    Nota informativa: La información contenida en este mensaje, así como el código fuente incluido en el mismo, se proporciona «COMO ESTÁ», sin garantías de ninguna clase, y no otorga derecho alguno. Usted asume cualquier riesgo al poner en práctica, utilizar o ejecutar lo recomendado o sugerido en el presente mensaje.

    Si esta respuesta le ha resultado útil, recuerde marcarla como satisfactoria.

    Si usas Visual Basic .NET y deseas ser productivo y feliz, se inteligente y activa la instrucción
    Option Strict.

    domingo, 4 de septiembre de 2016 17:50
    Moderador
  • Hola de nuevo,

    Gracias por la respuesta. Me parece que estoy en el peor de los escenarios. Lo único que hice fue agregar un control DataGridView al formulario y hacer que este se rellenara a medida que iba recibiendo datos. Pego el fragmento de código que uso para crear el .txt:

     Private Sub SaveFileStripMenuItem_Click(sender As Object, e As EventArgs) Handles SaveFileStripMenuItem.Click
            SaveFileDialog2.Filter = "Text Files | *.txt"
            SaveFileDialog2.DefaultExt = ".txt"
            SaveFileDialog2.ShowDialog()
            Dim _Line As String = Nothing
            Dim sFile As String
            sFile = SaveFileDialog2.FileName
            If File.Exists(sFile) = True Then 'Si el archivo existe, lo elimina antes
                My.Computer.FileSystem.DeleteFile(sFile, FileIO.UIOption.OnlyErrorDialogs,
                                                  FileIO.RecycleOption.DeletePermanently,
                                                  FileIO.UICancelOption.DoNothing)
            End If
            If SaveFileDialog2.FileName <> "" Then
                Dim swFile As StreamWriter = New StreamWriter(sFile)
                With DataGridView1
                    For i = 0 To .RowCount - 1
                        _Line = .Rows(i).Cells(0).Value & ";" &
                            .Rows(i).Cells(1).Value & ";" &
                            .Rows(i).Cells(2).Value & ";" &
                        .Rows(i).Cells(3).Value & ";" &
                        .Rows(i).Cells(4).Value & ";" &
                        .Rows(i).Cells(5).Value & ";" &
                        .Rows(i).Cells(6).Value & ";" &
                        .Rows(i).Cells(7).Value & ";" &
                        .Rows(i).Cells(8).Value & ";" &
                        .Rows(i).Cells(9).Value & ";" &
                        .Rows(i).Cells(10).Value & ";" &
                        .Rows(i).Cells(11).Value & ";" &
                        .Rows(i).Cells(12).Value & ";" &
                        .Rows(i).Cells(13).Value & ";" &
                        .Rows(i).Cells(14).Value & ";"
                        swFile.WriteLine(_Line)
                    Next
                End With
                swFile.Close()
            Else
                MsgBox("Path not selected", MsgBoxStyle.Information, "Path not selected")
            End If
            DataGridView1.Refresh()
        End Sub

    He puesto un máximo de 15 columnas posibles, por si el evento lo exige. Después me encargo de que solo aquellas que tengan contenido sean visibles.

    domingo, 4 de septiembre de 2016 19:56
  • "XabiT" escribió:

    > Me parece que estoy en el peor de los escenarios. Lo único que hice
    > fue agregar un control DataGridView al formulario y hacer que este
    > se rellenara a medida que iba recibiendo datos.

    ¿Y por qué no creas "manualmente" un objeto DataTable para enlazárselo al control DataGridView?

    Si deseas hacer una prueba, inserta en ese formulario los siguientes procedimientos:

        Private Function CreateDataTable()  As DataTable
    
             ' Crear el objeto DataTable llamado TiemposParciales
             '
             Dim dt  As New DataTable("TiemposParciales")
    
             ' Añadir las columnas necesarias
             '
             dt.Columns.Add(New DataColumn("IdDorsal", Type.GetType("System.Int32")))
             dt.Columns.Add(New DataColumn("TiempoParcial", Type.GetType("System.String")))
    
             ' Devolver el objeto DataTable.
             '
             Return dt
    
        End Function
    
        ''' <summary>
        ''' Crea un archivo de texto delimitado con el contenido de
        ''' un objeto DataTable.
        ''' </summary>
        ''' <param name="fileName">Ruta y nombre del archivo de texto.</param>
        ''' <param name="dt">Un objeto DataTable válido.</param>
        ''' <param name="separatorChar">El carácter delimitador de los campos.</param>
        ''' <param name="hdr">Indica si la primera fila contiene el nombre de los campos.</param>
        ''' <param name="textDelimiter">Indica si los campos alfanuméricos deben aparecer
        ''' entre comillas dobles.</param>
        ''' <returns></returns>
        ''' <remarks></remarks>
        Private Shared Sub CreateTextDelimiterFile(fileName As  String, dt As DataTable, _
                                                   separatorChar As Char, hdr  As Boolean, _
                                                   textDelimiter As Boolean)
    
            ' Si no se ha especificado un nombre de archivo,
            ' o el objeto DataTable no es válido, provocamos
            ' una excepción de argumentos no válidos.
            '
            If ((fileName = String.Empty) OrElse (dt Is Nothing)) Then
                Throw New ArgumentException("Argumentos no válidos.")
            End If
    
            Dim col As  Integer = 0
            Dim value As  String = String.Empty
    
            ' Creamos el archivo de texto con la codificación por defecto.
            '
            Using sw As New IO.StreamWriter(fileName, False, System.Text.Encoding.Default)
    
                If (hdr) Then
                    ' La primera línea del archivo de texto contiene
                    ' el nombre de los campos.
                    For Each dc As DataColumn In dt.Columns
    
                        If (textDelimiter) Then
                            ' Incluimos el nombre del campo entre el caracter
                            ' delimitador de texto especificado.
                            '
                            value &= """" & dc.ColumnName & """" & separatorChar
    
                        Else
                            ' No se incluye caracter delimitador de texto alguno.
                            '
                            value &= dc.ColumnName & separatorChar
    
                        End If
                     Next
    
                     sw.WriteLine(value.Remove(value.Length - 1, 1))
                     value = String.Empty
    
                 End If
    
                 ' Recorremos todas las filas del objeto DataTable
                 ' incluido en el conjunto de datos.
                 '
                 For Each dr As DataRow In dt.Rows
    
                     For Each dc As DataColumn In dt.Columns
    
                         If ((dc.DataType Is System.Type.GetType("System.String")) And _
                             (textDelimiter = True)) Then
    
                             ' Incluimos el dato alfanumérico entre el caracter
                             ' delimitador de texto especificado.
                             '
                             value &= """" & dr.Item(col).ToString & """" & separatorChar
    
                         Else
                             ' No se incluye caracter delimitador de texto alguno
                             '
                             value &= dr.Item(col).ToString & separatorChar
    
                         End If
    
                         ' Siguiente columna
                         col += 1
    
                     Next
    
                     ' Al escribir los datos en el archivo, elimino el
                     ' último carácter delimitador de la fila.
                     '
                     sw.WriteLine(value.Remove(value.Length - 1, 1))
                     value = String.Empty
                     col = 0
    
                Next ' Siguiente fila
    
            End Using
    
        End Sub
    
        Private Function GetFileName() As String
    
            Dim fileName As String = String.Empty
    
            Using sfd As New SaveFileDialog()
                sfd.Filter = "Archivos de texto | *.txt"
                sfd.DefaultExt = ".txt"
                Dim dr As DialogResult = sfd.ShowDialog()
                If (dr = DialogResult.OK) Then
                    fileName = sfd.FileName
                End If
            End Using
    
            If ((fileName.Length > 0) AndAlso (IO.File.Exists(sFile))) Then
                ' Si el archivo existe, solicitar confirmación para sobreescribirlo.
                '
                Dim msg As String = "Ya existe un archivo de texto con el mismo nombre." & _
                                    Environment.NewLine & "¿Desea sobrescribirlo?"
    
                If (MessageBox.Show(msg, "Crear archivo de texto delimitado", MessageBoxButtons.YesNo, _
                                    MessageBoxIcon.Information) = DialogResult.Yes) Then
                    ' Eliminar el archivo
                    IO.File.Delete(fileName)
    
                Else
                    ' No se desea sobrescribir el archivo.
                    fileName = String.Empty
    
                End If
    
            End If
    
            Return fileName
    
        End Function

    En el evento Load del formulario, crearías el objeto DataTable y se lo asignarías al control DataGridView:

        Private Sub Form1_Load(sender As Object, e As EventArgs) Handles MyBase.Load
    
            ' Enlazar el control DataGridView con un objeto DataTable
            DataGridView1.DataSource = CreateDataTable()
    
        End Using

    Y cuando desees crear el archivo de texto, ejecutarías lo siguiente:

        Private Sub SaveFileStripMenuItem_Click(sender As Object, e As EventArgs) Handles SaveFileStripMenuItem.Click
    
            Try
                ' Obtener el archivo de texto donde se desean exportar los datos
                '
                Dim sFile As String = GetFileName()
           
                If (sFile = String.Empty) Then
                    ' Se ha cancelado el cuadro de diálogo Guardar cómo;
                    ' abandonar el procedimiento
                    Return
                End If
    
                ' Referenciamos el objeto DataTable enlazado a un control DataGridView
                '
                Dim dt As DataTable = DirectCast(DataGridView1.DataSource, DataTable)
    
                ' Indicamos nuestra intención de crear un archivo de texto delimitado por
                ' puntos y comas, así como que en la primera línea aparezcan los nombres
                ' de las columnas, y que los campos alfanuméricos sean encerrados entre
                ' comillas dobles.
                '
                CreateTextDelimiterFile(sFile, dt, ";"c, True, True)
    
                MessageBox.Show("Se ha creado satisfactoriamente el archivo de texto.")
    
            Catch ex As Exception
                ' Se ha producido un error
                MessageBox.Show(ex.Message)
    
            End Try
    
        End Sub

    > He puesto un máximo de 15 columnas posibles, por si el evento lo exige.

    Fíjate que la función CreateDataTable solamente crea dos columnas, pero que le puedes añadir 15, 20 o la que estimes necesarias. ;-)


    Enrique Martínez Montejo
    [MS MVP - Visual Studio y Tecnologías de Desarrollo]

    Nota informativa: La información contenida en este mensaje, así como el código fuente incluido en el mismo, se proporciona «COMO ESTÁ», sin garantías de ninguna clase, y no otorga derecho alguno. Usted asume cualquier riesgo al poner en práctica, utilizar o ejecutar lo recomendado o sugerido en el presente mensaje.

    Si esta respuesta le ha resultado útil, recuerde marcarla como satisfactoria.

    Si usas Visual Basic .NET y deseas ser productivo y feliz, se inteligente y activa la instrucción
    Option Strict.


    lunes, 5 de septiembre de 2016 7:46
    Moderador
  • Gracias por el código. No estoy muy familiarizado y ayer estuve tratando de entenderlo y estuve probándolo con éxito. Cuando entendí finalmente tu propuesta pensé en que quizá otra opción también sería válida y es la de pasar los datos del DataGridView a un datatable y de ahí al txt. Pero bueno, eso para otra ocasión. Muchas gracias por tu ayuda. 

    Xabi Taberna

    martes, 6 de septiembre de 2016 8:35