Impresión de informes RDLC

 

[+] El principal problema que me encontré con el diseño de reportes con los informes rdlc de Visual Studio 2010, es que el control ReportViewer no cuenta con un método que envíe directamente el informe a imprimir

Si bien es cierto que puedes imprimir y exportar un informe mediante los comandos del propio ReportViewer, en muchas ocasiones y por requerimientos del proceso o del cliente es necesario la impresión directa del informe.

Navegando por internet y preguntando a San Google encontré una solución que resuelve el problema, de inicio la solución resulta un tanto confusa; en éste post no haré un análisis, ya que viéndolo trabajar puede uno ver su funcionamiento con claridad.

Así que… entremos en materia; el código y para no saturar el post dejo el link de la solución aquí. Personalmente le he hecho unas pequeñas modificación para crear una clase y que pudiera ser reusable, aquí lo dejo

   1: public class ControladorImpresion : IDisposable
   2: {
   3:  
   4:     #region Atributos
   5:     
   6:     private int m_currentPageIndex;
   7:     private IList<Stream> m_streams;
   8:     
   9:     #endregion
  10:     
  11:     #region Métodos privados
  12:     
  13:     private Stream CreateStream(string name, string fileNameExtension, Encoding encoding, string mimeType, bool willSeek)
  14:     {
  15:         //Routine to provide to the report renderer, in order to save an image for each page of the report.
  16:         Stream stream = new FileStream(@"..\..\" + name + "." + fileNameExtension, FileMode.Create);
  17:         m_streams.Add(stream);
  18:         return stream;
  19:     }
  20:     
  21:     private void Export(LocalReport report)
  22:     {
  23:         //Export the given report as an EMF (Enhanced Metafile) file.
  24:         string deviceInfo =
  25:           "<DeviceInfo>" +
  26:           "  <OutputFormat>EMF</OutputFormat>" +
  27:           "  <PageWidth>8.5in</PageWidth>" +
  28:           "  <PageHeight>11in</PageHeight>" +
  29:           "  <MarginTop>0.25in</MarginTop>" +
  30:           "  <MarginLeft>0.25in</MarginLeft>" +
  31:           "  <MarginRight>0.25in</MarginRight>" +
  32:           "  <MarginBottom>0.25in</MarginBottom>" +
  33:           "</DeviceInfo>";
  34:         Warning[] warnings;
  35:         m_streams = new List<Stream>();
  36:         report.Render("Image", deviceInfo, CreateStream, out warnings);
  37:         foreach (Stream stream in m_streams)
  38:         { stream.Position = 0; }
  39:     }
  40:     
  41:     private void PrintPage(object sender, PrintPageEventArgs ev)
  42:     {
  43:         //Handler for PrintPageEvents
  44:         Metafile pageImage = new Metafile(m_streams[m_currentPageIndex]);
  45:         ev.Graphics.DrawImage(pageImage, ev.PageBounds);
  46:         m_currentPageIndex++;
  47:         ev.HasMorePages = (m_currentPageIndex < m_streams.Count);
  48:     }
  49:  
  50:     private void Print()
  51:     {
  52:         //
  53:         PrintDocument printDoc;
  54:  
  55:         String printerName = ImpresoraPredeterminada();
  56:         if (m_streams == null || m_streams.Count == 0)
  57:         { return; }
  58:  
  59:         printDoc = new PrintDocument();
  60:         printDoc.PrinterSettings.PrinterName = printerName;
  61:         if (!printDoc.PrinterSettings.IsValid)
  62:         {
  63:             string msg = String.Format("Can't find printer \"{0}\".", printerName);
  64:             MessageBox.Show(msg, "Print Error");
  65:             return;
  66:         }
  67:         printDoc.PrintPage += new PrintPageEventHandler(PrintPage);
  68:         printDoc.Print();
  69:     }
  70:     
  71:     private string ImpresoraPredeterminada()
  72:     {
  73:         //
  74:  
  75:         for(Int32 i = 0 ; i < PrinterSettings.InstalledPrinters.Count ; i++)
  76:         {
  77:             PrinterSettings a = new PrinterSettings();
  78:             a.PrinterName = PrinterSettings.InstalledPrinters[i].ToString();
  79:             if (a.IsDefaultPrinter)
  80:             { return PrinterSettings.InstalledPrinters[i].ToString(); }
  81:         }
  82:         return "";
  83:     }
  84:  
  85:     #endregion
  86:     
  87:     #region Métodos públicos
  88:     
  89:     public void Imprimir(LocalReport argReporte)
  90:     {
  91:         //
  92:         Export(argReporte);
  93:         m_currentPageIndex = 0;
  94:         Print();
  95:     }
  96:     
  97:     #endregion
  98:  
  99:     #region Soporte para implementación de interfaces
 100:     
 101:     #region IDisposable
 102:     
 103:     public void Dispose()
 104:     {
 105:         if (m_streams != null)
 106:         {
 107:             foreach (Stream stream in m_streams)
 108:             {  stream.Close(); }
 109:             m_streams = null;
 110:         }
 111:     }
 112:     
 113:     #endregion
 114:     
 115:     #endregion
 116:  
 117: }


Los cambios que realicé fueron básicamente para habilitar el código para imprimir un LocalReport enviandolo directamentamente a la impresora predeterminada:

Los cambios serían:



  1. Método público llamado Imprimir que recibe un parámetro del tipo LocalReport.
  2. Método privado llamado ImpresoraPredeterminada que busca la impresora predeterminada en la colección de impresoras instaladas regresando una cadena que representa el nombre de dicha impresora.

El uso de la clase sería:



   1: ControladorImpresion objImpresion = new ControladorImpresion();
   2: objImpresion.Imprimir(miReporteLoca);

Naturalmente el argumento miReporteLocal ya está creado, instanciado, con sus parámetros cargados y así con sus datos, independientemente de como se llene el informe, procuré que la clase tenga sólo la responsabilidad de imprimir el informe a la impresora predeterminada.


Hasta aquí dejo el post, espero les sea de utilidad y les permita ir temprano a casa…


Saludos…!

Comentarios

  1. Perfecto. El código de la clase en VB seria:
    (El codigo se transformó a vb automaticamente, añadí los imports y sustituí una linea de codigo:
    AddHandler printDoc.PrintPage, New PrintPageEventHandler(AddressOf PrintPage)
    'printDoc.PrintPage += New PrintPageEventHandler(AddressOf PrintPage)


    Codigo:

    Imports System
    Imports System.IO
    Imports Microsoft.Reporting.WebForms
    Imports System.Drawing.Printing
    Imports System.Drawing.Imaging






    Public Class ControladorImpresion
    Implements IDisposable


    #Region "Atributos"

    Private m_currentPageIndex As Integer
    Private m_streams As IList(Of Stream)

    #End Region

    #Region "Métodos privados"

    Private Function CreateStream(name As String, fileNameExtension As String, encoding As Encoding, mimeType As String, willSeek As Boolean) As Stream
    'Routine to provide to the report renderer, in order to save an image for each page of the report.
    Dim stream As Stream = New FileStream("..\..\" & name & "." & fileNameExtension, FileMode.Create)
    m_streams.Add(stream)
    Return stream
    End Function

    Private Sub Export(report As LocalReport)
    'Export the given report as an EMF (Enhanced Metafile) file.
    Dim deviceInfo As String = "" & " EMF" & " 8.5in" & " 11in" & " 0.25in" & " 0.25in" & " 0.25in" & " 0.25in" & ""
    Dim warnings As Warning()
    m_streams = New List(Of Stream)()
    report.Render("Image", deviceInfo, AddressOf CreateStream, warnings)
    For Each stream As Stream In m_streams
    stream.Position = 0
    Next
    End Sub

    Private Sub PrintPage(sender As Object, ev As PrintPageEventArgs)
    'Handler for PrintPageEvents
    Dim pageImage As New Metafile(m_streams(m_currentPageIndex))
    ev.Graphics.DrawImage(pageImage, ev.PageBounds)
    m_currentPageIndex += 1
    ev.HasMorePages = (m_currentPageIndex < m_streams.Count)
    End Sub

    ResponderEliminar
  2. IMPORTANTE: Y ESTO TAMBIEN (no me lo metia en un sólo post)

    Private Sub Print()
    '
    Dim printDoc As PrintDocument

    Dim printerName As [String] = ImpresoraPredeterminada()
    If m_streams Is Nothing OrElse m_streams.Count = 0 Then
    Return
    End If

    printDoc = New PrintDocument()
    printDoc.PrinterSettings.PrinterName = printerName
    If Not printDoc.PrinterSettings.IsValid Then
    Dim msg As String = [String].Format("Can't find printer ""{0}"".", printerName)
    'MessageBox.Show(msg, "Print Error")
    Throw New Exception("Error al imprimir en clase PRINTER")
    Return
    End If

    AddHandler printDoc.PrintPage, New PrintPageEventHandler(AddressOf PrintPage)
    'printDoc.PrintPage += New PrintPageEventHandler(AddressOf PrintPage)
    printDoc.Print()
    End Sub

    Private Function ImpresoraPredeterminada() As String
    '

    For i As Int32 = 0 To PrinterSettings.InstalledPrinters.Count - 1
    Dim a As New PrinterSettings()
    a.PrinterName = PrinterSettings.InstalledPrinters(i).ToString()
    If a.IsDefaultPrinter Then
    Return PrinterSettings.InstalledPrinters(i).ToString()
    End If
    Next
    Return ""
    End Function

    #End Region

    #Region "Métodos públicos"

    Public Sub Imprimir(argReporte As LocalReport)
    '
    Export(argReporte)
    m_currentPageIndex = 0
    Print()
    End Sub

    #End Region

    #Region "Soporte para implementación de interfaces"

    #Region "IDisposable"

    Public Sub Dispose()
    If m_streams IsNot Nothing Then
    For Each stream As Stream In m_streams
    stream.Close()
    Next
    m_streams = Nothing
    End If
    End Sub

    #End Region

    #End Region

    Public Sub Dispose1() Implements IDisposable.Dispose

    End Sub
    End Class

    ResponderEliminar
  3. Buenas les comento que logre hacer q imprima en impresoras normales pero en las de punto de venta sale todas las letras pegadas hacia la izquierda del recibo alguien sabrá como corregirlo

    Gracias

    ResponderEliminar
  4. Muchas Gracias buscaba un codigo como este

    ResponderEliminar

Publicar un comentario

Entradas populares