目錄
志銘-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控制元件名稱
}