programando el datareport
Post on 22-Apr-2015
114 Views
Preview:
TRANSCRIPT
Programando el DataReport
http://vexpert.mvps.org/articles/adoReports.htm[13/03/2012 07:23:31 p.m.]
Programando el DataReport de VB6
Algo de Código para Gestionar el Objeto DataReport en Tiempo de Ejecución
Por Harvey Triana
El componente DataReport es una de aquellas ideas excelentes para Visual Basic, pero como siempre, parece que siempre nacenprematuras y suelen dar problemas donde aparentemente no los debería haber (siempre tenemos que encontrarnos con algunacarencia o BUG). No obstante estudiando a fondo DataReport, le he encontrado su esencia y capacidad para gestionar reportes dedatos. En fin, a pesar de las múltiples carencias actuales, DataReport es sumamente atractivo, algo para programadores VisualBasic, y estoy seguro que pronto será robusto. Entre las cosas más interesantes de DataReport encuentro que se puede enlazar nosolo a DataEnvironments, sino a clases de reconocimiento de datos, y a simples objetos de ADO. Este articulo muestra unejemplo.
¿Porque que gestionar DataReport con código, y no usar los asistentes (partiendo delsde el DataEnvironment)?. Sencillamente larespuesta es la creación de reportes reutilizables (dinámicos). Por ejemplo, hace poco tenia que crear cerca de cien reportes de unabase de datos petrolera. Solucione el problema con solo tres DataReport y clases que los manipulan al derecho y al revés.
Primeros Pasos con DataReport
Como siempre, mis artículos no son estrictamente didácticos, y van más halla de la documentación estándar (de otra manera notendría sentido). Para empezar con DataReport, recomiendo los siguientes títulos de la MSDN (siga los árboles subsecuentes). Esimportante que domines aquellos conceptos para seguir con esta lectura.
Acerca del Diseñador de entorno de datosEscribir informes con el Diseñador de informe de datos de MicrosoftTener acceso a datos mediante Visual Basic
El Objeto DataReport
Se trata de unas librerías ActiveX escritas para Visual Basic, soportadas en tecnología ADO. Un DataReport se asimila mucho aun formulario, con su diseñador y todo. A grandes rasgos, he encontrado las siguientes características:
Carencias
1. Los Controles para el diseñador son pocos y algo limitados.2. No permite la adición de Controles en tiempo de ejecusión.3. Los controles enlazables a datos deben obligatoriamente estar enlazados a un DataField.4. Carece de una interfaz para exportar a documentos a formatos de Office.5. El diseñador tiene limitaciones (por ejemplo no permite copiar y pegar controles).6. El problema de la orientación del papel ha hecho carrera en los News (ver MSDN: Articulo 197915 - Report Width is
Larger than the Paper Width). Aun no encuentro solución para impresoras en Red.7. Debería compartir la interfaz del objeto Printer.8. La variable de tipo DataReport no acepta todas las propiedades definidas en un objeto DataReport especifico (ver MSDN:
Articulo 190584- Some Properties or Methods Not Displayed in DataReport).
Beneficios
1. Es manipulable desde código (tiene un modulo de código).2. Es tecnología ADO (acepta cualquier origen de datos).3. Acepta el conjunto de datos en tiempo de ejecución (siempre que sea lógico con la estructura del reporte)4. Esta bien organizado en términos de objetos5. El acceso a los controles es a través de cadenas de texto (los controles en un DataReport son diferentes a los controles
ActiveX normales)6. Crea informes con buen rendimiento
Existirán mas carencias y beneficios, pero por el momento estos enunciados son suficientes.
Para Los programadores Visual Basic, el primer beneficio enunciado es suficiente para tener muy en cuanta a DataReport, ya que
Programando el DataReport
http://vexpert.mvps.org/articles/adoReports.htm[13/03/2012 07:23:31 p.m.]
permitirá explorar todas su posibilidades. De eso trata este articulo.
Reportes Reutilzables
Como programador de gestión de datos: ¿alguna vez ha deseado imprimir el contenido de un conjunto de registros de formasimple (títulos y los datos en una plantilla)?, de la misma forma que abrimos una tabla o consulta en MS Access o MS FoxPro yusamos el comando Print. O, imprimir el contenido de un DataGrid tal cual, sin mucho complique. Bien, podemos intentarescribir un componente ActiveX usando un DataReport y solucionar el problema casi para cualquier situación similar. Sepresentarán problemillas, que se podrán solucionar en el componente y este evolucionara de manera conveniente para nosotros.
El problema expuesto anteriormente es, desde el punto de vista de acceso a datos, sencillo, es decir no existen conjuntos de datossubyacentes (relaciónes maestro-detalle). No obstante es posible escribir reportes complejos (varios niveles de relación) yreutilizables basándose la tecnología de comandos SHAPE.
Bien, daré una solución aproximada al problema expuesto.
Ejercicio
Crear un Proyecto EXE Estándar.Agregar referencia a MS ActiveX Data Objects 2.1 Library.Agregar Proyecto DLL.Agregar referencia a MS ActiveX Data Objects 2.1 Library.Agregar referencia a MS Data Formatting Object LibraryReferencias: MS ActiveX Data Objects 2.1 LibraryAgregar un Data Report (menú Proyecto)Diseñe el DataReport como se ve en la siguiente figura:
Más detalles de los controles para reporte y se encuentra en la siguiente tabla:
Seccion Tipo Nombre
stEncabezadoDeInforme RptLabel lblEncabezadoDeInforme_H
stEncabezadoDeInforme RptLine lnEncabezadoDeInforme_H
stEncabezadoDePagina RptLabel lblTituloDeCelda1
stEncabezadoDePagina RptLabel lblTituloDeCelda2
stDetalle RptTextBox txtCelda1
stDetalle RptTextBox txtCelda2
stPieDePagina RptLabel lblPieDePagina_H
stPieDeInforme RptLabel lblPieDeInforme_H
stPieDeInforme RptLine lnPieDeInforme_H
Programando el DataReport
http://vexpert.mvps.org/articles/adoReports.htm[13/03/2012 07:23:31 p.m.]
El propósito de los caracteres _H al final de algunos nombres de los Controles es poder, mediante código, extender el ancho delcontrol todo el ancho del informe, lo que es conveniente para líneas y títulos (esto nos permite ignorar el ancho del papel sindañar el la presentación del informe).
Otras propiedades del DataReport son Name = rptGerneral1, ReportWidth = 9360 twips (para un papel de 8.5 pulgadas y secalcula mediante ReportWidth = 8.5*1440 - 1440 (LeftMargin) - 1440 (RightMargin), donde 1440 son twips por pulgada)
Por el momento no dará código al modulo del DataReport.
Agregue el siguiente bloque de código a la clase creada por defecto por la DLL, luego el nombre debe ser Name = cls_Informe1:
'// ------------------------------------------------------------'// CLASS : Report1Level'// DESCRIPTION : Code Template for Report 2 Leves'// AUTHOR : Harvey T.'// LAST UPDATE : 17/11/99'// SOURCE : -'// ------------------------------------------------------------Option Explicit
'//MEMBERSPrivate m_DetailMember As StringPrivate m_Report As rptGerneral1
'//COLLECTIONSPrivate DetailCells As Collection
'//CONTANTSPrivate Const nMAXCELLS As Integer = 10
Public Function AddDetailCell( _ ByVal Title As String, _ ByVal FieldName As String, _ Optional ByVal FormatString As String = vbNullString, _ Optional ByVal ColumnWidth As Long = nDEFAULCOLUMNWIDTH _ ) As cls_CeldaDetalle
Static Key As Integer Static NextLeft As Long
Dim cell As cls_CeldaDetalle Dim txt As RptTextBox Dim lbl As RptLabel Dim LineRight As Long
Key = Key + 1
'//Filter maximun cells If Key > nMAXCELLS Then Exit Function
'//Filter ReportWidth If ColumnWidth <= 0 Then ColumnWidth = nDEFAULCOLUMNWIDTH If NextLeft + ColumnWidth > m_Report.ReportWidth Then '//Try Landscape If NextLeft + ColumnWidth > gRptWidthLandscape Then Exit Function '//No chances of add new cell Else '//changes orientation to Landscape Call gChangesOrientation(vbPRORLandscape) m_Report.ReportWidth = gRptWidthLandscape End If End If
'//Cell Set cell = New cls_CeldaDetalle Set txt = m_Report.Sections("stDetalle").Controls("txtCelda" & Key) With txt .DataField = FieldName .DataMember = m_DetailMember .Visible = True .Width = ColumnWidth .Left = NextLeft
LineRight = .Left + .Width NextLeft = NextLeft + .Width End With If Len(FormatString) Then gGiveFormat txt, FormatString
'//Cell title Set lbl = GetLabel("stEncabezadoDePagina", "lblTituloDeCelda" & Key) With lbl .Left = txt.Left .Width = txt.Width
Programando el DataReport
http://vexpert.mvps.org/articles/adoReports.htm[13/03/2012 07:23:31 p.m.]
.Caption = gAdjustNameToWidth(lbl, Title) .Visible = True End With
gCellMargin txt cell.Key = Key Set cell.txtCell = txt DetailCells.Add cell, CStr(Key)
Set AddDetailCell = cell Set cell = NothingEnd Function
Public Property Get Item(vntIndexKey As Variant) As cls_CeldaDetalle Set Item = DetailCells(vntIndexKey)End Property
Public Property Get Count() As Long Count = DetailCells.CountEnd Property
Public Property Get NewEnum() As IUnknown Set NewEnum = DetailCells.[_NewEnum]End Property
Private Sub Class_Initialize() Set DetailCells = New Collection Set m_Report = New rptGerneral1 Call gGetPageSize(m_Report)End Sub
Private Sub Class_Terminate() Set DetailCells = Nothing Set m_Report = Nothing Call gResetPageOrientEnd Sub
Public Property Get MaxCells() As Integer MaxCells = nMAXCELLSEnd Property
Public Property Let PieDePagina(ByVal v As String) gLetCaption GetLabel("stPieDePagina", "lblPieDePagina_H"), vEnd Property
Public Property Let PieDeInforme(ByVal v As String) gLetCaption GetLabel("stPieDeInforme", "lblPieDeInforme_H"), vEnd Property
Public Property Let EncabezadoDeInforme(ByVal v As String) gLetCaption GetLabel("stEncabezadoDeInforme", _ "lblEncabezadoDeInforme_H"), v m_Report.Caption = vEnd Property
Private Function GetCaption( _ SectionName As String, _ LabelName As String _ ) As String GetCaption = _ m_Report.Sections(SectionName).Controls(LabelName).CaptionEnd Function
Public Property Set DataSource(v As ADODB.Recordset) Set m_Report.DataSource = vEnd Property
Public Property Set DataEnviron(v As Object) Set m_Report.DataSource = vEnd Property
Public Property Let DataMember(v As String) m_Report.DataMember = vEnd Property
Public Property Let DetailMember(v As String) m_DetailMember = vEnd Property
Public Sub ShowReport(Optional Modal As Boolean = True) If Not m_Report.Visible Then gCorrectPRB8456 m_Report, "stDetalle", "txtCelda", m_DetailMember gElongedToWidth m_Report '//Show m_Report.Show IIf(Modal, vbModal, vbModeless) Else
Programando el DataReport
http://vexpert.mvps.org/articles/adoReports.htm[13/03/2012 07:23:31 p.m.]
m_Report.SetFocus End IfEnd Sub
Private Function GetLine( _ SectionName As String, _ LineName As String _ ) As RptLine Set GetLine = m_Report.Sections(SectionName).Controls(LineName)End Function
Private Function GetLabel( _ SectionName As String, _ LabelName As String _ ) As RptLabel Set GetLabel = m_Report.Sections(SectionName).Controls(LabelName)End Function
Luego agrega una clase, con propiedad Instancing = 2-PublicNotCreatable, Name = cls_CeldaDetalle. Esta clase será un objeto decolección de la clase cls_Informe1, y servirá para tener referencia a cada columna agregada al DataReport. El código de la clasecls_CeldaDetalle es:
'// ------------------------------------------------------------'// CLASS : DetailCell'// DESCRIPTION : A cell in custum report.'// Member rpttextbox of some collection'// AUTHOR : Harvey T.'// LAST UPDATE : 17/11/99'// SOURCE : -'// ------------------------------------------------------------Option Explicit
Public Key As Integer
Private m_txtCell As RptTextBox
Friend Property Set txtCell(v As RptTextBox) Set m_txtCell = vEnd Property
Friend Property Get txtCell() As RptTextBox Set txtCell = m_txtCellEnd Property
Por ultimo, agrega un modulo estándar a la DLL, con Name = modCommon y el siguiente código. Es modulo modCommon haceparte de una biblioteca de código más general escrita por mí para manipular DataReport.
'// ------------------------------------------------------------'// MODULE : Common'// DESCRIPTION : Shared any'// AUTHOR : Harvey T.'// LAST UPDATE : 29/11/99'// ------------------------------------------------------------Option Explicit
Public Const nDEFAULCOLUMNWIDTH As Long = 1800 '//twipsPublic Const nGRIDLINESCOLOR As Long = &H808080
Public gRptWidthLandscape As Long '//twipsPublic gRptWidthPortrait As Long '//twipsPublic gRptCurOrientation As LongPublic gRptNewOrientation As Long
'//As global multiusePrivate groo As New ReportOrientation
Public Sub gGiveFormat(txt As RptTextBox, FormatString As String) Dim f As New StdDataFormat
f.Format = FormatString Set txt.DataFormat = f txt.Alignment = rptJustifyRightEnd Sub
Public Sub gCellMargin(txt As RptTextBox) Const nCELLMARGIN As Long = 60 '//twips With txt .Width = .Width - 2 * nCELLMARGIN .Left = .Left + nCELLMARGIN End WithEnd Sub
Programando el DataReport
http://vexpert.mvps.org/articles/adoReports.htm[13/03/2012 07:23:31 p.m.]
Public Sub gCorrectPRB8456( _ objRpt As Object, _ SectionName As String, _ CellPrefix As String, _ MemberName As String _ ) '//rptErrInvalidDataField '//« No se encuentra el campo de datos » '//Solution: Give the first DataField in hide Cells
Dim txt As RptTextBox Dim ctl As Variant Dim s As String
'//Fisrt DataField s = objRpt.Sections(SectionName).Controls(CellPrefix & "1").DataField
For Each ctl In objRpt.Sections(SectionName).Controls If InStr(ctl.Name, CellPrefix) Then Set txt = ctl If txt.DataField = vbNullString Then txt.DataMember = MemberName txt.DataField = s txt.Width = 0 End If End If NextEnd Sub
Public Sub gMoveLine( _ ln As RptLine, _ Optional LineLeft, _ Optional LineTop, _ Optional LineWidth, _ Optional LineHeight _ ) If Not IsMissing(LineLeft) Then ln.Left = LineLeft If Not IsMissing(LineTop) Then ln.Top = LineTop If Not IsMissing(LineWidth) Then ln.Width = LineWidth If Not IsMissing(LineHeight) Then ln.Height = LineHeight If Not ln.Visible Then ln.Visible = TrueEnd Sub
Public Sub gLetCaption( _ lbl As RptLabel, _ Caption As String _ ) lbl.Caption = Caption If Not lbl.Visible Then lbl.Visible = TrueEnd Sub
Public Sub gGetPageSize(objRpt As Object) Dim ptr As Printer Dim tmp As Long
Set ptr = Printer With ptr gRptCurOrientation = groo.GetPrinterOrientation( _ .DeviceName, .hDC) gRptNewOrientation = gRptCurOrientation .ScaleMode = vbTwips gRptWidthPortrait = .Width - objRpt.LeftMargin - _ objRpt.RightMargin gRptWidthLandscape = .Height - objRpt.LeftMargin - _ objRpt.RightMargin If gRptCurOrientation = vbPRORLandscape Then '//Swap tmp = gRptWidthPortrait gRptWidthPortrait = gRptWidthLandscape gRptWidthLandscape = tmp objRpt.ReportWidth = gRptWidthLandscape End If End With Set ptr = NothingEnd Sub
Public Sub gChangesOrientation(ro As Enum_ReportOriention) gRptNewOrientation = ro groo.SetPrinterOrientation roEnd Sub
Public Sub gElongedToWidth(objRpt As Object) Const sFLAG As String = "_H"
Dim sect As Section Dim ctl As Variant
Programando el DataReport
http://vexpert.mvps.org/articles/adoReports.htm[13/03/2012 07:23:31 p.m.]
Dim n As Long n = objRpt.ReportWidth
For Each sect In objRpt.Sections For Each ctl In sect.Controls If Right(ctl.Name, 2) = sFLAG Then ctl.Left = 0 ctl.Width = n End If Next NextEnd Sub
Public Sub gResetPageOrient() If Not gRptNewOrientation = gRptCurOrientation Then Call gChangesOrientation(gRptCurOrientation) End IfEnd Sub
Public Function gAdjustNameToWidth( _ lbl As RptLabel, _ Caption As String _ ) As String
Dim rtn As String Dim s As String
With Printer Set .Font = lbl.Font If .TextWidth(Caption) > lbl.Width Then s = Caption + Space(2) Do s = Left(s, Len(s) - 1) rtn = s + "..." Loop Until .TextWidth(rtn) < lbl.Width Or Len(s) = 0 gAdjustNameToWidth = rtn Else gAdjustNameToWidth = Caption End If End WithEnd Function
Public Sub gGetControlsList(objRpt As Object) Const CO As String = " " Dim sect As Section Dim ctl As Variant
Debug.Print "Section"; CO; "Type"; CO; "Name" For Each sect In objRpt.Sections For Each ctl In sect.Controls Debug.Print sect.Name; CO; TypeName(ctl); CO; ctl.Name Next NextEnd Sub
Agregue una nueva clase a la DLL. Esta clase contiene la API para manipular la orientación del papel. Observe los creditos alautor. El código de esta clase lo consigue en este Link: ReportOrientation.zip (3k)
Finalmente, al modulo del formulario del proyecto estándar, agrega un Hierarchacal FlexGrid, Name = flexMuestra, unCommanButton, Name = cmdInforme. El formulario llevara el siguiente código de ejemplo:
Los nombres y estructura de los proyectos se muestra a continuación:
Programando el DataReport
http://vexpert.mvps.org/articles/adoReports.htm[13/03/2012 07:23:31 p.m.]
El grupo de proyectos se llamará: grpReporteDeMuestra.vbg. Este grupo de proyectos es útil para depurar el componenteInformeGeneral1, que posteriormente se puede dar compatibilidad binaria para colocarlo al servicio de futuros proyectos. Elcódigo del cliente (frmMuestra) es el siguiente:
'// ------------------------------------------------------------'// FORM : frmMuestra'// DESCRIPTION : Ejemplo de DataReport general'// AUTHOR : Harvey T.'// LAST MODIFY : -'// ------------------------------------------------------------Option Explicit
Private rs As ADODB.Recordset
Private Sub cmdInforme_Click() flexMuestra.SetFocus DoEvents GenerarReporteEnd Sub
Private Sub GenerarReporte() Dim rpt As cls_Informe1 Set rpt = New cls_Informe1 With rpt Set .DataSource = rs .EncabezadoDeInforme = "Base de Datos NWIND (Clientes)" .PieDeInforme = "Fin de Informe" .PieDePagina = "Clientes con su Contacto" .AddDetailCell "Compañía", "NombreCompañía", , 6000 .AddDetailCell "Contacto", "NombreContacto", , 3000 .ShowReport True End WithEnd Sub
Private Sub Form_Load() Call InicieConjuntoDeRegistros
'//Cofigurar Grilla flexMuestra.ColWidth(0) = 300 flexMuestra.ColWidth(1) = 2000 flexMuestra.ColWidth(2) = 2000 Set flexMuestra.DataSource = rsEnd Sub
Private Function InicieConjuntoDeRegistros() Dim cnn As Connection Dim cmd As Command
Set cnn = New Connection Set cmd = New Command Set rs = New Recordset
'//Database command connection cnn.Open "Provider=Microsoft.Jet.OLEDB.3.51;" & _ "Data Source=D:\Archivos de programa\VB98\Nwind.mdb;" With cmd Set .ActiveConnection = cnn .CommandType = adCmdText .CommandText = "SELECT NombreCompañía, NombreContacto " & _ "FROM Clientes " & _ "ORDER BY NombreCompañía;"
Programando el DataReport
http://vexpert.mvps.org/articles/adoReports.htm[13/03/2012 07:23:31 p.m.]
End With
With rs .CursorLocation = adUseClient .Open cmd, , adOpenForwardOnly, adLockReadOnly Set cmd.ActiveConnection = Nothing Set cmd = Nothing Set .ActiveConnection = Nothing End With cnn.Close Set cnn = NothingEnd Function
Private Sub Form_Unload(Cancel As Integer) If Not rs Is Nothing Then rs.Close End IfEnd Sub
Private Sub Form_Resize() If Not Me.WindowState = vbMinimized Then flexMuestra.Move 0, 0, Me.ScaleWidth, Me.ScaleHeight - 330 cmdInforme.Move 0, Me.ScaleHeight - cmdInforme.Height End IfEnd Sub
La ejecución del proyecto muestra la siguiente interfaz de usuario:
La ejecución del informe a través del botón Informe, mostrara el siguiente Informe:
Mostrado en un Zoom = 50 %.
Discusión y Ampliación del Informe Reutilizable
Tal cual el componente InformeGeneral1, servirá para mostrar cualquier tabla o vista de datos con dos columnas, solo habrá quemodificar el código del cliente, a saber el procedimiento: InicieConjuntoDeRegistros. Para ampliar la capacidad a más columnas,deberá agregar controles (debido a la limitación numero 2) RptLabel de nombre lblTituloDeCeldaX, y controles txtCeldaX a susrespectivas secciones (X es el nuevo numero del control agregado, por ejemplo si agrega una tercera columna, X = 3). Aun notermina el trabajo tedioso, tendrá que dar las propiedades pertinentes a cada nuevo control (debido a la limitación numero 5). Porultimo deberá modificar la constante nMAXCELLS del la clase cls_Informe1 (esta contante evita el error por desbordamiento delnúmero de columnas enviadas a DataReport).
Se puede dar una grilla a la presentación de la tabla en el informe, pero es un trabajo algo tedioso, deberá agregar controlesRptLine a los lados de las celdas y sus titulo. Sin bien vale la pena y le queda de tarea.
Programando el DataReport
http://vexpert.mvps.org/articles/adoReports.htm[13/03/2012 07:23:31 p.m.]
El componente InformeGeneral1 intenta solucionar el problema de la orientación del papel de la siguiente manera: Si el numerode columnas no cabe en posición Portrait, el reporte pasa (automaticmente) a orientación LandScape, hasta que acepte un numerode columnas que cubran el área del reporte, más halla no se mostraran más columnas (sin generar error). Si estudia el código, laclase ReportOrientation contiene la API necesaria para cambiar la orientación del papel. Desdichadamente el código trabaja solopara impresoras locales.
Debido a la carencia número 3: « Los controles enlazables a datos deben obligatoriamente estar enlazados a un DataField », esnecesario ejecutar el procedimiento gCorrectPRB8456 del modulo modCommon antes de mostrar el Informe. Este procedimientoda un DataField repetido y oculto a las columnas que no se utilizan.
También puede agregar más RptLabel, Imágenes, numeración de páginas, etc. para mejorar la apariencia del Informe. Un informede ejemplo llevado sobre la base de código se muestra a continuación:
top related