none
Local Report zeigt keine Grafik an RRS feed

  • Frage

  • Hallo zusammen,

    in einer VB-2008-Forms-Anwendung erstelle ich eine Grafik, die auch wunschgemäß angezeigt wird. Zum Drucken dieser Grafik speichere ich dessen Image als .png-Datei, erstelle einen Local Report als .rdlc, speichere und rendere dann als PDF.
    Klappt soweit ganz gut: Kopf, Fuß, Legende usw. werden in der gewünschtren Form ausgegeben - nur anstelle der Grafik erscheint das berühmte rote X.
    Der Code zum Einfügen der Grafik ist wie folgt:

    Sub RepInsImage()
        Dim RepItem As String
    
        pObjGrafikBox.Image.Clone.Save(pGrafikFullPfad, System.Drawing.Imaging.ImageFormat.Png)
        prItemsCtr = prItemsCtr + 1
    
        RepItem = "   <Image Name=""" & "Item" & prItemsCtr & """>" & vbCrLf
        RepItem = RepItem & "    <Sizing>Fit</Sizing>" & vbCrLf
        RepItem = RepItem & "    <Top>" & pTop & "</Top>" & vbCrLf
        RepItem = RepItem & "    <Width>" & pWidth & "</Width>" & vbCrLf
        RepItem = RepItem & "    <Source>External</Source>" & vbCrLf
        RepItem = RepItem & "    <ZIndex>" & prItemsCtr & "</ZIndex>" & vbCrLf
        RepItem = RepItem & "    <Left>" & pLeft & "</Left>" & vbCrLf
        RepItem = RepItem & "    <Height>" & pHeight & "</Height>" & vbCrLf
        RepItem = RepItem & "    <Value>=""" & "file:///" & pGrafikFullPfad & """</Value>" & vbCrLf
        RepItem = RepItem & "    </Image>" & vbCrLf
    
        ReDim Preserve pRepBytesNeu(pRepBytesNeuPtr + Len(RepItem) + 10)
    
        Dim I As Integer
        For I = 1 To Len(RepItem)
          pRepBytesNeu(pRepBytesNeuPtr) = Asc(Mid(RepItem, I, 1))
          pRepBytesNeuPtr = pRepBytesNeuPtr + 1
        Next
    
      End Sub

    Der String pGrafikFullPfad enthält den vollständigen Pfad, z.B. "C:/DIAG.png", das Image steht dort auch und ist über den Explorer aufrufbar.

    Auf meinem Entwicklungsrechner (VISTA, VB2008-Entwicklungsumgebung befindet sich auf Laufwerk D:) triit der o.a. Fehler auf.
    Auf meinem Entwicklungsrechner (VISTA, Programm in C/Programme installiert) triit der o.a. Fehler auf.
    Auf meinem LAPTOP (Vista, Programm in C/Programme installiert) zeigt er mir die Grafik an.
    Auf einem weiteren Rechner (XP, Programm in C/Programme installiert) zeigt er mir die Grafik ebenfalls an.
    Alle Rechner sind über MS-Update auf dem neuesten Stand.

    Kann mir hier jemand helfen oder einen Tipp geben?

    Gruß
    Sven

     

    Dienstag, 6. Juli 2010 16:38

Antworten

  • Hallo Sven,

    da die Anzeige nur bei einigen Rechnern nicht funktioniert,
    würde ich auf Berechtigungsprobleme tippen.

    Eine direkte Ablage auf C:\ ist zumindest für Vista und später nicht zu empfehlen.
    Verwende für die Ablage das temporäre Verzeichnis, was Du mit Path.GetTempPath erhältst.
    Den Dateinamen solltest Du über System.Uri erstellen, um sicherzustellen,
    dass es sich um einen korrekte Angabe handelt (file:/// reicht nicht immer).

    Fürs Umwandeln in eine Byte Folge verwende System.Encoding.GetBytes
    (eine Umwandlung via Asc ist sehr gewagt, wenn anderes als ASCII Zeichen vorkommen).

    Im übrigen wäre eine Nutzung der LINQ To XML Unterstützung empfehlenswert,
    womit das Erstellen des erforderlichen XML deutlich einfacher und übersichtlicher wird:
    XML in Visual Basic

    Gruß Elmar

    Mittwoch, 7. Juli 2010 07:35
    Beantworter
  • Hallo Sven,

    naja ein klein wenig fehlte da noch an einem übersetzbaren Programm ;-)
    Da aber die Struktur erkennbar war, habe ich mir anderweitig geholfen.
    Problematisch sehe ich vor allem das Konvertieren des XML an,
    Xml <-> String <-> Bytes, da kann schon mal was passieren.

    Und wie oben schon geschrieben kann, man das sehr gut direkt über XML machen.
    Ich habe mich für XML Linq entschieden, via DOM ginge es aber ebenso.
    Visual Basics Erweiterungen erwiesen sich wegen des Namespaces eher als
    hinderlich  und so bin ich ohne ausgekommen ;-)
    Ich habe daraus eine Klasse (kein Modul) erstellt, die sich an Deinen Aufbau anlehnt
    und die Dateinamen usw. weitgehend beibehält (soweit ich sie erraten habe).

    Als Vorlage für den "RepQuer" habe ich einen leeren Bericht verwendet.

    Option Strict On
    Option Explicit On
    Option Infer On
    
    Imports System.Drawing
    Imports System.Diagnostics
    Imports System.IO
    Imports System.Linq
    Imports System.Xml
    Imports System.Xml.Linq
    Imports Microsoft.Reporting.WinForms
    
    Public Class PdfImageReport
      Implements IDisposable
    
    #Region "Konstanten"
      ' Ergänzt LogDirectory für Diagramm
      Private Const DiagrammPfad As String = "ES-Diagramme" ' Verzeichnis für Grafiken
    
      Private Const ReportAusgabeName As String = "RepQuer" ' Ausgabename für den Bericht
      Private Const ReportExtension As String = ".htm"  ' Endung der Vorlagen - Ist in Wirklichkeit eine .rdlc
    
      Private Const DiagrammName As String = "ES-Diag"  ' Basisname für Grafiken
      Private Const DiagrammExtension As String = "png"  ' Verwendet PNG
    
      Private Const PdfName As String = "ES-Rep"  ' Basisname für PDF Dokument
      Private Const PdfExtension As String = "pdf"  ' PDF Ausgabe
    
      ' RDL 2005 Namespace
      Private Shared ReadOnly ReportNamespace As XNamespace = "http://schemas.microsoft.com/sqlserver/reporting/2005/01/reportdefinition"
    #End Region
    
      Private _berichtVerzeichnis As String ' das Berichtsverzeichnis (pRepPfad)
      Private _ausgabeVerzeichnis As String ' das Arbeitsverzeichnis (pLogDirectory)
      Private _tempDateiNamen As New List(Of String)  ' Arbeitsdateien (nach Ausgabe löschen)
    
      Private _reportDocument As XDocument ' Der Bericht als XDocument
      ' Position für die Grafik in cm
      Private _position As New RectangleF(0.25F, 1.4F, 25.5F, 14.7F)
      ' pLeft = "0.25cm"
      ' pTop = "1.4cm"
      ' pWidth = "25.5cm"
      ' pHeight = "14.7cm"
    
      ''' <summary>
      ''' Legt die Position des eingebetteten Bildes fest
      ''' </summary>
      ''' <remarks>
      ''' Alterativ: Werte direkt bei InsertImage
      ''' </remarks>
      Public Property BildPosition() As RectangleF
        Get
          Return _position
        End Get
        Set(ByVal value As RectangleF)
          Me._position = value
        End Set
      End Property
    
      ''' <summary>
      ''' Erstellt eine neue Instanz mit einem Verzeichnis.
      ''' </summary>
      ''' <param name="berichtVerzeichnis">Das Verzeicnis für Berichtsvorlagen.</param>
      ''' <param name="ausgabeVerzeichnis">Das Arbeitsverzeichnis für Ausgaben.</param>
      ''' <remarks>
      ''' berichtVerzeichnis war pRepPfad,
      ''' ausgabeVerzeichnis war plogDirectory.
      ''' </remarks>
      Public Sub New(ByVal berichtVerzeichnis As String, ByVal ausgabeVerzeichnis As String)
        ' Verzeichnisse müssen vorhanden sein.
        If String.IsNullOrEmpty(berichtVerzeichnis) _
          OrElse Not Directory.Exists(berichtVerzeichnis) Then
          Throw New ArgumentException("berichtVerzeichnis")
        End If
    
        If String.IsNullOrEmpty(ausgabeVerzeichnis) _
          OrElse Not Directory.Exists(ausgabeVerzeichnis) Then
          Throw New ArgumentException()
        End If
        Me._berichtVerzeichnis = berichtVerzeichnis
        Me._ausgabeVerzeichnis = ausgabeVerzeichnis
      End Sub
    
      ''' <summary>RepOpen</summary>
      ''' <param name="reportName">Name der Berichtsvorlage.</param>
      Public Sub Open(ByVal reportName As String)
        ' Ausgabeverzeichnis + Diagramm-Verzeichnisname
        Dim grafikPfad As String = Path.Combine(Me._ausgabeVerzeichnis, DiagrammPfad)
        ' und ggf. anlegen
        Directory.CreateDirectory(grafikPfad)
    
        ' Endung ist hier .htm nicht .rdlc
        Dim reportPfad As String = Path.Combine(Me._berichtVerzeichnis, reportName & ReportExtension)
        Me._reportDocument = XDocument.Load(reportPfad)
      End Sub
    
      ''' <summary>Bild über Pfad einfügen</summary>
      ''' <param name="bitmapfileName">Der Dateiname der Bitmap</param>
      Public Sub InsertImage(ByVal bitmapfileName As String)
        InsertImage(DirectCast(Bitmap.FromFile(bitmapfileName), Bitmap))
      End Sub
    
      ''' <summary>RepInsImage</summary>
      ''' <param name="bitmap">Die Bitmap (Image)</param>
      Public Sub InsertImage(ByVal bitmap As Bitmap)
        Dim root As XElement = Me._reportDocument.Root
    
        Dim bodyName As XName = ReportNamespace + "Body"
        Dim reportItemsName As XName = ReportNamespace + "ReportItems"
    
        Dim grafikDatei As String = GetGrafikDateiName()
    
        ' einen Klon anlegen und als Datei speichern
        bitmap = DirectCast(bitmap.Clone(), Bitmap)
        bitmap.Save(grafikDatei, System.Drawing.Imaging.ImageFormat.Png)
        bitmap.Dispose()
    
        Dim body = root.Elements(bodyName).FirstOrDefault
        If body Is Nothing Then
          Throw New InvalidOperationException("Kein Body Element gefunden.")
        End If
    
        Dim reportItems = body.Element(reportItemsName)
        If reportItems Is Nothing Then
          reportItems = New XElement(reportItemsName)
          body.Add(reportItems)
        End If
    
        ' Laufende Nummerierung
        Dim count As Integer = reportItems.Nodes.Count
        reportItems.Add(CreateImageElement(grafikDatei, count))
      End Sub
    
      Public Sub RenderPDF()
        If Me._reportDocument Is Nothing Then
          Throw New InvalidOperationException("Zuerst muß der Bericht geöffnet werden.")
        End If
    
        ' Name für den generierten Bericht
        Dim reportDateiname = GetReportDateiName()
        Me._reportDocument.Save(reportDateiname)
        ' Freigeben
        Me._reportDocument = Nothing
    
        Dim pdfDateiName = GetPdfDateiName()
    
        Using reportQuer As New LocalReport()
          Using reportStream As New StreamReader(reportDateiname)
            reportQuer.LoadReportDefinition(reportStream)
          End Using
          reportQuer.EnableExternalImages = True
    
          Dim reportBytes = reportQuer.Render("PDF", "", "", "", "", Nothing, Nothing)
    
          File.WriteAllBytes(pdfDateiName, reportBytes)
        End Using
    
        Dim readerProcess = Process.Start(pdfDateiName)
        'readerProcess.WaitForExit()
      End Sub
    
    
      Private Function CreateImageElement(ByVal grafikDatei As String, ByVal index As Integer) As XElement
        Dim elementName As New XAttribute("Name", InvariantFormat("Image{0}", index + 1))
    
        Dim imageElement = New XElement(ReportNamespace + "Image", _
          New XElement(ReportNamespace + "Sizing", "Fit"), _
          New XElement(ReportNamespace + "Source", "External"), _
          New XElement(ReportNamespace + "Left", FormatZentimeter(BildPosition.Left)), _
          New XElement(ReportNamespace + "Top", FormatZentimeter(BildPosition.Top)), _
          New XElement(ReportNamespace + "Width", FormatZentimeter(BildPosition.Width)), _
          New XElement(ReportNamespace + "Height", FormatZentimeter(BildPosition.Height)), _
          New XElement(ReportNamespace + "ZIndex", index + 1), _
          New XElement(ReportNamespace + "Value", New Uri(grafikDatei, UriKind.Absolute)))
        imageElement.Add(elementName)
        Return imageElement
      End Function
    
    #Region "Dateinamen erstellen"
      ''' <summary>
      ''' Ermitteln des Dateinamens für die Bericht.
      ''' </summary>
      Private Function GetReportDateiName() As String
        Return GetDateiName(ReportAusgabeName, "rdlc")
      End Function
    
      ''' <summary>
      ''' Ermitteln eines Dateinamens für die Grafik.
      ''' </summary>
      Private Function GetGrafikDateiName() As String
        Return GetDateiName(DiagrammName, DiagrammExtension)
      End Function
    
      ''' <summary>
      ''' Ermitteln eines Dateinamens für die PDF Datei.
      ''' </summary>
      ''' <remarks>Sollte der Vorlage GetNeuFileName ähnlich sein</remarks>
      Private Function GetPdfDateiName() As String
        Return GetDateiName(PdfName, PdfExtension, False)
      End Function
    
      Private Function GetDateiName(ByVal name As String, ByVal endung As String, Optional ByVal isTemp As Boolean = True) As String
        ' hier gleiches Verzeichnis wie für Grafik
        Dim verzeichnis As String = Path.Combine(Me._ausgabeVerzeichnis, DiagrammPfad)
        ' ggf. anlegen
        Directory.CreateDirectory(verzeichnis)
    
        ' und Dateiname anfügen
        For index As Integer = 1 To 9999
          Dim dateiName As String = InvariantFormat( _
            "{0}{1:0000}.{2}", _
            name, index, endung)
    
          dateiName = Path.Combine(verzeichnis, dateiName)
          If Not File.Exists(dateiName) Then
            ' Dateinamen merken für spätere Aufräumarbeiten
            If isTemp Then
              Me._tempDateiNamen.Add(dateiName)
            End If
            Return dateiName
          End If
        Next
        ' Ein Aufräumen wäre nicht schlecht...
        Throw New InvalidOperationException("Es konnte kein eindeutiger Dateiname ermittelt werden.")
      End Function
    #End Region
    
      ''' <summary>
      ''' Invariante Formatierung für Berichtsparameter etc.
      ''' </summary>
      ''' <remarks>Setzt InvariantCulture für die Formatierung von Zahlen usw. ein.</remarks>
      Private Shared Function FormatZentimeter(ByVal value As Single) As String
        Return InvariantFormat("{0:F2}cm", value)
      End Function
    
      ''' <summary>
      ''' Invariante Formatierung für Berichtsparameter etc.
      ''' </summary>
      ''' <remarks>Setzt InvariantCulture für die Formatierung von Zahlen usw. ein.</remarks>
      Private Shared Function InvariantFormat(ByVal format As String, ByVal ParamArray args As Object()) As String
        Return String.Format(System.Globalization.CultureInfo.InvariantCulture, _
            format, args)
      End Function
    
      Public Sub Close()
        Me.Dispose(True)
      End Sub
    
    #Region "IDisposable Support"
      Public Sub Dispose() Implements IDisposable.Dispose
        Dispose(True)
        GC.SuppressFinalize(Me)
      End Sub
    
      Protected Overridable Sub Dispose(ByVal disposing As Boolean)
        ' Temporäre Dateien löschen
        For Each dateiname In Me._tempDateiNamen
          Try
            File.Delete(dateiname)
          Catch
          End Try
        Next
        Me._tempDateiNamen.Clear()
      End Sub
    #End Region
    
    End Class
    
    

    Die Verwendung wäre z. B.:

        Using report As New PdfImageReport("C:\TEMP", "C:\TEMP")
          report.Open("ImageReport")
          report.InsertImage("E:\MEDIA\BILDER\KATZEN_01.JPG")
          report.RenderPDF()
        End Using
    
    
    Bei mir erschien das Mietzekätzchen, das häufiger für Tests herhalten muß,
    im Akrobat Reader.

    Anmerkkung am Rande: Dispose (bzw. Close) löscht die temporären Dateien.

    Inwieweit Probleme bei komplexeren Berichtsvorlagen auftreten können
    (meiner war wie gesagt leer) habe ich jetzt nicht im Detail geprüft.
    Eine Alternative anstatt XML Direkt wäre eine dynamische Generierung via
    Klassenmodel, wie sie auf http://www.gotreportviewer.com/ beispielhaft gezeigt wird.

    Sollten sich die Bilder bei Dir immer noch nicht zeigen wäre ein weiterer
    weg sie als Parameter einzubetten, siehe z. B.:

    http://stackoverflow.com/questions/36693/how-can-i-render-a-png-image-as-a-memory-stream-onto-a-net-reportviewer-repor

    Gruß Elmar

    Dienstag, 13. Juli 2010 16:38
    Beantworter

Alle Antworten

  • Hallo Sven,

    da die Anzeige nur bei einigen Rechnern nicht funktioniert,
    würde ich auf Berechtigungsprobleme tippen.

    Eine direkte Ablage auf C:\ ist zumindest für Vista und später nicht zu empfehlen.
    Verwende für die Ablage das temporäre Verzeichnis, was Du mit Path.GetTempPath erhältst.
    Den Dateinamen solltest Du über System.Uri erstellen, um sicherzustellen,
    dass es sich um einen korrekte Angabe handelt (file:/// reicht nicht immer).

    Fürs Umwandeln in eine Byte Folge verwende System.Encoding.GetBytes
    (eine Umwandlung via Asc ist sehr gewagt, wenn anderes als ASCII Zeichen vorkommen).

    Im übrigen wäre eine Nutzung der LINQ To XML Unterstützung empfehlenswert,
    womit das Erstellen des erforderlichen XML deutlich einfacher und übersichtlicher wird:
    XML in Visual Basic

    Gruß Elmar

    Mittwoch, 7. Juli 2010 07:35
    Beantworter
  • Hallo Elmar,

    vielen Dank für die Tipps, die ich z.T. eingearbeitet habe.
    Leider wird die Grafik immer noch nicht angezeigt.

    Ich habe den Fehler soweit eingegrenzt, dass beim "rendern" auf dem Entwicklungsrechner die Grafik nicht erkannt wird, wohl aber auf den anderen Rechnern (Bei exakt gleichem XML-File [als .rdlc]).
    Die Zugriffsberechtigungen auf den einzelnen Rechner sind sind identisch, also offensichtlich gibt es ein Berechtigungsproblem beim "rendern" (den XML-File als .rdlc und auch die Grafik als .png kann ich aus VB heraus problemlos laden und speichern). 

    Der Code ist wie folgt:

    Sub RepClose(ByVal RepName)
    
        'Fertigstellen: Rest der ReportDefinition kopieren
        Dim RestLeng As Long
        RestLeng = pAnzVor - pRepBytesOldPtr
        ReDim Preserve pRepBytesNeu(pRepBytesNeuPtr + RestLeng - 1)
        Array.Copy(pRepBytes, pRepBytesOldPtr, pRepBytesNeu, pRepBytesNeuPtr, RestLeng)
    
        'Als temporären Bericht speichern
        My.Computer.FileSystem.WriteAllBytes(pGrafikPfad & "RepQuer.rdlc", pRepBytesNeu, False)
    
        Dim warnings As Warning() = Nothing
        Dim streamids As String() = Nothing
        Dim mimeType As String = Nothing
        Dim coding As String = Nothing
        Dim extension As String = Nothing
        Dim deviceInfo As String = ""
        Dim bytes() As Byte
    
        Dim RepQuer As New LocalReport
        Dim StrReader As StreamReader = New StreamReader(pGrafikPfad & "RepQuer.rdlc")
        RepQuer.LoadReportDefinition(StrReader)
        RepQuer.EnableExternalImages = True
    
        bytes = RepQuer.Render("PDF", deviceInfo, mimeType, coding, extension, streamids, warnings)
    
        Dim fs As New FileStream(pGrafikPfad & pNeuFileName, FileMode.Create)
        fs.Write(bytes, 0, bytes.Length)
        fs.Close()
    
        ShellExecuteEx(0, pGrafikPfad & pNeuFileName)
    
        'Temporäre Objekte löschen
        StrReader.Dispose()
        'My.Computer.FileSystem.DeleteFile(pGrafikPfad & "ES-Diag.png")
        'My.Computer.FileSystem.DeleteFile(pGrafikPfad & "RepQuer.rdlc")
    
      End Sub
    

    Kannst Du mir mit diesen Informationen weiterhelfen?

    Gruß
    Sven

     

     

    Samstag, 10. Juli 2010 09:45
  • Hallo Sven,

    mit den beiden Codeschnippsel alleine wird das etwas schwierig,
    da zuviele Unbekannte übrigbleiben.

    Wenn Du ein Beispielprojekt bereitstellen könntest wäre das deutlich einfacher.

    Gruß Elmar

     

    Montag, 12. Juli 2010 07:10
    Beantworter
  • Hallo Elmar,

    untenstehend der Code noch einmal besser lesbar.

    Option Explicit On
    
    Imports System
    Imports System.Text
    Imports Microsoft.VisualBasic
    Imports Microsoft.Reporting.WinForms
    
    Module MiniReportProgramm
    
      '***************************************************************************************************************
      ' Reporting: Programm ausführen
      '***************************************************************************************************************
      'Deklaration der Funktion
      Private Declare Function ShellExecute Lib "shell32.dll" Alias "ShellExecuteA" _
        (ByVal hwnd As Int32, ByVal lpOperation As String, ByVal lpFile As String, ByVal lpParameters As String, _
         ByVal lpDirectory As String, ByVal nShowCmd As Int32) As Int32
      'Funktion zum Aufrufen 
      Public Function ShellExecuteEx(ByVal lWindowHandle As Integer, ByVal sExecute As String) As Boolean
        Return (ShellExecute(lWindowHandle, "open", sExecute, "", "", &H1&) = 0)
      End Function
    
    
      '***************************************************************************************************************
      ' Reporting: PRIVATEs
      '***************************************************************************************************************
      Private pRepBytes(1) As Byte
      Private pRepBytesNeu(1) As Byte
      Private pRepBytesNeuPtr As Long
      Private pRepBytesOldPtr As Long
      Private pAnzVor As Long
    
      'Zentraler Publics-Block für Felder im Report
      Private pTop As String
      Private pLeft As String
      Private pWidth As String
      Private pHeight As String
      Private pCol As String
      Private pStyle As String
      Private pAlign As String
      Private pSize As String
      Private pWeight As String
      Private prItemsCtr As Long
    
      Private pGrafikBmp As Bitmap
      Private pGrafikPfad As String
      Private pGrafikFullPfad As String
    
      '***************************************************************************************************************
      ' GrafikDruck: Druck der Grafik im Querformat
      '***************************************************************************************************************
      Sub MiniGrafikDruck()
    
        Dim CursorTyp As Cursor
        CursorTyp = pMainForm.Cursor
        pMainForm.Cursor = System.Windows.Forms.Cursors.WaitCursor
    
        Call RepOpen("RepQuer")
    
    
        ' Grafik *************************************************************************************************
        pTop = "1.4cm"
        pLeft = "0.25cm"
        pWidth = "25.5cm"
        pHeight = "14.7cm"
        Call RepInsImage()
    
        ' Endebehandlung *****************************************************************************************
        Call RepClose("RepQuer")
        pMainForm.Cursor = CursorTyp
    
      End Sub
    
      '***************************************************************************************************************
      ' RepOpen: Vorlage des Berichtes öffnen
      '***************************************************************************************************************
      Sub RepOpen(ByVal RepName)
    
        'Pfade vorbesetzen: 
        'Im Pfad ES-Diagramme werden die Diagramme abgelegt, ist gleichzeitig auch Pfad für temporäre Files
        pGrafikPfad = pLogDirectory & "/ES-Diagramme/"
        pGrafikPfad = pGrafikPfad.Replace("\", "/")
        pGrafikFullPfad = pGrafikPfad & "ES-Diag.png"
        'Falls Pfad noch nicht exisiert: Neu anlegen
        If Not (My.Computer.FileSystem.DirectoryExists(pGrafikPfad)) Then _
            My.Computer.FileSystem.CreateDirectory(pGrafikPfad)
        'Falls Files aus einer vorigen Sitzung noch existieren: Löschen
        If (My.Computer.FileSystem.FileExists(pGrafikPfad & "ES-Diag.png")) Then _
          My.Computer.FileSystem.DeleteFile(pGrafikPfad & "ES-Diag.png")
        If (My.Computer.FileSystem.FileExists(pGrafikPfad & "RepQuer.rdlc")) Then _
          My.Computer.FileSystem.DeleteFile(pGrafikPfad & "RepQuer.rdlc")
        Call GetNeuFileName(".pdf")
    
    
        'ReportDefinition byteweise einlesen
        ' Dim Pfad As String = pRepPfad & "/" & RepName & ".rdlc"
        Dim Pfad As String = pRepPfad & RepName & ".htm"  'Ist in Wirklichkeit eine .rdlc
        Dim Info As System.IO.FileInfo
        Info = My.Computer.FileSystem.GetFileInfo(Pfad)
        pRepBytes = My.Computer.FileSystem.ReadAllBytes(Pfad)
        pAnzVor = Info.Length
    
        'ReportDefinition in Zeilenstruktur übertragen. 
        'Dabei den Byte-Pointer auf RepItems setzen (da werden dann später die neuen Items eingefügt)
        Dim I As Double
        Dim Body As String = ""
        Dim Line As String = ""
    
        For I = 0 To Info.Length - 1
          Line = Line & Chr(pRepBytes(I))
          If (pRepBytes(I)) = 10 Then
            If InStr(1, Line, "<Body>") > 1 Then Body = "Erkannt"
            If InStr(1, Line, "<ReportItems>") > 1 And Body = "Erkannt" Then
              pRepBytesNeuPtr = I + 1
              Body = ""
              Exit For
            End If
            Line = ""
          End If
        Next
    
    
        'Bytes von 0 bis pRepBytesNeuPtr (also den ersten Teil bit zu den neuen Items)kopieren
        ReDim pRepBytesNeu(pRepBytesNeuPtr + 10)
        Array.Copy(pRepBytes, 0, pRepBytesNeu, 0, pRepBytesNeuPtr)
        pRepBytesOldPtr = pRepBytesNeuPtr
    
        prItemsCtr = 0
    
      End Sub
    
    
      '***************************************************************************************************************
      ' RepInsImage: ImageBox im Bericht platzieren 
      '       Alle Einstellungen wurden im zentralen Report-Block eingetragen
      '***************************************************************************************************************
      Sub RepInsImage()
        Dim RepItem As String
    
        pObjGrafikBox.Image.Clone.Save(pGrafikFullPfad, System.Drawing.Imaging.ImageFormat.Png)
        prItemsCtr = prItemsCtr + 1
    
        RepItem = "   <Image Name=""" & "Item" & prItemsCtr & """>" & vbCrLf
        RepItem = RepItem & "    <Sizing>Fit</Sizing>" & vbCrLf
        RepItem = RepItem & "    <Top>" & pTop & "</Top>" & vbCrLf
        RepItem = RepItem & "    <Width>" & pWidth & "</Width>" & vbCrLf
        RepItem = RepItem & "    <Source>External</Source>" & vbCrLf
        RepItem = RepItem & "    <ZIndex>" & prItemsCtr & "</ZIndex>" & vbCrLf
        RepItem = RepItem & "    <Left>" & pLeft & "</Left>" & vbCrLf
        RepItem = RepItem & "    <Height>" & pHeight & "</Height>" & vbCrLf
        RepItem = RepItem & "    <Value>=""" & "file:///" & pGrafikFullPfad & """</Value>" & vbCrLf
        RepItem = RepItem & "    </Image>" & vbCrLf
    
        'Im Namen pGrafikFullPfad könnte ein Umlaut stehen: Daher den ganzen string nach UTF8 konvertieren
        Dim u8 As Encoding = Encoding.UTF8
        Dim Bu8 As Byte() = u8.GetBytes(RepItem)
        ReDim Preserve pRepBytesNeu(pRepBytesNeuPtr + Bu8.GetLength(0) + 10)
        Array.Copy(Bu8, 0, pRepBytesNeu, pRepBytesNeuPtr, Bu8.GetLength(0))
        pRepBytesNeuPtr = pRepBytesNeuPtr + Bu8.GetLength(0)
    
      End Sub
    
      '***************************************************************************************************************
      ' RepClose: Vorlage des Berichtes fertigstellen, als Temp. Bericht speichern, 
      '      als .pdf drucken und Temp. Bericht löschen
      '***************************************************************************************************************
      Sub RepClose(ByVal RepName)
    
        'Fertigstellen: Rest der ReportDefinition kopieren
        Dim RestLeng As Long
        RestLeng = pAnzVor - pRepBytesOldPtr
        ReDim Preserve pRepBytesNeu(pRepBytesNeuPtr + RestLeng - 1)
        Array.Copy(pRepBytes, pRepBytesOldPtr, pRepBytesNeu, pRepBytesNeuPtr, RestLeng)
    
        'Als temporären Bericht speichern
        My.Computer.FileSystem.WriteAllBytes(pGrafikPfad & "RepQuer.rdlc", pRepBytesNeu, False)
    
        Dim warnings As Warning() = Nothing
        Dim streamids As String() = Nothing
        Dim mimeType As String = Nothing
        Dim coding As String = Nothing
        Dim extension As String = Nothing
        Dim deviceInfo As String = ""
        Dim bytes() As Byte
    
        Dim RepQuer As New LocalReport
        Dim StrReader As StreamReader = New StreamReader(pGrafikPfad & "RepQuer.rdlc")
        RepQuer.LoadReportDefinition(StrReader)
        RepQuer.EnableExternalImages = True
    
        bytes = RepQuer.Render("PDF", deviceInfo, mimeType, coding, extension, streamids, warnings)
    
        Dim fs As New FileStream(pGrafikPfad & pNeuFileName, FileMode.Create)
        fs.Write(bytes, 0, bytes.Length)
        fs.Close()
    
        ShellExecuteEx(0, pGrafikPfad & pNeuFileName)
    
        'Temporäre Objekte löschen
        StrReader.Dispose()
        'My.Computer.FileSystem.DeleteFile(pGrafikPfad & "ES-Diag.png")
        'My.Computer.FileSystem.DeleteFile(pGrafikPfad & "RepQuer.rdlc")
    
      End Sub
    
     
    End Module
    Gruß Sven
    Montag, 12. Juli 2010 11:51
  • Hallo Sven,

    naja ein klein wenig fehlte da noch an einem übersetzbaren Programm ;-)
    Da aber die Struktur erkennbar war, habe ich mir anderweitig geholfen.
    Problematisch sehe ich vor allem das Konvertieren des XML an,
    Xml <-> String <-> Bytes, da kann schon mal was passieren.

    Und wie oben schon geschrieben kann, man das sehr gut direkt über XML machen.
    Ich habe mich für XML Linq entschieden, via DOM ginge es aber ebenso.
    Visual Basics Erweiterungen erwiesen sich wegen des Namespaces eher als
    hinderlich  und so bin ich ohne ausgekommen ;-)
    Ich habe daraus eine Klasse (kein Modul) erstellt, die sich an Deinen Aufbau anlehnt
    und die Dateinamen usw. weitgehend beibehält (soweit ich sie erraten habe).

    Als Vorlage für den "RepQuer" habe ich einen leeren Bericht verwendet.

    Option Strict On
    Option Explicit On
    Option Infer On
    
    Imports System.Drawing
    Imports System.Diagnostics
    Imports System.IO
    Imports System.Linq
    Imports System.Xml
    Imports System.Xml.Linq
    Imports Microsoft.Reporting.WinForms
    
    Public Class PdfImageReport
      Implements IDisposable
    
    #Region "Konstanten"
      ' Ergänzt LogDirectory für Diagramm
      Private Const DiagrammPfad As String = "ES-Diagramme" ' Verzeichnis für Grafiken
    
      Private Const ReportAusgabeName As String = "RepQuer" ' Ausgabename für den Bericht
      Private Const ReportExtension As String = ".htm"  ' Endung der Vorlagen - Ist in Wirklichkeit eine .rdlc
    
      Private Const DiagrammName As String = "ES-Diag"  ' Basisname für Grafiken
      Private Const DiagrammExtension As String = "png"  ' Verwendet PNG
    
      Private Const PdfName As String = "ES-Rep"  ' Basisname für PDF Dokument
      Private Const PdfExtension As String = "pdf"  ' PDF Ausgabe
    
      ' RDL 2005 Namespace
      Private Shared ReadOnly ReportNamespace As XNamespace = "http://schemas.microsoft.com/sqlserver/reporting/2005/01/reportdefinition"
    #End Region
    
      Private _berichtVerzeichnis As String ' das Berichtsverzeichnis (pRepPfad)
      Private _ausgabeVerzeichnis As String ' das Arbeitsverzeichnis (pLogDirectory)
      Private _tempDateiNamen As New List(Of String)  ' Arbeitsdateien (nach Ausgabe löschen)
    
      Private _reportDocument As XDocument ' Der Bericht als XDocument
      ' Position für die Grafik in cm
      Private _position As New RectangleF(0.25F, 1.4F, 25.5F, 14.7F)
      ' pLeft = "0.25cm"
      ' pTop = "1.4cm"
      ' pWidth = "25.5cm"
      ' pHeight = "14.7cm"
    
      ''' <summary>
      ''' Legt die Position des eingebetteten Bildes fest
      ''' </summary>
      ''' <remarks>
      ''' Alterativ: Werte direkt bei InsertImage
      ''' </remarks>
      Public Property BildPosition() As RectangleF
        Get
          Return _position
        End Get
        Set(ByVal value As RectangleF)
          Me._position = value
        End Set
      End Property
    
      ''' <summary>
      ''' Erstellt eine neue Instanz mit einem Verzeichnis.
      ''' </summary>
      ''' <param name="berichtVerzeichnis">Das Verzeicnis für Berichtsvorlagen.</param>
      ''' <param name="ausgabeVerzeichnis">Das Arbeitsverzeichnis für Ausgaben.</param>
      ''' <remarks>
      ''' berichtVerzeichnis war pRepPfad,
      ''' ausgabeVerzeichnis war plogDirectory.
      ''' </remarks>
      Public Sub New(ByVal berichtVerzeichnis As String, ByVal ausgabeVerzeichnis As String)
        ' Verzeichnisse müssen vorhanden sein.
        If String.IsNullOrEmpty(berichtVerzeichnis) _
          OrElse Not Directory.Exists(berichtVerzeichnis) Then
          Throw New ArgumentException("berichtVerzeichnis")
        End If
    
        If String.IsNullOrEmpty(ausgabeVerzeichnis) _
          OrElse Not Directory.Exists(ausgabeVerzeichnis) Then
          Throw New ArgumentException()
        End If
        Me._berichtVerzeichnis = berichtVerzeichnis
        Me._ausgabeVerzeichnis = ausgabeVerzeichnis
      End Sub
    
      ''' <summary>RepOpen</summary>
      ''' <param name="reportName">Name der Berichtsvorlage.</param>
      Public Sub Open(ByVal reportName As String)
        ' Ausgabeverzeichnis + Diagramm-Verzeichnisname
        Dim grafikPfad As String = Path.Combine(Me._ausgabeVerzeichnis, DiagrammPfad)
        ' und ggf. anlegen
        Directory.CreateDirectory(grafikPfad)
    
        ' Endung ist hier .htm nicht .rdlc
        Dim reportPfad As String = Path.Combine(Me._berichtVerzeichnis, reportName & ReportExtension)
        Me._reportDocument = XDocument.Load(reportPfad)
      End Sub
    
      ''' <summary>Bild über Pfad einfügen</summary>
      ''' <param name="bitmapfileName">Der Dateiname der Bitmap</param>
      Public Sub InsertImage(ByVal bitmapfileName As String)
        InsertImage(DirectCast(Bitmap.FromFile(bitmapfileName), Bitmap))
      End Sub
    
      ''' <summary>RepInsImage</summary>
      ''' <param name="bitmap">Die Bitmap (Image)</param>
      Public Sub InsertImage(ByVal bitmap As Bitmap)
        Dim root As XElement = Me._reportDocument.Root
    
        Dim bodyName As XName = ReportNamespace + "Body"
        Dim reportItemsName As XName = ReportNamespace + "ReportItems"
    
        Dim grafikDatei As String = GetGrafikDateiName()
    
        ' einen Klon anlegen und als Datei speichern
        bitmap = DirectCast(bitmap.Clone(), Bitmap)
        bitmap.Save(grafikDatei, System.Drawing.Imaging.ImageFormat.Png)
        bitmap.Dispose()
    
        Dim body = root.Elements(bodyName).FirstOrDefault
        If body Is Nothing Then
          Throw New InvalidOperationException("Kein Body Element gefunden.")
        End If
    
        Dim reportItems = body.Element(reportItemsName)
        If reportItems Is Nothing Then
          reportItems = New XElement(reportItemsName)
          body.Add(reportItems)
        End If
    
        ' Laufende Nummerierung
        Dim count As Integer = reportItems.Nodes.Count
        reportItems.Add(CreateImageElement(grafikDatei, count))
      End Sub
    
      Public Sub RenderPDF()
        If Me._reportDocument Is Nothing Then
          Throw New InvalidOperationException("Zuerst muß der Bericht geöffnet werden.")
        End If
    
        ' Name für den generierten Bericht
        Dim reportDateiname = GetReportDateiName()
        Me._reportDocument.Save(reportDateiname)
        ' Freigeben
        Me._reportDocument = Nothing
    
        Dim pdfDateiName = GetPdfDateiName()
    
        Using reportQuer As New LocalReport()
          Using reportStream As New StreamReader(reportDateiname)
            reportQuer.LoadReportDefinition(reportStream)
          End Using
          reportQuer.EnableExternalImages = True
    
          Dim reportBytes = reportQuer.Render("PDF", "", "", "", "", Nothing, Nothing)
    
          File.WriteAllBytes(pdfDateiName, reportBytes)
        End Using
    
        Dim readerProcess = Process.Start(pdfDateiName)
        'readerProcess.WaitForExit()
      End Sub
    
    
      Private Function CreateImageElement(ByVal grafikDatei As String, ByVal index As Integer) As XElement
        Dim elementName As New XAttribute("Name", InvariantFormat("Image{0}", index + 1))
    
        Dim imageElement = New XElement(ReportNamespace + "Image", _
          New XElement(ReportNamespace + "Sizing", "Fit"), _
          New XElement(ReportNamespace + "Source", "External"), _
          New XElement(ReportNamespace + "Left", FormatZentimeter(BildPosition.Left)), _
          New XElement(ReportNamespace + "Top", FormatZentimeter(BildPosition.Top)), _
          New XElement(ReportNamespace + "Width", FormatZentimeter(BildPosition.Width)), _
          New XElement(ReportNamespace + "Height", FormatZentimeter(BildPosition.Height)), _
          New XElement(ReportNamespace + "ZIndex", index + 1), _
          New XElement(ReportNamespace + "Value", New Uri(grafikDatei, UriKind.Absolute)))
        imageElement.Add(elementName)
        Return imageElement
      End Function
    
    #Region "Dateinamen erstellen"
      ''' <summary>
      ''' Ermitteln des Dateinamens für die Bericht.
      ''' </summary>
      Private Function GetReportDateiName() As String
        Return GetDateiName(ReportAusgabeName, "rdlc")
      End Function
    
      ''' <summary>
      ''' Ermitteln eines Dateinamens für die Grafik.
      ''' </summary>
      Private Function GetGrafikDateiName() As String
        Return GetDateiName(DiagrammName, DiagrammExtension)
      End Function
    
      ''' <summary>
      ''' Ermitteln eines Dateinamens für die PDF Datei.
      ''' </summary>
      ''' <remarks>Sollte der Vorlage GetNeuFileName ähnlich sein</remarks>
      Private Function GetPdfDateiName() As String
        Return GetDateiName(PdfName, PdfExtension, False)
      End Function
    
      Private Function GetDateiName(ByVal name As String, ByVal endung As String, Optional ByVal isTemp As Boolean = True) As String
        ' hier gleiches Verzeichnis wie für Grafik
        Dim verzeichnis As String = Path.Combine(Me._ausgabeVerzeichnis, DiagrammPfad)
        ' ggf. anlegen
        Directory.CreateDirectory(verzeichnis)
    
        ' und Dateiname anfügen
        For index As Integer = 1 To 9999
          Dim dateiName As String = InvariantFormat( _
            "{0}{1:0000}.{2}", _
            name, index, endung)
    
          dateiName = Path.Combine(verzeichnis, dateiName)
          If Not File.Exists(dateiName) Then
            ' Dateinamen merken für spätere Aufräumarbeiten
            If isTemp Then
              Me._tempDateiNamen.Add(dateiName)
            End If
            Return dateiName
          End If
        Next
        ' Ein Aufräumen wäre nicht schlecht...
        Throw New InvalidOperationException("Es konnte kein eindeutiger Dateiname ermittelt werden.")
      End Function
    #End Region
    
      ''' <summary>
      ''' Invariante Formatierung für Berichtsparameter etc.
      ''' </summary>
      ''' <remarks>Setzt InvariantCulture für die Formatierung von Zahlen usw. ein.</remarks>
      Private Shared Function FormatZentimeter(ByVal value As Single) As String
        Return InvariantFormat("{0:F2}cm", value)
      End Function
    
      ''' <summary>
      ''' Invariante Formatierung für Berichtsparameter etc.
      ''' </summary>
      ''' <remarks>Setzt InvariantCulture für die Formatierung von Zahlen usw. ein.</remarks>
      Private Shared Function InvariantFormat(ByVal format As String, ByVal ParamArray args As Object()) As String
        Return String.Format(System.Globalization.CultureInfo.InvariantCulture, _
            format, args)
      End Function
    
      Public Sub Close()
        Me.Dispose(True)
      End Sub
    
    #Region "IDisposable Support"
      Public Sub Dispose() Implements IDisposable.Dispose
        Dispose(True)
        GC.SuppressFinalize(Me)
      End Sub
    
      Protected Overridable Sub Dispose(ByVal disposing As Boolean)
        ' Temporäre Dateien löschen
        For Each dateiname In Me._tempDateiNamen
          Try
            File.Delete(dateiname)
          Catch
          End Try
        Next
        Me._tempDateiNamen.Clear()
      End Sub
    #End Region
    
    End Class
    
    

    Die Verwendung wäre z. B.:

        Using report As New PdfImageReport("C:\TEMP", "C:\TEMP")
          report.Open("ImageReport")
          report.InsertImage("E:\MEDIA\BILDER\KATZEN_01.JPG")
          report.RenderPDF()
        End Using
    
    
    Bei mir erschien das Mietzekätzchen, das häufiger für Tests herhalten muß,
    im Akrobat Reader.

    Anmerkkung am Rande: Dispose (bzw. Close) löscht die temporären Dateien.

    Inwieweit Probleme bei komplexeren Berichtsvorlagen auftreten können
    (meiner war wie gesagt leer) habe ich jetzt nicht im Detail geprüft.
    Eine Alternative anstatt XML Direkt wäre eine dynamische Generierung via
    Klassenmodel, wie sie auf http://www.gotreportviewer.com/ beispielhaft gezeigt wird.

    Sollten sich die Bilder bei Dir immer noch nicht zeigen wäre ein weiterer
    weg sie als Parameter einzubetten, siehe z. B.:

    http://stackoverflow.com/questions/36693/how-can-i-render-a-png-image-as-a-memory-stream-onto-a-net-reportviewer-repor

    Gruß Elmar

    Dienstag, 13. Juli 2010 16:38
    Beantworter
  • Hallo Elmar,

    meine Reaktion kommt leider etwas spät (anderes Projekt dazwischengeschoben, sorry).
    Mittlerweile habe ich aber einen Weg gefunden: Das Image einfach als .bmp speichern und als .bmp rendern lassen.
    Warum das beim rendern mit .png nicht klappt, habe ich nicht herausgefunden.

    Trotzdem noch einmal herzlichen Dank für Deine Mühe.

    Gruß
    Sven

    Dienstag, 31. August 2010 10:06
  • Hallo Sven,

    ein mögliche Erklärung für das PNG Problem wäre:
    FIX: When you export a SQL Server 2005 Reporting Services report that contains an image to a .pdf format file,
    the .pdf file is displayed incorrectly

    und eine weitere Erklärung zu finden in: http://blogs.msdn.com/b/donovans/archive/2007/07/20/reporting-services-pdf-renderer-faq.aspx
    unter I'm not using the controls in local mode and my PDFs are still big.  Why?

    Danach wäre JPEG eine Alternative.

    Gruß Elmar

    Dienstag, 31. August 2010 11:43
    Beantworter