c# - ¿Cómo imprimir un archivo de texto en una impresora térmica con PrintDocument?
winforms report (3)
¿Su impresora térmica es compatible con OPOS?
Si es así, le recomiendo que intente usar Microsoft Point of Service
Estoy creando una aplicación usando C # con Winforms y ahora necesito imprimir el recibo de venta en una impresora térmica.
Para hacer esto, estoy creando un archivo de texto y leyéndolo con
PrintDocument
pero no puedo hacerlo porque no sé cómo configurar el tamaño del papel, alinear el centro del texto en el papel y otras configuraciones.
Cuando imprimo, el archivo de texto se imprime, pero todo está desordenado, y después de la impresión final, el papel no se detiene.
Cómo podría hacer esto ?
molesto.
private PrintDocument printDocument = new PrintDocument();
private static String RECEIPT = Environment.CurrentDirectory + @"/comprovantes/comprovante.txt";
private String stringToPrint = "";
private void button1_Click(object sender, EventArgs e)
{
generateReceipt();
printReceipt();
}
private void generateReceipt()
{
FileStream fs = new FileStream(COMPROVANTE, FileMode.Create);
StreamWriter writer = new StreamWriter(fs);
writer.WriteLine("==========================================");
writer.WriteLine(" NOME DA EMPRESA AQUI ");
writer.WriteLine("==========================================");
writer.Close();
fs.Close();
}
private void printReceipt()
{
FileStream fs = new FileStream(COMPROVANTE, FileMode.Open);
StreamReader sr = new StreamReader(fs);
stringToPrint = sr.ReadToEnd();
printDocument.PrinterSettings.PrinterName = DefaultPrinter.GetDefaultPrinterName();
printDocument.PrintPage += new PrintPageEventHandler(printPage);
printDocument.Print();
sr.Close();
fs.Close();
}
private void printPage(object sender, PrintPageEventArgs e)
{
int charactersOnPage = 0;
int linesPerPage = 0;
Graphics graphics = e.Graphics;
// Sets the value of charactersOnPage to the number of characters
// of stringToPrint that will fit within the bounds of the page.
graphics.MeasureString(stringToPrint, this.Font,
e.MarginBounds.Size, StringFormat.GenericTypographic,
out charactersOnPage, out linesPerPage);
// Draws the string within the bounds of the page
graphics.DrawString(stringToPrint, this.Font, Brushes.Black,
e.MarginBounds, StringFormat.GenericTypographic);
// Remove the portion of the string that has been printed.
stringToPrint = stringToPrint.Substring(charactersOnPage);
// Check to see if more pages are to be printed.
e.HasMorePages = (stringToPrint.Length > 0);
}
Estoy intentando crear este modelo de recibo.
Usando RDLC, pero es muy lento para comenzar a imprimir . Estoy siguiendo este example
//works but slow
private void Run() {
LocalReport report = new LocalReport();
report.ReportPath = @"../../reports/ReciboDeVenda.rdlc";
Export(report);
Print();
}
private void Export(LocalReport report) {
string deviceInfo =
@"<DeviceInfo>
<OutputFormat>EMF</OutputFormat>
<PageWidth>8.5in</PageWidth>
<PageHeight>11in</PageHeight>
<MarginTop>0.25in</MarginTop>
<MarginLeft>0.25in</MarginLeft>
<MarginRight>0.25in</MarginRight>
<MarginBottom>0.25in</MarginBottom>
</DeviceInfo>";
Warning[] warnings;
m_streams = new List<Stream>();
report.Render("Image", deviceInfo, CreateStream,
out warnings);
foreach (Stream stream in m_streams)
stream.Position = 0;
}
private Stream CreateStream(string name, string fileNameExtension,
Encoding encoding, string mimeType, bool willSeek) {
Stream stream = new MemoryStream();
m_streams.Add(stream);
return stream;
}
private void Print() {
if (m_streams == null || m_streams.Count == 0)
throw new Exception("Error: no stream to print.");
PrintDocument printDoc = new PrintDocument();
printDoc.PrinterSettings.PrinterName = DefaultPrinter.GetDefaultPrinterName();
if (!printDoc.PrinterSettings.IsValid) {
throw new Exception("Error: cannot find the default printer.");
}else {
printDoc.PrintPage += new PrintPageEventHandler(PrintPage);
m_currentPageIndex = 0;
printDoc.Print();
}
}
private void PrintPage(object sender, PrintPageEventArgs ev) {
Metafile pageImage = new Metafile(m_streams[m_currentPageIndex]);
// Adjust rectangular area with printer margins.
Rectangle adjustedRect = new Rectangle(
ev.PageBounds.Left - (int)ev.PageSettings.HardMarginX,
ev.PageBounds.Top - (int)ev.PageSettings.HardMarginY,
ev.PageBounds.Width,
ev.PageBounds.Height);
// Draw a white background for the report
ev.Graphics.FillRectangle(Brushes.White, adjustedRect);
// Draw the report content
ev.Graphics.DrawImage(pageImage, adjustedRect);
// Prepare for the next page. Make sure we haven''t hit the end.
m_currentPageIndex++;
ev.HasMorePages = (m_currentPageIndex < m_streams.Count);
}
Si envía una cadena simple a su
printPage(object sender, PrintEventArgs e)
, solo imprimirá texto sin formato en la misma fuente que se ve ''desordenada'' (como lo llamó) Si desea que se imprima bien formateado con diferentes fuentes (negrita, regular), debe hacerlo todo manualmente:
List<string> itemList = new List<string>()
{
"201", //fill from somewhere in your code
"202"
};
private void printPage( object sender, PrintPageEventArgs e )
{
Graphics graphics = e.Graphics;
Font regular = new Font( FontFamily.GenericSansSerif, 10.0f, FontStyle.Regular );
Font bold = new Font( FontFamily.GenericSansSerif, 10.0f, FontStyle.Bold );
//print header
graphics.DrawString( "FERREIRA MATERIALS PARA CONSTRUCAO LTDA", bold, Brushes.Black, 20, 10 );
graphics.DrawString( "EST ENGENHEIRO MARCILAC, 116, SAO PAOLO - SP", regular, Brushes.Black, 30, 30 );
graphics.DrawString( "Telefone: (11)5921-3826", regular, Brushes.Black, 110, 50 );
graphics.DrawLine( Pens.Black, 80, 70, 320, 70 );
graphics.DrawString( "CUPOM NAO FISCAL", bold, Brushes.Black, 110, 80 );
graphics.DrawLine( Pens.Black, 80, 100, 320, 100 );
//print items
graphics.DrawString( "COD | DESCRICAO | QTY | X | Vir Unit | Vir Total |", bold, Brushes.Black, 10, 120 );
graphics.DrawLine( Pens.Black, 10, 140, 430, 140 );
for( int i = 0; i < itemList.Count; i++ )
{
graphics.DrawString( itemList[i].ToString(), regular, Brushes.Black, 20, 150 + i * 20 );
}
//print footer
//...
regular.Dispose();
bold.Dispose();
// Check to see if more pages are to be printed.
e.HasMorePages = ( itemList.Count > 20 );
}
Posibles mejoras en mi ejemplo:
-
Centrar las cadenas de encabezado podría hacerse mejor usando
graphics.MeasureString()
. - La lista de elementos debería ser una lista de una clase de negocios en lugar de una cadena simple
Debido a que todo esto es mucho trabajo, realmente debería considerar usar RDLC o algún software de terceros para diseñar sus documentos.
Si quiero crear un informe yo mismo, en lugar de intentar crear un archivo de texto, usaré HTML para renderizar.
También para simplificar la lógica de representación y mezclar etiquetas html y datos, usaría
plantillas de texto en tiempo de ejecución T4
.
Entonces puedo pasar datos a la plantilla html y renderizar el informe simplemente.
Entonces es suficiente asignar la cadena de salida a una propiedad
DocumentText
de un
WebBrowser
y llamar a su método
Print
.
Descargar
Puede clonar o descargar un ejemplo de trabajo de r-aghaei/HtmlUsingRuntimeT4 .
¿Por qué HTML?
Debido a un formato simple y flexible.
Puede utilizar todo el poder de las etiquetas html y los estilos CSS para formatear el texto.
Es realmente mejor que usar
DrawString
.
¿Por qué plantillas de texto en tiempo de ejecución?
Debido a que hace que mezclar datos y html sea realmente fácil y puede usar el lenguaje C # para realizar algunas tareas como calcular la suma, iterar sobre los registros del modelo, etc.
Utiliza el motor de plantillas
T4
y le permite usar la plantilla en tiempo de ejecución y alimentar datos a la plantilla.
Ejemplo
1- Agregar un modelo al proyecto:
using System;
using System.Collections.Generic;
namespace Sample
{
public class ReportModel
{
public string CustomerName { get; set; }
public DateTime Date { get; set; }
public List<OrderItem> OrderItems { get; set; }
}
public class OrderItem
{
public string Name { get; set; }
public int Price { get; set; }
public int Count { get; set; }
}
}
2- Agregue una plantilla de texto en tiempo de ejecución (también conocida como plantilla de texto preprocesada) al proyecto y
ReportTemplate.tt
nombre
ReportTemplate.tt
.
Ábrelo y agrega dicho código:
<#@ template language="C#"#>
<#@ import namespace="System.Linq" #>
<#@ import namespace="System.Collections.Generic" #>
<#@ parameter name="Model" type="Sample.ReportModel"#>
<html>
<head>
<title></title>
<style type="text/css">
body { font-family: Calibri;width:400px;}
table { text-align:center; }
.container {width:400px; height:100%;}
</style>
</head>
<body>
<div class="container">
<h1 style="text-align:center;">Order</h1>
<hr/>
<table style="width:100%">
<tr>
<td>Customer: <#=Model.CustomerName#></td>
<td>Order Date: <#=Model.Date#></td>
</tr>
</table>
<hr/>
<table style="width:100%">
<tr><th>Index</th><th>Name</th><th>Price</th><th>Count</th><th>Sum</th></tr>
<#
int index =1;
foreach(var item in Model.OrderItems)
{
#>
<tr>
<td><#=index#></td>
<td><#=item.Name#></td>
<td><#=item.Price#></td>
<td><#=item.Count#></td>
<td><#=item.Count * item.Price#></td>
</tr>
<#
index++;
}
var total= Model.OrderItems.Sum(x=>x.Count * x.Price);
#>
<tr><td></td><td></td><td></td><th>Total:</th><th><#=total#></th></tr>
</table>
<div>
</body>
</html>
3- Ponga un control
WebBrowser
en el formulario y escriba dicho código donde desee generar el informe:
var rpt = new ReportTemplate();
rpt.Session = new Dictionary<string, object>();
rpt.Session["Model"] = new Sample.ReportModel
{
CustomerName = "Reza",
Date = DateTime.Now,
OrderItems = new List<Sample.OrderItem>()
{
new Sample.OrderItem(){Name="Product 1", Price =100, Count=2 },
new Sample.OrderItem(){Name="Product 2", Price =200, Count=3 },
new Sample.OrderItem(){Name="Product 3", Price =50, Count=1 },
}
};
rpt.Initialize();
this.webBrowser1.DocumentText= rpt.TransformText();
4- Si desea imprimir el informe, puede llamar al:
this.webBrowser1.Print();
Debe llamar a
Print
después de completar el documento.
Entonces, si desea imprimir directamente sin mostrar el resultado al usuario, puede manejar el evento
DocumentCompleted
y llamar a
Print
allí.
Aquí está el resultado: