備忘錄:基於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. 參考


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM