Construyendo Reportes Para Aplicaciones Web: Parte 3

Bien, continuando con esta última parte de la serie de de reportes para aplicaciones web, vamos a basarnos en el reporte que ya teníamos antes para hacer una construcción de subreportes o reportes anidados.

En primer lugar, debemos decir que un subreporte no es más que un reporte independiente que es definido y llamado desde dentro de otro reporte. En el diseñador de reportes debemos seleccionar el objeto Subreport y arrastrarlo hasta nuestro reporte.

Hay dos requerimientos importantes que debemos mencionar acerca de los subreportes:

1. La estructura del subreporte debe ser por medio de una tabla (diseño en forma de tabla, no se refiere a que los datos deben provenir de una tabla de base de datos).

2. En el reporte padre solamente se muestra la parte de “Body” del subreporte (el header y footer son ignorados).

Entonces, manos a la obra. Empecemos por definir nuestro nuevo reporte que va a ser el subreporte que llamaremos del reporte que ya teníamos (¿parece trabalenguas verdad?). Para empezar, crearemos un nuevo reporte al que llamaremos DetalleOrdenes.rdlc y le agregaremos un objeto Tabla de la caja de herramientas (toolbox).

DetalleOrdenes_Tabla

Ahora, como hicimos antes, vamos a seleccionar la fuente de datos y utilizaremos la tabla dbo.[Order Details] de la base de datos Northwind. Esto creará un nuevo dataset llamado DataSet2 a donde podremos arrastrar la tabla de la conexión de la base de datos.

DataSet2

Arrastramos ahora los campos ProductID, Quantity y UnitPrice a cada uno de los campos en la tabla de la siguiente forma:

Tabla_Con_Campos

Para efectos del artículo no entraremos en detalles del formato y lo dejaremos tal y como está. Ahora es necesario definir un parámetro para el reporte, el parámetro nos servirá para mostrar solamente aquellos registros del detalle de la orden que correspondan con la orden misma, una especie de reporte maestro-detalle. La definición la hacemos en el menú Report y seleccionando la opción Report Parameters. El parámetro lo llamaremos NoOrden y será de tipo Integer (no olvide chequear el checkbox de la opción Allow Null Value).

Report_Parameters

NoOrden

Ahora seleccionamos la tabla y vamos a las propiedades de la misma seleccionando el Tab de Filters. Esto nos servirá para hacer el filtro del parámetro y que se seleccionen solo los datos que corresponden a la orden.

Propiedades_Tabla

Para la definición del filtro, es necesario indicar que el valor para el campo OrderID debe ser igual al del parámetro NoOrden.

Filter

Bien, eso es todo en el subreporte. Ahora regresamos a nuestro reporte principal y hacemos al objeto List un poco más grande para poder ubicar al objeto Subreport dentro de él:

Subreport

Ahora debemos configurar nuestro objeto Subreport. Indicaremos primero que utilice el reporte DetalleOrdenes.rdlc que acabamos de crear, para ello entramos a las propiedades del subreport y en el Tab General seleccionamos:

Subreport_Subreport

Ahora vamos al Tab Parameters donde le daremos el valor a nuestro parámetro y le decimos que el parámetro NoOrden tomará su valor del campo OrderID:

Subreport_Parameters

Ahora, le diremos al objeto List que solamente nos muestre una orden por página, así podremos tener una mejor visión del efecto del subreporte y veremos claramente la información de la orden. Para lograr esto iremos a las propiedades del objeto List y en el Tab General haremos click en el botón “Edit details group”; en la sección Expression seleccionaremos al campo OrderID y luego seleccionaremos la opción “Page break at end”.

List_PageBreak

Ahora podemos ejecutar el reporte para ver el resultado de todo lo que hemos hecho:

Subreport_Error

La ejecución produce dos resultados, uno esperado, uno inesperado. El resultado esperado es que efectivamente se muestra solamente una orden en cada página. El resultado inesperado es un error al mostrar el subreporte.

Este error sucede debido a que el modo en que funciona el subreporte. Es necesario indicarle la fuente de datos, como hasta este momento solamente hemos definido la fuente de datos para el reporte padre el subreporte no tiene fuente de datos, entonces es necesario definirla, pero no se define en cualquier lado. Para ello es necesario escribir un poco de código.

Primero vamos al archivo de CodeBehind de la página default, Default.aspx.cs, y definiremos un método para realizar el procesamiento del evento SubreportProcessing del objeto LocalReport que ya hemos visto antes. Agregamos al inicio del archivo la siguiente directiva:

using Microsoft.Reporting.WebForms;

 

 

Y ahora escribimos el método:

protected static void
        
LocalReport_SubreportProcessing(object sender,
                                   SubreportProcessingEventArgs e)
{           

 

 

}

 

 

Este método nos servirá como manejador del evento donde diremos que fuente de datos debe usar el subreporte. Observen la estructura, debe ser igual a la mostrada. Ahora indicamos la fuente de datos:

protected static void 
         
LocalReport_SubreportProcessing (object sender,
                           SubreportProcessingEventArgs
e)
{
  
ObjectDataSource ObjectDataSource2 = new ObjectDataSource("Report.Part1.DataSet2TableAdapters.Order_DetailsTableAdapter", "GetData");
  
e.DataSources.Add(new ReportDataSource 
                       ("DataSet2_Order_Details"

                         objectDataSource2));
}

 

 

Pero esto no es todo. Hay que indicarle al evento SubreportProcessing del objeto LocalReport que use ese método. Esto lo logramos agregando la siguiente línea de código al final del evento Page_Load:

ReportViewer1.LocalReport.SubreportProcessing += LocalReport_SubreportProcessing;

Luego de esto podemos ejecutar nuevamente la aplicación y veremos el siguiente resultado:

Subreport_final

Bien, espero les haya gustado y ojalá les sirva mucho para realizar tareas más complejas en sus aplicaciones.

Hasta la próxima.

32 Comments

  • Hola que tal, no se el porque pero hago todo igual al articulo y obtengo el error al mostrar el subreporte.
    como lo demustras antes de poner el codigo por detras.
    Cuales serian otras causas por que se presenta este error.
    Yo tengo mi reportview on el paquete en español, sera que eso puede estar causando el problema?
    Saludos

  • Hola Diego.
    Ese error se presenta cuando por algún motivo el subreporte no tiene forma de obtener los datos o existe algún problema de configuración.
    Principalmente vería si no has olvidado agregar el delegado al reporte. Esta línea es:
    ReportViewer1.LocalReport.SubreportProcessing += LocalReport_SubreportProcessing;
    Espero te sirva.
    Jose Rolando Guay Paz

  • HOLA BUENO SIGO CON LO MISMO NO ME QUERIDO AVANZAR A VER LA PARTE 3 Y ES QUE NO HE PODIDO RESOLVER EL ERROR SIGUENTE DE LA PARTE DE SELECIONAR POR RANGO DE FECHAS


    An error has occurred during report processing.
    El valor del parámetro 'FechaInicial' no se puede convertir de 'System.String' a 'System.DateTime'

    ESPERO ME PUEDAS DAR UNA PISTA GRACIAS

  • Hola Eduardo.

    Pues mira, este error es típico de cuando se intenta convertir de string a fecha pero el formato de la fecha no es el esperado. Por ejemplo: si quieres convertir a fecha el string "03/05/2008" esto tiene dos posibilidades, dependiendo de la configuración regional del servidor de base de datos se puede interpretar como 3 de mayo, o 5 de marzo.

    Sin embargo, para que esto suceda debe estar pasando un caso como convertir a fecha "20/07/2008" y si tu servidor tiene el formato MM/DD/YYYY entonces no puede ser, porque no hay mes 20.

    Tienes que asegurarte de que los formatos de fecha coinciden correctamente para que el error pueda ser controlado.

    Ojalá que esto te ayude.

    Saludos.
    José Rolando Guay Paz

  • Podrías por favor publicar el codigo fuente de la aplicacion o enviarmela a mi mail =) julianochoa@live.com.mx

    gracias de antemano :D

  • El codigo fuente ha sido publicado.

    Saludos.
    Jose Guay

  • hola José, me parece de gran ayuda el artículo sobre todo el esfuerzo de poner las pantallas lo hace mas comprensible, bueno estoy empezando a hacer un reporte similar al del ejemplo y la pregunta es si luego de mostrar el sub reporte ¿se puede generar un salto de página?

    espero puedas ayudarme
    y gracias de antemano
    saludos!!

  • Hola Marco Antonio.

    Bueno, para ello tendrias que agregar un page break en las propiedades del objeto list que contiene al subreporte, no se puede hacer directamente desde el subreporte.

    Cuando abres las propiedades del "list" vas a "Edit Details Group" y luego seleccionas "Page Break At End".

    Saludos.
    Jose Guay

  • hola Jose, gracias por la respuesta me ayudo mucho al menos se que voy por el camino correcto y si me sale el reporte todos estaremos satisfechos, pero me he topado con un problema medio chistoso ... te cuento yo accedo a los datos mediante un servicio web y el detalle es que no puedo acceder a mis objetos desde el

    protected static void LocalReport_SubreportProcessing(object sender, SubreportProcessingEventArgs e)
    {
    }

    mas o menos la idea seria asi dentro de mi evento form_load del formulario que contiene el reporViewer
    con el reporte que contiene al sub reporte

    DataSetGeneral dsGeneral = new DataSetGeneral();
    DatosValidacion.Instancia.ObjetoDatosValidacion = ServicioWeb.funcion(DatosValidacion.Instancia.ObjetoDatosValidacion, var1,var2);
    dsGeneral.tabla1.Merge(DatosValidacion.Instancia.ObjetoDatosValidacion.tabla1);
    tabla1BindingSource.DataSource = dsGeneral.tabla1;
    reportViewer1.LocalReport.SubreportProcessing += LocalReport_SubreportProcessing;
    this.reportViewer1.RefreshReport();

    asi funciona y me carga los datos del reporte principal, "DatosValidacion.Instancia.ObjetoDatosValidacion" es mi contenedor de datos, como veras envio parametros cuando invoco una funcion de mi servicioweb (variables), weno hasta ahi todo funciona el detalle es que dentro del evento protected static void LocalReport_SubreportProcessing(object sender, SubreportProcessingEventArgs e) no puedo acceder a ninguna de estas variables ni objetos ni nada, derrepente tengas algun link o mayor ayuda para llenar datos como tu lo hiciste dentro de este evento pero con mi forma de trabajo ( ObjectDataSource ObjectDataSource2 =
    new ObjectDataSource("Report.Part1.DataSet2TableAdapters.Order_DetailsTableAdapter", "GetData");
    e.DataSources.Add(new ReportDataSource("DataSet2_Order_Details", ObjectDataSource2));)

    weno es todo un rollo ojala me salga ps, toy tras de eso creo el problema es hacer un merge dentro de este evento y enviar parametros a mi servicio web y como te dije soy nuevo en esto, neófito entonces de una u otra forma debo arreglarmela, jose de antemano gracias por tu ayuda
    un abrazo !

  • Hola Marco Antonio.

    Mira, si tu servicio web te devuelve un dataset entonces puedes trabajarlo de la misma manera, solo que haciendo referencia al dataset que obtienes.

    Ahora, el problema que experimentas de no tener acceso a las variables y todo eso, es porque LocalReport_SubreportProcessing esta definido como "static". Si le quitas el modificador static, debe funcionar bien.

    Saludos.
    Jose Guay

  • hola Jose, nuevamente felicitarte por tus aportes que me han sido de gran utilidad, ya logre hacer el reporte, estuve recontra ocupado con eso y logre hacer la conexion etc etc. mas o menos asi

    private void frmImpresiones_Load(object sender, EventArgs e)
    {
    DatosValidacion.Instancia.ObjetoDatosValidacion = servicio.llenaTabla1(DatosValidacion.Instancia.ObjetoDatosValidacion, "string1");
    dsGeneral.tabla1.Merge(DatosValidacion.Instancia.ObjetoDatosValidacion.tabla1Table);
    tabla1BindingSource.DataSource = dsGeneral.tabla1; reportViewer1.LocalReport.SubreportProcessing += LocalReport_SubreportProcessing;

    this.reportViewer1.RefreshReport();
    }

    protected void LocalReport_SubreportProcessing(object sender, SubreportProcessingEventArgs e)
    {
    DatosValidacion.Instancia.ObjetoDatosValidacion = servicio.llenaTabla2(DatosValidacion.Instancia.ObjetoDatosValidacion, "string", Convert.ToDateTime(fecha1), Convert.ToDateTime(fecha2));
    dsGeneral.tabla2(DatosValidacion.Instancia.ObjetoDatosValidacion.tabla2);
    e.DataSources.Add(new ReportDataSource("DSGeneral_tabla2", dsGeneral.tabla2));
    }

    y funciono sim embargo notese que la funcion RefreshReport() fue importante y que evento SubreportProcessing fue donde llene la tabla enviando parametros y en el evento load cargue los datos asignando un dataSource al bindingSource solo de esa manera funciono, en todo caso espero estito pueda servir de algo para aquellos nuevos que se estan iniciando con esto = que yo.
    Ahora ya estoy peleando con el formato del reporte, y una pregunta ¿cómo podria hacer para que los titulos de la tabla del subreporte se repita cuando hay un cambio de pagina en caso de que los datos sobrepasan una pagina? algo asi como en excel filas que se repiten en cada página, lo del encabezado de página es otra cosa.
    muy agradecido
    saludos

  • Hola Hassan.

    Veo un poco complicado lo que propones, creo que no es posible ya que en los subreportes no puedes incluir ni header ni footer, solamente body.

    Ahora, lo que si puedes hacer es un subreporte con dos partes en el body, una que simule el header y otra que sea el body en si.

    En la que simula el header, le pones una tabla o un objeto como lista o algo asi, y utilizas un codigo como el siguiente en la propiedad "Visibility" para saber que paginas son pares y cuales son impares (frontales y anversas) para hacer que el objeto se muestre o no:

    =IIf(RowNumber("list1") mod 2, True, False)

    En este caso "list1" es el Scope o contenedor de los datos.

    Espero que te ayude un poco.

    Jose Guay

  • Buenas;

    Tengo un problema al momento de integrar un reporte.
    El caso es el siguiente. He diseñado un informe de estilo matriz y otro de estilo tabla.

    He realizado todo lo que he encontrado en cuanto foro y el resultado es el mismo "Error: no se puede mostrar el informe integrado"

    El codigo que cree es de la siguiente manera...
    ------------------------------------------------------

    Imports Microsoft.Reporting.WebForms
    Partial Public Class Informe
    Inherits System.Web.UI.Page
    Dim dtInforme As DataTable = Nothing
    Public Sub LocalReport_SubreportProcessing(ByVal sender As Object, ByVal e As SubreportProcessingEventArgs)
    Try
    'Dim ds As New dsMatriz
    'If dtInforme Is Nothing Then
    ' dtInforme = ds.Tables("dsBolsa")
    'End If
    'e.DataSources.Add(New ReportDataSource("DataSet1_OrderDetails", dtInforme))

    Dim ObjectDataSource2 As New ObjectDataSource("Report.Part1.DataSet2TableAdapters.Order_DetailsTableAdapter", "GetData")
    e.DataSources.Add(New ReportDataSource("DataSet2_Order_Details", ObjectDataSource2))
    Catch ex As Exception
    Dim x As String
    x = ex.Message
    End Try
    End Sub

    Protected Sub Page_Load(ByVal sender As Object, ByVal e As EventArgs) Handles Me.Load
    Try
    Dim ds As New dsMatriz
    'ReportViewer1 = New Microsoft.Reporting.WebForms.ReportViewer
    'ReportViewer1.ProcessingMode = ProcessingMode.Local
    'AddHandler ReportViewer1.LocalReport.SubreportProcessing, AddressOf LocalReport_SubreportProcessing
    'ReportViewer1.LocalReport.DataSources.Add(New ReportDataSource("DataSet2_Orders", ds.Tables("dsMatriz")))

    'AddHandler ReportViewer1.LocalReport.SubreportProcessing, AddressOf LocalReport_SubreportProcessing
    ReportViewer1.LocalReport.DataSources.Add(New ReportDataSource("dsBolsa", ds.Tables("dsBolsa")))
    Catch ex As Exception
    Dim x As String
    x = ex.Message
    End Try
    End Sub
    End Class

    ------------------------------------------------------
    He puesto para que me den alguna luz o por el contrario me puedan corregir y puntualizar mi error.

    Agradezco su colaboracion prestada.

  • @Cristian - Veo que tienes tu declaracion del evento LocalReport.SubreportProcessing comentada, sin esto el subreporte no sabra que hay un delegate esperando por ejecutarse y no se llenara el datasource.

    'AddHandler ReportViewer1.LocalReport.SubreportProcessing, AddressOf LocalReport_SubreportProcessing

  • Jose Gracias; Lo he descomentariado y me sigue arrojando el mismo resultado. Realmente no se que mas utilizar. Todo lo que se encuentra comentariado en el codigo es lo que he intentado realizar para que funcione y no ha sido posible.
    No se si el problema radique en otra cosa. Porque pongo puntos de interruncion en el load y en la rutina llamada LocalReport_SubreportProcessing pero no pasan por ahi.

    Cualquir cosa, mis email de contacto son los siguientes
    cristian.cadena@reval.com.co o titiritou@hotmail.com

    Nuevamente agradezco la colaboracion prestada

  • Ayuda jose me marca error en lo siguiente en los signos += y en el ;

    ReportViewer1.LocalReport.SubreportProcessing += LocalReport_SubreportProcessing;

    yenlav19@hotmail.com

  • @Yenni: Cual es el error que te marca?

  • Hola Jose.

    Como se le mandan parametros al subreporte ya que ese trae los datos con un store y necesita parametros.

    porque ya hice todo lo que haces en el ejemplo pero me sigue marcando el error de que no se puede mostrar el subreporte.

    Gracias

  • Saludos...
    Muy buenos los artículos sobre subreportes...
    Espero q me puedas ayudar:

    Tengo un reporte (Ficha de una persona) la cual contiene 10 subreportes relacionados a dicha persona(las direcciones en las q vivio, su historial crediticio, etc) Todo funciona muy bien.

    Lo q necesito ahora es poder desde una aplicación VB elegir q subreportes se van a imprimir.

    Existe forma de indicar desde código q rubreportes van en el reporte principal??? O de eliminar subreportes del reporte principal??

  • @hapari: creo que la unica forma de lograr eso es enviando un parametro por cada subreporte donde el parametro indicara si el subreporte es visible o no.

    Saludos.
    Jose Guay

  • Buenos días. alguien me puede colaborar con lo siguiente: tengo ya un informe construido a partir de un dataset. que me retorna por ejemplo (nombre, direccion, tel, ciudad, pais, edad)

    actualmente el informe me muestra todo en 1 fila x 6 columnas

    la idea es crear una especie de formato de levantamiento de información (como una encuesta ) el encabezado se construye con ese dataset de manera que me aparezca la información en 3 columnas x 2 filas :

    -------------------------------------------------
    nombre | direccion | tel |
    -------------------------------------------------
    ciudad | pais | edad
    -------------------------------------------------

  • mUCHAS GRACIAS POR TUS APORTES... ME HAN SERVIDO DE MUCHO...

  • Gracias........
    funciona perfectamente gracias al post
    "Construyendo Reportes Para Aplicaciones Web: Parte 3"

    si necesitan alguna cosita
    me escriben a tabcast@yahoo.es

  • Hola a todos porfa necesito saber como hacer un reporte con xtrareport y .net, pero utilizando filtros si alguien me puede ayudar se lo agradecer mucho.

  • @Silvi. En el website de DevExpress puedes encontrar muchos ejemplos. Aqui http://demos.devexpress.com/XtraReportsDemos/ReportTypes/TableReport.aspx hay uno para hacer un reporte simple que pasa un parametro para filtrar la informacion.

    Espero te sirva.

    Jose

  • Muchas gracias José, ya lo logré.
    Ahora necesito hacer subreportes con xtrareport si alguien me puede ayudar gracias.

  • Gracias Jose Rolando ya lo logre,
    ahora necesito hacer reportes utilizando xtrareport en . net si me pueden ayudar.
    gracias.

  • Muy buenas a todos. Muchas gracias por todos los aportes. Excelente material. Me ha sido muy útil para obtener reportes de buena calidad dentro de un proyecto que estoy desarrollando. No obstante no he conseguido que aparezca el icono de imprimir directamente sin necesidad de exportar el reporte. Conmigo son cuatro las personas que han expuesto el mismo caso, pero aun no hay claridad al respecto. Estoy usando el IEXPLORE 8.0, pero aun así, nada.

    :-S

  • Amigos me encuentro con el mismo error de EDUARDO (Al inicio). Me aparece el siguiente error:

    Error al procesar el informe.
    El valor del parámetro 'Fecha1' no se puede convertir de 'System.String' a 'System.DateTime'

    Ya intente convertir los parametros desde la consulta sql asi:

    WHERE fecha BETWEEN CONVERT(DATETIME, @Fecha1, 103) and CONVERT(DATETIME, @Fecha2, 103), e incluso a varios formatos y nada. Sigue apareciento. Me ayudan porfavor. es demasiado urgente!!! GRACIAS... (y)

  • Hola Edwin. Sólo debes agregar el argumento System.TypeCode.DateTime al momento de agregar el parámetro de fecha al ObjectDataSource que definiste... así:

    ObjectDataSource1.SelectParameters.Add("FECHA_FINAL", System.TypeCode.DateTime, this.TextBoxFechaFinal.Text);

    ...y listo. Éxitos y saludos desde Santa Marta, Colombia.

  • Una duda,es posible definir un numero maximo de filas para mi tablita. Lo que pasa es que necesito colocar solo 10 filas en la tabla,pero en el reporte cuando aparecen menos de 10, entre 4,5 o 6 la tablita se acopla al contenido y yo quisiera que me respetara un alto. O no se si pueda especificar que me muestre 10 filas mi tablita y si con una consulta me retornan menos de 10,las demas filas que me sigan aparececiendo, osea ya nada mas 4 filas en blanco, se podrà?

  • An excelent tutorial, amazing, I was dealing with the crappy subreports for days. Now I have an idea and I need to turn that C# code to VB.Net.

    I have a question. Is it possible to use a graphic with the reports and like double click a bar of the graphic and go to another report?.

Comments have been disabled for this content.