• 备忘录:基于rdlc报表实现打印产品标签


    志铭-2022年1月13日 21:40:39

    0. 背景说明

    • 产品快递箱 包装箱需要贴一张箱标,标注产品的如下一些信息

      • 产品检验信息
      • 产品公司信息
      • 产品SKU集小程序商城二维码链接
    • 最终测试Demo效果

      最终效果



    1. 条形码生成

    使用ZXing.NET 生成产品的批次和SKU的条形码,简单的封装一个辅助类用于创建条形码

       using ZXing;
       using ZXing.Common;
    
       public static class BarCodeHelper
       {
           /// <summary>
           /// 创建条形码
           /// </summary>
           /// <param name="barCodeNo">条码</param>
           /// <param name="height">高度</param>
           /// <param name="width">宽度</param>
           /// <returns>图片字节数组</returns>
           public static byte[] CreateBarCode(string barCodeNo, int height = 120, int width = 310)
           {
               EncodingOptions encoding = new EncodingOptions()
               {
                   Height = height,
                   Width = width,
                   Margin = 1,
                   PureBarcode = false//在条码下显示条码,true则不显示
               };
               BarcodeWriter wr = new BarcodeWriter()
               {
                   Options = encoding,
                   Format = BarcodeFormat.CODE_128,
    
               };
               Bitmap img = wr.Write(barCodeNo);
    
               //保存在当前项目路径下
               // string filepath = AppDomain.CurrentDomain.BaseDirectory + "\\" + barCodeNo + ".jpg";
               // img.Save(filepath, ImageFormat.Jpeg);
    
               return BitmapToBytes(img);
           }
    
           /// <summary>
           /// 位图转为字节数组
           /// </summary>
           /// <param name="bitmap">位图</param>
           /// <returns></returns>
    
           private static byte[] BitmapToBytes(Bitmap bitmap)
           {
               using (MemoryStream ms = new MemoryStream())
               {
                   bitmap.Save(ms, ImageFormat.Gif);
                   byte[] byteImage = new byte[ms.Length];
                   byteImage = ms.ToArray();
                   return byteImage;
               }
           }
    
       }
    


    2. 获取产品的小程序码

    • 根据当前产品的SKU,动态的获取当前产品的微信小程序商城中页面的小程序码

    • 获取小程序码,适用于需要的码数量极多的业务场景。通过该接口生成的小程序码,永久有效,数量暂无限制

      • POST https://api.weixin.qq.com/wxa/getwxacodeunlimit?access_token=ACCESS_TOKEN
      • 具体参数及返回值参考:微信小程序接口文档
    • WebRequest发送POST请求到微信小程序接口,接受返回的图片butter

      /// <summary>
      /// 发送http Post请求图片
      /// </summary>
      /// <param name="url">请求地址</param>
      /// <param name="messsage">请求参数</param>
      /// <returns>图片的字节数组</returns>
      public static byte[] PostRequestReturnImage(string url, string messsage)
      {
          byte[] byteData = Encoding.UTF8.GetBytes(messsage);
          HttpWebRequest webRequest = (HttpWebRequest)WebRequest.Create(url);
          webRequest.Method = "POST";
          webRequest.ContentType = "image/jpeg";
          webRequest.ContentLength = byteData.Length;
          webRequest.AddRange(0, 10000000);
          using (Stream stream = webRequest.GetRequestStream())
          {
              stream.Write(byteData, 0, byteData.Length);
          }
          using (Stream stream = webRequest.GetResponse().GetResponseStream())
          {
              using (BinaryReader br = new BinaryReader(stream))
              {
                  byte[] butter = br.ReadBytes(10000000);
                  return butter;
              }
          }
      }
      


    3. 报表设计器设计标签模版

    3.1 为WinForm控件工具箱添加ReportViewer控件

    • NuGet:PM>Install-Package Microsoft.ReportingServices.ReportViewerControl.WinForms -Pre

      • 注意ReportView有许多依赖,执行上述命令执行安装,不要在在NuGet管理界面搜索安装,减少不必要的麻烦
    • 安装后,在winform工具箱 Micsoft SQL Server选项卡下有ReportView 控件

    3.2 为VS2019安装RDLC报表项目模版

    使用的VS2019默认没有报表项目,需要安装扩展:Microsoft Reporting Designer

    • 扩展-->管理扩展-->联机搜索:Microsoft Rdlc Report Designer

    • 之后右键项目-->添加 会显示:报表

    3.3 创建报表文件

    • 创建报表文件MyReport.rdlc,注意事项

      • 打开报表文件,会自动显示报表数据窗口,没有在重新打开VS
      • 报表数据窗口-->数据集-->添加数据集
        • 定义数据对象:ReportModel类
        • 设置数据源名称:ReportModelObject
        • 数据源:选择对象ReportModel
    • 布局:我使用列表布局,右键插入列表

    • 数据:绑定数据源ReportModelObject的字段

    • 关于图像:首先右键插入图像,图像属性设置:

      • 工具提示:value=System.Convert.FromBase64String(Fields!Image1.Value)
      • 数据源:数据库
      • 使用字段:ReportModel中的图像字段,比如我这里是Image1字段(string 类型)
      • MIME类型:image/jpeg

    3.4 ReportView初始化

        private void InitReport()
        {
            myReportViewer.LocalReport.ReportPath = "MyReport.rdlc";//报表文件名称
            ReportDataSource rds = new ReportDataSource
            {
                Name = "ReportModelObject",
                Value = GetDataSource()
            };
            myReportViewer.LocalReport.DataSources.Add(rds);
            myReportViewer.RefreshReport();
        }
    
        /// <summary>
        /// 测试使用的数据源
        /// </summary>
        /// <returns></returns>
        private List<ReportModel> GetDataSource()
        {
            return new List<ReportModel>()
                {
                    new ReportModel()
                    {
                        //这里就是将图片的字节数组转为字符串,从而实现绑定在报表中
                        Image1=Convert.ToBase64String(BarCodeHelper.CreateBarCode("123456789012")),
                    }
                };
        }
    


    4. 直接打印ReportView中报表,不要弹出选择打印机窗口

    ViewReport控件上自带的打印按钮,点击会弹出选择打印机的窗口,不希望如此

    在配置文件中设置默认打印机

    <configuration>
    	<appSettings >
    		<add key="printer" value ="打印机名称"/>
    	</appSettings>	
    </configuration>
    

    封装一个单独打印的辅助类,这个解放方法非我原创,是参考博文空白画映:C# WinForm RDLC报表不预览直接连续打印

        public class PrintHelper
        {
            /// <summary>
            /// 用来记录当前打印到第几页了
            /// </summary>
            private int m_currentPageIndex;
    
            /// <summary>
            /// 声明一个Stream对象的列表用来保存报表的输出数据,LocalReport对象的Render方法会将报表按页输出为多个Stream对象。
            /// </summary>
            private IList<Stream> m_streams;
    
            private bool isLandSapces = false;
    
            /// <summary>
            /// 用来提供Stream对象的函数,用于LocalReport对象的Render方法的第三个参数。
            /// </summary>
            /// <param name="name"></param>
            /// <param name="fileNameExtension"></param>
            /// <param name="encoding"></param>
            /// <param name="mimeType"></param>
            /// <param name="willSeek"></param>
            /// <returns></returns>
            private Stream CreateStream(string name, string fileNameExtension, Encoding encoding, string mimeType, bool willSeek)
            {
                //如果需要将报表输出的数据保存为文件,请使用FileStream对象。
                Stream stream = new MemoryStream();
                m_streams.Add(stream);
                return stream;
            }
    
            /// <summary>
            /// 为Report.rdlc创建本地报告加载数据,输出报告到.emf文件,并打印,同时释放资源
            /// </summary>
            /// <param name="rv">参数:ReportViewer.LocalReport</param>
            public void PrintStream(LocalReport rvDoc)
            {
                //获取LocalReport中的报表页面方向
                isLandSapces = rvDoc.GetDefaultPageSettings().IsLandscape;
                Export(rvDoc);
                PrintSetting();
                Dispose();
            }
    
            private void Export(LocalReport report)
            {
                string deviceInfo =
                @"<DeviceInfo>
                     <OutputFormat>EMF</OutputFormat>
                 </DeviceInfo>";
                m_streams = new List<Stream>();
                //将报表的内容按照deviceInfo指定的格式输出到CreateStream函数提供的Stream中。
                report.Render("Image", deviceInfo, CreateStream, out Warning[] warnings);
                foreach (Stream stream in m_streams)
                {
                    stream.Position = 0;
                }
            }
    
            private void PrintSetting()
            {
                if (m_streams == null || m_streams.Count == 0)
                {
                    throw new Exception("错误:没有检测到打印数据流");
                }
                //声明PrintDocument对象用于数据的打印
                PrintDocument printDoc = new PrintDocument();
                //获取配置文件的清单打印机名称
                System.Configuration.AppSettingsReader appSettings = new System.Configuration.AppSettingsReader();
                printDoc.PrinterSettings.PrinterName = appSettings.GetValue("printer", Type.GetType("System.String")).ToString();
                printDoc.PrintController = new StandardPrintController();//指定打印机不显示页码 
                //判断指定的打印机是否可用
                if (!printDoc.PrinterSettings.IsValid)
                {
                    throw new Exception("错误:找不到打印机");
                }
                else
                {
                    //设置打印机方向遵从报表方向
                    printDoc.DefaultPageSettings.Landscape = isLandSapces;
                    //声明PrintDocument对象的PrintPage事件,具体的打印操作需要在这个事件中处理。
                    printDoc.PrintPage += new PrintPageEventHandler(PrintPage);
                    m_currentPageIndex = 0;
                    //设置打印机打印份数
                    printDoc.PrinterSettings.Copies = 1;
                    //执行打印操作,Print方法将触发PrintPage事件。
                    printDoc.Print();
                }
            }
    
            /// <summary>
            /// 处理程序PrintPageEvents
            /// </summary>
            /// <param name="sender"></param>
            /// <param name="ev"></param>
            private void PrintPage(object sender, PrintPageEventArgs ev)
            {
                //Metafile对象用来保存EMF或WMF格式的图形,
                //我们在前面将报表的内容输出为EMF图形格式的数据流。
                Metafile pageImage = new Metafile(m_streams[m_currentPageIndex]);
    
                //调整打印机区域的边距
                System.Drawing.Rectangle adjustedRect = new System.Drawing.Rectangle(
                    ev.PageBounds.Left - (int)ev.PageSettings.HardMarginX,
                    ev.PageBounds.Top - (int)ev.PageSettings.HardMarginY,
                    ev.PageBounds.Width,
                    ev.PageBounds.Height);
    
                //绘制一个白色背景的报告
                //ev.Graphics.FillRectangle(Brushes.White, adjustedRect);
    
                //获取报告内容
                //这里的Graphics对象实际指向了打印机
                ev.Graphics.DrawImage(pageImage, adjustedRect);
                //ev.Graphics.DrawImage(pageImage, ev.PageBounds);
    
                // 准备下一个页,已确定操作尚未结束
                m_currentPageIndex++;
    
                //设置是否需要继续打印
                ev.HasMorePages = (m_currentPageIndex < m_streams.Count);
            }
    
            public void Dispose()
            {
                if (m_streams != null)
                {
                    foreach (Stream stream in m_streams)
                    {
                        stream.Close();
                    }
    
                    m_streams = null;
                }
            }
        }
    

    自定义打印按钮:btnPrint,添加其点击事件

        private void btnPrint_Click(object sender, EventArgs e)
        {
            PrintHelper printHelper = new PrintHelper();
            printHelper.PrintStream(myReportViewer.LocalReport);//myReportViewer是当前的ReportViewk控件名称
        }
    


    5. 参考

  • 相关阅读:
    [原]80386中断表
    [原]elf可执行连接文件格式
    [原]nasm语法
    VLAN基础配置及Access接口
    配置hybird接口
    配置Trunk接口
    [导入]Oracle常用技巧和脚本
    [导入]ORACLE 常用的SQL语法和数据对象
    [导入]Oracle 基本知识
    [导入]Oracle特殊包
  • 原文地址:https://www.cnblogs.com/shanzhiming/p/15798341.html
Copyright © 2020-2023  润新知