Benutzer mit den meisten Antworten
Local Report zeigt keine Grafik an

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
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 BasicGruß Elmar
- Als Antwort markiert Thorsten DörflerModerator Samstag, 31. Juli 2010 15:45
-
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.:
Bei mir erschien das Mietzekätzchen, das häufiger für Tests herhalten muß,Using report As New PdfImageReport("C:\TEMP", "C:\TEMP") report.Open("ImageReport") report.InsertImage("E:\MEDIA\BILDER\KATZEN_01.JPG") report.RenderPDF() End Using
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.:Gruß Elmar
- Als Antwort vorgeschlagen Thorsten DörflerModerator Donnerstag, 15. Juli 2010 10:50
- Als Antwort markiert Thorsten DörflerModerator Samstag, 31. Juli 2010 15:44
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 BasicGruß Elmar
- Als Antwort markiert Thorsten DörflerModerator Samstag, 31. Juli 2010 15:45
-
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 -
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 -
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.:
Bei mir erschien das Mietzekätzchen, das häufiger für Tests herhalten muß,Using report As New PdfImageReport("C:\TEMP", "C:\TEMP") report.Open("ImageReport") report.InsertImage("E:\MEDIA\BILDER\KATZEN_01.JPG") report.RenderPDF() End Using
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.:Gruß Elmar
- Als Antwort vorgeschlagen Thorsten DörflerModerator Donnerstag, 15. Juli 2010 10:50
- Als Antwort markiert Thorsten DörflerModerator Samstag, 31. Juli 2010 15:44
-
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 -
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 incorrectlyund 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