Mostrando entradas con la etiqueta DataTable. Mostrar todas las entradas
Mostrando entradas con la etiqueta DataTable. Mostrar todas las entradas

Llenar un DataGridView a partir de un SP (procedimiento almacenado) de una base de datos [Versión C#]

Problema: Me gustó la entrada anterior que explicaba cómo llenar un grid, pero ahora lo necesito en C#.
Solución: El nuevo ejemplo en C#

Para hacerlo un poco más real, ésta vez utilicé una base de datos existente y que Microsoft lo suele regalar como ejemplo.
La base de datos es pubs y el SP que escogí para este ejemplo es byroyalty.

Creé un formulario llamado Form1, el cual contiene un DataGridView llamado DataGridView1.

Luego, se escribe el procedimiento para llenar el grid.

private void Llenar_DataGridView()
{
    //Los argumentos de conexion a la base de datos
    string args="Data source=LAPTOP; Initial catalog=pubs; Integrated Security=SSPI";
    SqlConnection conn = new SqlConnection();
    conn.ConnectionString = args;

    try
    {
        //Indico el SP que voy a utilizar
        SqlCommand command = new SqlCommand("byroyalty", conn);
        command.CommandType = CommandType.StoredProcedure;
               
        SqlDataAdapter adapter = new SqlDataAdapter(command);

        //Envió los parámetros que necesito
        SqlParameter param = new SqlParameter("@percentage", SqlDbType.Int);
        param.Value = 100;
        command.Parameters.Add(param);

        DataTable dt = new DataTable();

        conn.Open();

        //Aquí ejecuto el SP y lo lleno en el DataTable
        adapter.Fill(dt);

        //Enlazo mis datos obtenidos en el DataTable con el grid
        dataGridView1.DataSource = dt;

    }
    catch (Exception ex)
    {
        MessageBox.Show(ex.Message);
    }
}

Este procedimiento lo llamo desde el load de la aplicación.
private void Form1_Load(object sender, EventArgs e)
{
    Llenar_DataGridView();
}

Listo, con esto queda explicado el mismo ejemplo que el anterior.

Si desean un video tutorial paso a paso, pueden utilizar el siguiente enlace.


Saludos a todos.

Llenar un DataGridView a partir de un SP (procedimiento almacenado) de una base de datos [Versión VB.Net]

Problema: Mi amada Alondra me ha hecho ver que no tengo una entrada que explique directamente cómo llenar un DataGridView desde un select o un SP (procedimiento almacenado), sino que lo tengo enredado en diferentes entradas, así que para esté contenta, explicaré paso a paso como hacerlo.
Solución: La que explico a continuación.

Necesito llenar mi DataGridView con los datos que tengo en diferentes tablas de mi base de datos, por lo que el select es un poquito complejo y no puedo crearlo directamente con un DataTable tipado, así que tengo que hacerlo por código.

Voy a suponer que mi select sería algo así:

select campo1, campo2, campo3
from tablaA, tablaB
where relacionA1 = relacionB2

Obviamente por la complejidad de mi select, decido ponerlo en un SP, el cual por ejemplo, me quedaría así:

create procedure sp_prueba
        @variable int
as

select campo1, campo2, campo3
from tablaA, tablaB
where relacionA1 = relacionB2
and restriccionB1 = @variable

GO

Esa fue la parte que tengo que realizar en mi base de datos, ahora la parte del Visual Basic .Net.

Tengo mi formulario creado llamado Form1, el cual contiene un DataGridView llamado DataGridView1 junto con los campos necesarios creados en modo diseño.

Ahora creo un procedimiento para llenar este grid.

Private Sub Llenar_DataGridView()
        'Los argumentos de conexión a la base de datos 
        Dim args As String = "Data Source=;" & _ 
                "Initial Catalog=;Integrated Security=SSPI"

        'Abro la conexión
        Using connection As SqlConnection = New SqlConnection(args)
                Dim command As SqlCommand
                Dim adapter As SqlDataAdapter
                Dim dtTable As DataTable
               
                'Indico el SP que voy a utilizar
                command = New SqlCommand("sp_prueba", connection)
                command.CommandType = CommandType.StoredProcedure
                adapter = New SqlDataAdapter(command)
                dtTable = New DataTable
                With command.Parameters
                        'Envió los parámetros que necesito
                        .Add(New SqlParameter("@variable", SqlDbType.Int)).Value = valor
                End With
                Try
                        'Aquí ejecuto el SP y lo lleno en el DataTable
                        adapter.Fill(dtTable)
                        'Enlazo mis datos obtenidos en el DataTable con el grid
                        DataGridView1.DataSource = dtTable
                        'Si no pongo esta línea, se crean automáticamente los campos del grid dependiendo de los campos del DataTable
                        DataGridView1.AutoGenerateColumns = False
                        'Aquí le indico cuales campos del select de mi SP van con los campos de mi grid
                        With DataGridView1
                                .Columns("Campo1").DataPropertyName = "campo1"
                                .Columns("Campo2").DataPropertyName = "campo2"
                                .Columns("Campo3").DataPropertyName = "campo3"
                        End With
                Catch expSQL As SqlException
                        MsgBox(expSQL.ToString, MsgBoxStyle.OkOnly, "SQL Exception")
                        Exit Sub
                End Try
        End Using
End Sub

Este procedimiento lo llamo desde el load de la aplicación.

Private Sub Form1_Load(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles MyBase.Load
        Llenar_DataGridView()
End Sub

Y listo, con esto, los datos de mi select se mostrarán en el grid de mi aplicación.

¿Quedó claro mi linda Alondra? Muchos besitos para ti.

Problema con SubInformes y dos DataTables en Crystal Reports

Problema: Tengo dos reportes, creados por separado, por ejemplo, informe1 e informe2, pero por varias necesidades tuve que poner el informe2 dentro del informe1 como un subreporte, pero, ¡HORROR!, el informe2 no me muestra ningún dato al momento de mostrarlo.
Solución: Revisar la manera en que se envían los datos a los diferentes informes.

El informe1 se llenan con parámetros de campos fórmula que envío desde mi formulario, el informe2 se llena con un DataTable que contiene la información necesaria. ¿Cuál es el error? Que el informe1 no contiene el DataTable del informe2 y por eso no se muestra la información.

Desgraciadamente, el informe1 o el informe principal debe contener TODOS los DataTables de los otros subinformes (por si tengo más de uno), porque del informe principal se envía los datos a los informes secundarios o subreportes. Solo hay que tener un poquito de cuidado al tratar con la información de los diferentes DataTables, sobre todo si los nombres son iguales.

Recomendación: Usar DataTables con nombres diferentes para cada reporte, porque si no se puede caer en inconsistencias.

De allí, el enviar la información a los diferentes reportes y subinformes es de lo más sencillo, como lo indico en el siguiente ejemplo.

Private Sub Cargar_Rep_Sub()
        'CrystalReport1 es el informe1 (el principal)
        Dim rdInforme As New CrystalReport1
        'El informe principal solo recibe campos fórmulas
        rdInforme.DataDefinition.FormulaFields("campo1").Text = Label1.Text
        rdInforme.DataDefinition.FormulaFields("campo2").Text = Text1.Text
       
        'Anteriormente, en informe1.rpt tuve que añadir el dataTable del informe2.rpt
        'Aquí se envía los datos al informe principal
        rdInforme.SetDataSource(dataTable)
       
        'Asigno el visor al reporte
        Form1.CrystalReportViewer1.ReportSource = rdInforme
End Sub

¿Pero, qué pasa si tengo dos o más dataTables? ¿Cómo envío dos dataTables a un mismo reporte?

Para ello se utiliza un dataSet tipo con los dataTables creados anteriormente, como lo explico en otra sección, la diferencia radica en la forma en que se llenan estas dataTables.

Generalmente, para llenar mis datos que obtengo de diferente lugares (por ejemplo un SP o un select), lo realizo de esta manera.

Try
        Dim dtTabla as New DataTable
        adapter.Fill(dtTabla)
Catch expSQL As SqlException
        MsgBox(expSQL.ToString, MsgBoxStyle.OkOnly, "SQL Exception")
        Exit Sub
End Try

Pero para llenar las tablas del dataSet tipo se realiza así.

Try
        'dtTabla es el nombre de la tabla que se encuentra dentro del dataSet tipo
        adapter.Fill(dataSet1, "dtTabla")
Catch expSQL As SqlException
        MsgBox(expSQL.ToString, MsgBoxStyle.OkOnly, "SQL Exception")
        Exit Sub
End Try

Luego, para enviar los datos al Crystal Report se asigna el dataSet.

Private Sub Cargar_Reporte()
        'CrystalReport1 es el informe1 (el principal)
        Dim rdInforme As New CrystalReport1
       
        'Aquí se envía los datos al informe principal
        rdInforme.SetDataSource(dataSet1)
       
        'Asigno el visor al reporte
        Form1.CrystalReportViewer1.ReportSource = rdInforme
End Sub

Crear un informe Crystal Report a partir de un DataGridView

Problema: Tengo mi aplicación lista y funcionando, ahora solo me falta imprimir los informes o reportes necesarios, pero uno de ellos debe mostrar lo que tengo en un grid.
Solución: Una mezcla de DataSet, DataTable y paciencia.

Explicaré paso a paso lo que descubrí e implementé.

No solía utilizar los datasets "tipados" como suele llamarlos, pues las tablas las creaba directamente por código con DataTables.

¿Qué es un dataset tipado? Pues un archivo DataSet.xsd que se añade al proyecto.

¿Y para qué lo necesito? Para añadir los campos al reporte que crearé.

¿Es necesario crear un DataSet para crear mi reporte? Desgraciadamente, sí. No he encontrado otra forma de hacerlo. Bueno, existen muchas maneras, pero esta es la más sencilla para utilizar los datos de mi DataGridView.

¿Para qué necesito un DataSet si mis datos los tengo en un DataTable (que obviamente muestro en un grid)? Para poner los campos en el reporte, no se puede anexar directamente los campos de un DataTable creado desde código.

¿Cómo lo hago? Click derecho en el Explorador de Soluciones en el nombre del proyecto, se escoge Agregar, luego "Nuevo elemento" y se abre la ventana de "Agregar nuevo elemento" o también en el menú Proyecto - Agregar nuevo elemento...

En la lista de "Categorías" escogemos la sección "Datos" o navegamos hasta encontrar una cosita llamada "Conjunto de Datos", en cuya explicación indica que es un DataSet, le doy Agregar y se abre una ventana en gris que indica que se pueden añadir tablas.

¿Cómo lleno el DataSet con mis datos del DataTable? Pues, directamente desde el diseñador, no se puede, si es que el DataTable se llena por código, como en mi caso, que el DataTable lo lleno con los datos que muestro en un grid, lo que podemos hacer es anexar después, como lo explico más adelante.

Necesito, de ley, una DataTable dentro del DataSet, para poder añadir los campos en el reporte, así que, con paciencia, agrego un dataTable con los campos que necesito.

¿Cómo creo un dataTable dentro del dataset? Una vez abierto el diseñador del dataset (una pantalla en gris), se da clic derecho en cualquier sitio, en el menú despegable escoger Agregar - DataTable.

Esto creará un cuadradito azul con gris de nombre "DataTable1", al dar clic allí se podrá cambiar el nombre de la tabla (sí, ese es el nombre del dataTable), luego, para añadir los campos, se da clic derecho en el área gris del dataTable (justo debajo del nombre), en el menú que aparece escoger Agregar - Columna, y aparecerá un rectángulo blanco con el nombre "DataColumn1", al dar clic allí se podrá cambiar el nombre de la columna.  Para añadir más columnas repetir los pasos anteriores y listo.

OJO: De ser posible, poner el mismo nombre del DataTable con que se llena el grid y los mismos campos. O sea, si en mi grid lleno los datos con un DataTable llamado dtTabla con los campos c1, c2 y c3, esto mismo lo pongo en el DataSet.

¿Cómo lleno el DataSet con la información que tengo en mi grid? Como recordaremos, ya tengo un DataSet el cual contiene los datos que se muestran en mi grid, y por lo tanto, tengo un procedimiento, Sub, Function o lugar donde están siendo llenados, lo único necesario de hacer es llenar también el DataSet tipado en el mismo lugar.

Un ejemplo, tengo mi proceso que lleno el DataTable el cual está enlazado a mi grid, y le añadí el DataSet.

Private Sub Llenar_Grid_y_Reporte(ByVal cotizacion_codigo As Integer)
        'Obtengo los datos
        Using connection As SqlConnection = New SqlConnection(args) 'Previamente tendremos nuestra variable de conexión
                command = New SqlCommand("sp_llena_grid", connection)
                command.CommandType = CommandType.StoredProcedure
                adapter = New SqlDataAdapter(command)
                With command.Parameters
                        .Add(New SqlParameter("@var1", SqlDbType.Int)).Value = var1
                End With
                Try
                        adapter.Fill(dtTablaGrid) 'Este DataTable es que utilizo para llenar el grid
                        adapter.Fill(dataSet1, "dtTablaReporte") 'Este es el DataSet tipado que añadí
                Catch expSQL As SqlException
                        MsgBox(expSQL.ToString, MsgBoxStyle.OkOnly, "SQL Exception")
                        Exit Sub
                End Try
        End Using
        
        'Lleno el grid
        DataGridView1.AutoGenerateColumns = False
        DataGridView1.DataSource = dtTablaGrid
        With DataGridView1
                .Columns("col1").DataPropertyName = "col1"
                .Columns("col2").DataPropertyName = "col2"
        End With
End Sub

Bien, tengo mi DataSet con mi DataTable, ahora tengo que crear el reporte.

Agrego un nuevo elemento al proyecto, pero esta vez busco la categoría Reporte y escojo el que dice Crystal Reports.

Luego aparecerá una ventana para crear un nuevo documento de Crystal Report, el cual está seleccionado Usar asistente para informes, de momento escojo ese por ser el más fácil. Abajo escojo "Estandar" y doy click en Aceptar. Mi reporte se llamará CrystalReport1.rpt.

Expandir las carpetas "Datos del proyecto" y la "ADO.NET DataSets", allí debe encontrarse el DataSet que creé anteriormente junto con la DataTable también creada, la añado y continúo con el asistente.

El asistente indicará los campos que deseo añadir al reporte, el modo de agrupamiento, si deseo un subconjunto de datos e infinidad de cosas por el estilo, pero como yo deseo ver mi reporte ya, solo le doy click en Finalizar. No me preocupo, porque luego se puede añadir todo lo que deseo o se me haya olvidado.

Una vez abierto el informe en modo diseño, puedo revisar como se visualiza desde la lengüeta "Vista previa del Informe", pero me mostrará datos ficticios o en el peor de los casos, ningún dato.

Tengo mi archivo del reporte, ahora necesito un "visor" de dicho informe, esto es, un formulario que muestre el informe que he creado. Para ello, agrego otro Windows Forms, el cual llamo Form1, y que le doy el tamaño y la personalización que quiero, al final, desde el "Cuadro de herramientas", en la sección Informe, escojo un "CrystalReportViewer" que lo llamo CrystalReportViewer1 y lo añado al formulario. El viewer o visor se puede personalizar a gusto. No es necesario anexarle un reporte, pues esto lo hago más adelante.

Bien, tengo mi formulario que contiene el visor, el reporte Crystal Report, mi DataSet con una DataTable, ahora queda unirlo todo. Desde el formulario en que tengo mi grid, tengo además, un botón que dice Imprimir, el cual mostrará el reporte o informe con los datos de mi grid. Para ello creé un proceso que junta todo y que se llama desde el botón Imprimir.

Private Sub Imprimir_Click(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles Imprimir.Click
        'Invoco al procedimiento que llena los datos del reporte
        Cargar_Crystal()
        'Abro el formulario que contiene el reporte
        Form1.ShowDialog()
End Sub
Private Sub Cargar_Crystal()
        'El CrystalReport1 es el nombre de mi archivo reporte CrystalReport1.rpt
        Dim rdInforme As New CrystalReport1
        'El dtTabla es mi DataTable que contiene los datos del DataGridView llenado anteriormente
        rdInforme.SetDataSource(dtTabla)
        'El Form1 es el formulario que contiene el visor llamado CrystalReportViewer1
        Form1.CrystalReportViewer1.ReportSource = rdInforme
End Sub

Si todo sale bien (y espero que así sea) se mostrarán los datos de los campos añadidos en el reporte. Recuerda que los nombres son importantes, los DataTables del grid y del reporte (DataSet) deben llamarse iguales, con todo y mayúsculas, así mismo con los campos.

Nota: Esto está creado en Microsoft Visual Basic 2008 y el Crystal Reports Basic para Visual Studio 2008.

Mover fila en el DataGridView arriba y abajo con botón DataGridViewButtonColumn

Problema: Tengo un grid con dos columnas tipo botón, un botón indica subir una fila y el otro botón indica bajar una fila. Debo mover las filas de arriba hacia abajo dependiendo del clic en su respectivo botón.
Solución: Después de buscar por un buen rato en el Internet, no encontré una forma directa, así que tuve que realizar mi propia opción.

Nota: Mi DataGridView está enlazado a un BindingSource el cual está enlazado a un DataTable (que enredo), lo que significa que los datos iniciales (los que se presentan en el grid) son fueron llenados originariamente en el DataTable, por lo que debo modificar directamente el DataTable.

La acción de mover no es otra cosa que eliminar la fila actual e insertarla una posición arriba o abajo de la que se encontraba originariamente.

'Debo obtener la acción clic de la columna botón
Private Sub DataGridView1_CellContentClick(ByVal sender As System.Object, ByVal e As System.Windows.Forms.DataGridViewCellEventArgs) Handles DataGridView1.CellContentClick
       'Sirve para obtener el índice de la fila a mover
        Dim indice As Integer
       'Se crea una nueva fila a partir de la DataTable que está enlazada al grid
        Dim dr As DataRow = DataTable.NewRow()
        'Obtiene la posición actual de la fila a moverse
        indice = DataGridView1.CurrentRow.Index
        'Pregunto si se dio clic en la columna botón arriba
        If DataGridView1.Columns(e.ColumnIndex).Name = "arriba" Then
                'Se encuentra en la parte superior del grid
                If indice = 0 Then
                        Exit Sub
                End If
                'Se deben de insertar los datos en la fila, campo x campo
                dr(0) = DataGridView1.Rows(indice).Cells("campo0").Value
                dr(1) = DataGridView1.Rows(indice).Cells("campo1").Value
                'Primero se elimina la fila actual
                DataTable.Rows.RemoveAt(indice)
                'Después se inserta la fila copiada una posición arriba
                DataTable.Rows.InsertAt(dr, indice - 1)
                'Posiciona el foco en la posición actual
                DataTable.CurrentCell = DataTable.Rows(indice - 1).Cells(0)
        End If
        'Se repiten los mismos pasos para la columna botón abajo
        'Solo que se cambia el signo
        'Click en la columna botón abajo
        If DataGridView1.Columns(e.ColumnIndex).Name = "abajo" Then
                'Se encuentra en la parte inferior del grid
                If indice = DataGridView1.Rows.Count - 1 Then
                        Exit Sub
                End If
                'Se deben de insertar los datos en la fila, campo x campo
                dr(0) = DataGridView1.Rows(indice).Cells("campo0").Value
                dr(1) = DataGridView1.Rows(indice).Cells("campo1").Value
                'Primero se elimina la fila actual
                DataTable.Rows.RemoveAt(indice)
                'Después se inserta la fila copiada una posición abajo
                DataTable.Rows.InsertAt(dr, indice + 1)
                'Posiciona el foco en la posición actual
                DataTable.CurrentCell = DataTable.Rows(indice + 1).Cells(0)
        End If
End Sub

No se necesita actualizar el BindingSource o el DataGridView, si están enlazados al DataTable, puesto que se hace automáticamente.

Llenar un TreeView de forma recursiva

Problema: Llenar un TreeView con nodos hijos desde una base de datos
Solución: Usar un método recursivo como se explica a continuación.

Agradezco mucho a estas páginas por guiarme en la manera de llenar un treeview de forma sencilla Llenar un TreeView desde un DataSet usando un método recurrente C#Llenar un TreeView desde un DataSet en VB .NET

Pero como siempre, tuve que cambiar un poco el código para que se adaptara a mis necesidades, por lo que lo resumo aquí.

Mi cambio en sí, es que los datos los traigo de un SP y los lleno en un DataTable y no en un dataSet Ejemplo.

'Variable usada en todos los procesos
Private dtTabla As New DataTable 'Creo una nueva tabla
'Creo mi propio proceso para obtener los datos del SP
Private Sub CargarArbolTreeView()
        Dim adapter As New SqlDataAdapter
       
        Using connection As SqlConnection = New SqlConnection(args) 'La variable arg contiene la cadena de conexion
                Dim command As New SqlCommand("sp_ejemplo", connection) 'Llamo al SP
                command.CommandType = CommandType.StoredProcedure 'Indico que es un SP
                adapter = New SqlDataAdapter(command) 'Lo cargo al adapter              
                dtTabla = New DataTable 'Creo una nueva tabla
                Try
                        adapter.Fill(dtTabla) 'Lleno mis datos del SP al dataTable
                        CrearNodosDelPadre(0, Nothing) 'Llamo al proceso que llena el treeview
                Catch expSQL As SqlException
                        MsgBox(expSQL.ToString, MsgBoxStyle.OkOnly, "SQL Exception")
                End Try
        End Using
End Sub

Mi proceso CrearNodosDelPadre es casi igual a lo explicado en las páginas de referencia, solo cambia la manera de utilizar el DataView.

Private Sub CrearNodosDelPadre(ByVal indicePadre As Integer, ByVal nodePadre As TreeNode)
        Dim dataViewHijos As DataView
       
        'Crear un DataView con los Nodos que dependen del Nodo padre pasado como parámetro
        dataViewHijos = New DataView(dtTabla)
       
        'Filtra por cada padre
        dataViewHijos.RowFilter = dtTabla.Columns("IdentificadorPadre").ColumnName + _
        " = " + indicePadre.ToString()
               
        ' Agregar al TreeView los nodos Hijos que se han obtenido en el DataView.
        For Each dataRowCurrent As DataRowView In dataViewHijos
                Dim nuevoNodo As New TreeNode
               
                'Descripción o texto del nodo
                nuevoNodo.Text = dataRowCurrent("NombreNodo").ToString().Trim()
               
                'Si necesito guardar el valor del IdentificadorNodo dentro del mismo nodo
                nuevoNodo.Name = dataRowCurrent("IdentificadorNodo").ToString().Trim()
               
                'Si necesito guardar el valor del IdentificadorPadre dentro del mismo nodo
                nuevoNodo.Tag = dataRowCurrent("IdentificadorPadre").ToString().Trim()
                             
                'Si el parámetro nodoPadre es nulo es porque es la primera llamada, son los Nodos
                'del primer nivel que no dependen de otro nodo.
                If nodePadre Is Nothing Then
                        treeview1.Nodes.Add(nuevoNodo)
                Else
                        'se añade el nuevo nodo al nodo padre.
                        nodePadre.Nodes.Add(nuevoNodo)
                End If

                'Llamada recurrente al mismo método para agregar los Hijos del Nodo recién agregado.
                CrearNodosDelPadre(Int32.Parse(dataRowCurrent("IdentificadorNodo").ToString()), nuevoNodo)
        Next dataRowCurrent
End Sub       

Si falta más explicación detallada, revisar las páginas de referencias que indico al principio.