C#Winform设计的通用标签设计器
技术看点
- PropertyGrid的使用
- 自定义控件的使用
- 对象序列化成XML
- GDI+Windows驱动打印
前言
是的,一不小心把公司名称透露了。索性帮公司打一下广告。公司(上海易溯信息科技)是中国奶制品行业追溯生产管理方面的龙头。最近也是准备把业务拓展到东南亚地区,筹备走出国门。由于老系统的Windows驱动打印部分出现打印速度不够快,绘图精度不高,标签设计器简陋等问题。于是开始了重构,当然只是参考老程序的实现方式,程序是重新实现的。程序是用很零散的空闲时间写的,效果还需要在项目中实际运用,进行检验。
设计
由于一发现不熟悉的技术点就上网搜索,其实大部分技术难题都是搜索解决的。这里就不申明版权问题了,“如有雷同,纯属意外!”。哈哈
运行时读取模板数据,模板里标签的元素的设计
设计时可视化自定义控件的设计类图
编码实现
1)PropertyGrid的使用
代码都来自网络,主要就是属性名使用中文。使用英文对实施的电器工程师来说不太友好。
public delegate void PropertyChanged(object Value); /// <summary> /// 主要是实现中文化属性显示 /// </summary> public class PropertyBase : ICustomTypeDescriptor { AttributeCollection ICustomTypeDescriptor.GetAttributes() { return TypeDescriptor.GetAttributes(this, true); } string ICustomTypeDescriptor.GetClassName() { return TypeDescriptor.GetClassName(this, true); } string ICustomTypeDescriptor.GetComponentName() { return TypeDescriptor.GetComponentName(this, true); } TypeConverter ICustomTypeDescriptor.GetConverter() { return TypeDescriptor.GetConverter(this, true); } EventDescriptor ICustomTypeDescriptor.GetDefaultEvent() { return TypeDescriptor.GetDefaultEvent(this, true); } PropertyDescriptor ICustomTypeDescriptor.GetDefaultProperty() { return null; } object ICustomTypeDescriptor.GetEditor(Type editorBaseType) { return TypeDescriptor.GetEditor(this, editorBaseType, true); } EventDescriptorCollection ICustomTypeDescriptor.GetEvents() { return TypeDescriptor.GetEvents(this, true); } EventDescriptorCollection ICustomTypeDescriptor.GetEvents(Attribute[] attributes) { return TypeDescriptor.GetEvents(this, attributes, true); } PropertyDescriptorCollection ICustomTypeDescriptor.GetProperties() { return ((ICustomTypeDescriptor)this).GetProperties(new Attribute[0]); } PropertyDescriptorCollection ICustomTypeDescriptor.GetProperties(Attribute[] attributes) { ArrayList props = new ArrayList(); Type thisType = this.GetType(); PropertyInfo[] pis = thisType.GetProperties(); foreach (PropertyInfo p in pis) { if (p.DeclaringType == thisType || p.PropertyType.ToString() == "System.Drawing.Color") { //判断属性是否显示 BrowsableAttribute Browsable = (BrowsableAttribute)Attribute.GetCustomAttribute(p, typeof(BrowsableAttribute)); if (Browsable != null) { if (Browsable.Browsable == true || p.PropertyType.ToString() == "System.Drawing.Color") { PropertyStub psd = new PropertyStub(p, attributes); props.Add(psd); } } else { PropertyStub psd = new PropertyStub(p, attributes); props.Add(psd); } } } PropertyDescriptor[] propArray = (PropertyDescriptor[])props.ToArray(typeof(PropertyDescriptor)); return new PropertyDescriptorCollection(propArray); } object ICustomTypeDescriptor.GetPropertyOwner(PropertyDescriptor pd) { return this; } } /// <summary> /// 自定义属性拦截器 /// </summary> public class PropertyStub : PropertyDescriptor { PropertyInfo info; public PropertyStub(PropertyInfo propertyInfo, Attribute[] attrs) : base(propertyInfo.Name, attrs) { info = propertyInfo; } public override Type ComponentType { get { return info.ReflectedType; } } public override bool IsReadOnly { get { return info.CanWrite == false; } } public override Type PropertyType { get { return info.PropertyType; } } public override bool CanResetValue(object component) { return false; } public override object GetValue(object component) { try { return info.GetValue(component, null); } catch { return null; } } public override void ResetValue(object component) { } public override void SetValue(object component, object value) { info.SetValue(component, value, null); } public override bool ShouldSerializeValue(object component) { return false; } //通过重载下面这个属性,可以将属性在PropertyGrid中的显示设置成中文 public override string DisplayName { get { if (info != null) { ChnPropertyAttribute uicontrolattibute = (ChnPropertyAttribute)Attribute.GetCustomAttribute(info, typeof(ChnPropertyAttribute)); if (uicontrolattibute != null) return uicontrolattibute.PropertyName; else { return info.Name; } } else return ""; } } public override string Description { get { if (info != null) { ChnPropertyAttribute uicontrolattibute = (ChnPropertyAttribute)Attribute.GetCustomAttribute(info, typeof(ChnPropertyAttribute)); if (uicontrolattibute != null) return uicontrolattibute.PropertyDescription; } return string.Empty; } } }
/// <summary> /// 中文方式自定义属性标识 /// </summary> public class ChnPropertyAttribute : Attribute { private string _PropertyName; private string _PropertyDescription; public ChnPropertyAttribute(string Name, string Description) { _PropertyName = Name; _PropertyDescription = Description; } public ChnPropertyAttribute(string Name) { _PropertyName = Name; _PropertyDescription = ""; } public string PropertyName { get { return _PropertyName; } } public string PropertyDescription { get { return _PropertyDescription; } } }
实际使用中文属性
2)自定义控件的使用
/// <summary> /// 标签最顶层容器,标签设计时容器 /// </summary> [Serializable] public partial class RadiusRectangleSharp : Panel { #region 鼠标移动和缩放 const int Band = 5; const int MinWidth = 10; const int MinHeight = 10; private EnumMousePointPosition _mMousePointPosition; private Point _p, _p1; private EnumMousePointPosition MousePointPosition(Size size, System.Windows.Forms.MouseEventArgs e) { if ((e.X >= -1 * Band) | (e.X <= size.Width) | (e.Y >= -1 * Band) | (e.Y <= size.Height)) { if (e.X < Band) { if (e.Y < Band) { return EnumMousePointPosition.MouseSizeTopLeft; } else { if (e.Y > -1 * Band + size.Height) { return EnumMousePointPosition.MouseSizeBottomLeft; } else { return EnumMousePointPosition.MouseSizeLeft; } } } else { if (e.X > -1 * Band + size.Width) { if (e.Y < Band) { return EnumMousePointPosition.MouseSizeTopRight; } else { if (e.Y > -1 * Band + size.Height) { return EnumMousePointPosition.MouseSizeBottomRight; } else { return EnumMousePointPosition.MouseSizeRight; } } } else { if (e.Y < Band) { return EnumMousePointPosition.MouseSizeTop; } else { if (e.Y > -1 * Band + size.Height) { return EnumMousePointPosition.MouseSizeBottom; } else { return EnumMousePointPosition.MouseDrag; } } } } } else { return EnumMousePointPosition.MouseSizeNone; } } #endregion #region Local Variables private Color _borderColor = Color.White; private int _radius = 8; private int _opacity = 68; private Color _dimmedColor = Color.LightGray; protected Rectangle IRect = new Rectangle(); #endregion #region Properties public Color BorderColor { get { return _borderColor; } set { _borderColor = value; Invalidate(); } } public int Opacity { get { return _opacity; } set { _opacity = value; this.Invalidate(); } } public int Radius { get { return _radius; } set { _radius = value; this.Invalidate(); } } /// <summary> /// 当前模板信息 /// </summary> public TemplateItemInfo CurrentTemplateInfo { get { return _currentTempletInfo; } set { _currentTempletInfo = value; } } private TemplateItemInfo _currentTempletInfo = new TemplateItemInfo(); #endregion public RadiusRectangleSharp() { InitializeComponent(); AllowDrop = true; BackColor = Color.White; SetStyle(ControlStyles.AllPaintingInWmPaint | ControlStyles.OptimizedDoubleBuffer | ControlStyles.ResizeRedraw | ControlStyles.SupportsTransparentBackColor | ControlStyles.UserPaint, true); SetStyle(ControlStyles.Opaque, true); Margin = new Padding(1, 1, 1, 1); Padding = new Padding(0, 0, 0, 0); BorderColor = Color.DarkBlue; UpdateStyles(); SendToBack(); } protected override void OnPaint(PaintEventArgs e) { SmoothingMode sm = e.Graphics.SmoothingMode; e.Graphics.SmoothingMode = SmoothingMode.HighQuality; e.Graphics.Clear(Color.White); DrawBorder(e.Graphics); DrawBackground(e.Graphics); e.Graphics.SmoothingMode = sm; } protected void DrawBorder(Graphics g) { Rectangle rect = ClientRectangle; rect.Width--; rect.Height--; using (GraphicsPath bp = GetPath(rect, _radius)) { using (Pen p = new Pen(_borderColor)) { g.DrawPath(p, bp); } } } protected void DrawBackground(Graphics g) { Rectangle rect = ClientRectangle; IRect = rect; rect.X++; rect.Y++; rect.Width -= 2; rect.Height -= 2; using (GraphicsPath bb = GetPath(rect, _radius)) { using (Brush br = new SolidBrush(Color.FromArgb(_opacity, BackColor))) { g.FillPath(br, bb); } } } protected GraphicsPath GetPath(Rectangle rc, int r) { int x = rc.X, y = rc.Y, w = rc.Width, h = rc.Height; r = r << 1; GraphicsPath path = new GraphicsPath(); if (r > 0) { if (r > h) { r = h; }; //Rounded if (r > w) { r = w; }; //Rounded path.AddArc(x, y, r, r, 180, 90); //Upper left corner path.AddArc(x + w - r, y, r, r, 270, 90); //Upper right corner path.AddArc(x + w - r, y + h - r, r, r, 0, 90); //Lower right corner path.AddArc(x, y + h - r, r, r, 90, 90); //Lower left corner path.CloseFigure(); } else { path.AddRectangle(rc); } return path; } protected override void OnMouseDown(MouseEventArgs e) { _p.X = e.X; _p.Y = e.Y; _p1.X = e.X; _p1.Y = e.Y; } protected override void OnMouseUp(MouseEventArgs e) { _mMousePointPosition = EnumMousePointPosition.MouseSizeNone; this.Cursor = Cursors.Arrow; } protected override void OnMouseMove(MouseEventArgs e) { if (e.Button == MouseButtons.Left) { //本控件是顶层容器,不允许位移 switch (_mMousePointPosition) { #region 位置计算 case EnumMousePointPosition.MouseDrag: break; case EnumMousePointPosition.MouseSizeBottom: Height = Height + e.Y - _p1.Y; _p1.X = e.X; _p1.Y = e.Y; //'记录光标拖动的当前点 break; case EnumMousePointPosition.MouseSizeBottomRight: Width = Width + e.X - _p1.X; Height = Height + e.Y - _p1.Y; _p1.X = e.X; _p1.Y = e.Y; //'记录光标拖动的当前点 break; case EnumMousePointPosition.MouseSizeRight: Width = Width + e.X - _p1.X; Height = Height + e.Y - _p1.Y; _p1.X = e.X; _p1.Y = e.Y; //'记录光标拖动的当前点 break; case EnumMousePointPosition.MouseSizeTop: Height = Height - (e.Y - _p.Y); break; case EnumMousePointPosition.MouseSizeLeft: Width = Width - (e.X - _p.X); break; case EnumMousePointPosition.MouseSizeBottomLeft: Width = Width - (e.X - _p.X); Height = Height + e.Y - _p1.Y; _p1.X = e.X; _p1.Y = e.Y; //'记录光标拖动的当前点 break; case EnumMousePointPosition.MouseSizeTopRight: Width = Width + (e.X - _p1.X); Height = Height - (e.Y - _p.Y); _p1.X = e.X; _p1.Y = e.Y; //'记录光标拖动的当前点 break; case EnumMousePointPosition.MouseSizeTopLeft: Width = Width - (e.X - _p.X); Height = Height - (e.Y - _p.Y); break; default: break; #endregion } if (Width < MinWidth) Width = MinWidth; if (Height < MinHeight) Height = MinHeight; } else { _mMousePointPosition = MousePointPosition(Size, e); switch (_mMousePointPosition) { #region 改变光标 case EnumMousePointPosition.MouseSizeNone: this.Cursor = Cursors.Arrow; //'箭头 break; case EnumMousePointPosition.MouseDrag: this.Cursor = Cursors.SizeAll; //'四方向 break; case EnumMousePointPosition.MouseSizeBottom: this.Cursor = Cursors.SizeNS; //'南北 break; case EnumMousePointPosition.MouseSizeTop: this.Cursor = Cursors.SizeNS; //'南北 break; case EnumMousePointPosition.MouseSizeLeft: this.Cursor = Cursors.SizeWE; //'东西 break; case EnumMousePointPosition.MouseSizeRight: this.Cursor = Cursors.SizeWE; //'东西 break; case EnumMousePointPosition.MouseSizeBottomLeft: this.Cursor = Cursors.SizeNESW; //'东北到南西 break; case EnumMousePointPosition.MouseSizeBottomRight: this.Cursor = Cursors.SizeNWSE; //'东南到西北 break; case EnumMousePointPosition.MouseSizeTopLeft: this.Cursor = Cursors.SizeNWSE; //'东南到西北 break; case EnumMousePointPosition.MouseSizeTopRight: this.Cursor = Cursors.SizeNESW; //'东北到南西 break; default: break; #endregion } } } protected override void OnResize(EventArgs eventargs) { if (CurrentTemplateInfo != null) { CurrentTemplateInfo.Width = Size.Width; CurrentTemplateInfo.Height = Size.Height; Invalidate(); } } protected override void OnDragEnter(DragEventArgs drgevent) { drgevent.Effect = DragDropEffects.Copy; base.OnDragEnter(drgevent); } protected override void OnDragDrop(DragEventArgs drgevent) { try { string[] strs = (string[])drgevent.Data.GetData(typeof(string[])); //获取拖拽数据 PictureBox ctrl = null; #region 实例化元素控件 switch (strs.FirstOrDefault()) { case "Barcode": ctrl = new BarcodePictureBox(); break; case "Image": ctrl = new ImagePictureBox(); break; case "Text": ctrl = new StaticTextBox(); break; default: break; } #endregion ctrl.Location = PointToClient(new Point(drgevent.X, drgevent.Y)); //屏幕坐标转换成控件容器坐标 ctrl.BringToFront(); Controls.Add(ctrl); } catch (Exception ex) { string msg = "初始化控件出错!错误码:" + ex.Message + Environment.NewLine + ex.StackTrace; MessageTip.ShowError(this, msg, 3000); } base.OnDragDrop(drgevent); } }
整体上来说就是GDI+的使用,其中用了Base64编码来序列化图片。
3)对象序列化成XML
使用的是标准的方式:
/// <summary> /// 把对象序列化成xml文件 /// </summary> /// <typeparam name="T">对象的类</typeparam> /// <param name="outFile">输出的文件和路径</param> /// <param name="t">对象的实例</param> public static void SerializerToXML<T>(string outFile, T t) where T : class { using (System.IO.FileStream fs = new System.IO.FileStream(outFile, System.IO.FileMode.Create)) { XmlSerializerNamespaces ns = new XmlSerializerNamespaces(); ns.Add("", ""); XmlSerializer xs = new XmlSerializer(typeof(T)); xs.Serialize(fs, t, ns); fs.Flush(); } } /// <summary> /// 从XML文件反序列化成集合对象 /// </summary> /// <typeparam name="T">对象</typeparam> /// <param name="inXMLFile">xml的文件,全路径</param> /// <returns>对象集合</returns> public static T LoadFromXML<T>(string inXMLFile) where T : class { var t = default(T); using (System.IO.FileStream fs = new System.IO.FileStream(inXMLFile, System.IO.FileMode.Open)) { XmlSerializer xs = new XmlSerializer(typeof(T)); t = (T)xs.Deserialize(fs); fs.Close(); } return t; }
/// <summary> /// 图形元素 /// </summary> [Serializable] public class ImageElementNode : PropertyBase, IElementNodeData, INotifyPropertyChanged { protected PictureBoxSizeMode _PictureBoxSizeMode = PictureBoxSizeMode.StretchImage; [ChnProperty("缩放模式", "图片原始尺寸和元素大小不一致时需要对原始图片进行缩放,设置缩放模式。")] [Category("通用属性")] public PictureBoxSizeMode ImageBoxSizeMode { get { return _PictureBoxSizeMode; } set { _PictureBoxSizeMode = value; } } private Point _location; [ChnProperty("位置", "节点元素的在模板里的位置的坐标,鼠标选中节点即可以移动位置。")] [Category("通用属性")] public Point Location { get { return _location; } set { _location = value; } } private string _name; [ChnProperty("元素名称", "一般自动生成,不需要维护。")] [Category("通用属性")] [XmlAttribute("Name")] public string Name { get { return _name; } set { _name = value; NotifyPropertyChanged("Name"); } } [ChnProperty("元素节点类型", "模板元素节点类型,元素产生时根据添加时自动确定,设计时不要修改类型。"), Category("通用属性")] [XmlAttribute("NodeCategory")] public ElementNodeCategory NodeCategory { get { return ElementNodeCategory.静态文本; } } private ImageRoteType _roteDescription = ImageRoteType.正常; [ChnProperty("旋转角度", "变形的形态描述,比如顺时针旋转90度。"), Category("通用属性")] public ImageRoteType RoteDescription { get { return _roteDescription; } set { _roteDescription = value; NotifyPropertyChanged("RoteDescription"); } } private Size _size; [ChnProperty("元素大小", "包括高度和宽度,单位是像素。使用鼠标可调节大小,滚轮进行缩放。"), Category("通用属性")] public Size Size { get { return _size; } set { _size = value; } } [ChnProperty("静态表达式", @"可以从‘ProductName,ProductCode,ProductSpec, CorpCode,BatchCode,LineCode,DepartCode,TeamCode,WorkerCode, PackDate,ValidateDate,ProductFullName,CustomerCode,CustomerName’字段中获取值。"), Category("表达式")] public StaticMapProperty StaticMapProperty { get; set; } [ChnProperty("动态表达式", @"可以从‘Code,EncryptCode,ParentCode,CipherFieldCode,Seqence, VersionCode,VersionName,BatchCode,CorpCode,LineCode,PackDate,ProductCode,WorkPointInt, WorkPointAZ,Dynamic,’字段中获取值。"), Category("表达式")] public DynamicMapProperty DynamicMapProperty { get; set; } private string text; [ChnProperty("固定字符", "在不写任何表达式的情况下设置的固定字符"), Category("通用属性")] [XmlAttribute("Text")] public string Text { get { return text; } set { text = value; NotifyPropertyChanged("Text"); } } private string _动态内容; [ChnProperty("动态内容", @"格式:{PropertyName[(Start[,Length])]}[&Blank[(Length)]]&{PropertyName[(Start[,Length])]} PropertyName:动态属性里的选项 Start:动态属性对应内容的开始位置 Length:截取内容的长度 Blank:空格 Length:空格的个数 &:为分隔符 设置此内容的时候,请务必小心,设置时系统不检测其值的合法性,在执行的时候可能会报错"), Category("表达式")] public string 动态内容 { get { return _动态内容; } set { _动态内容 = value; NotifyPropertyChanged("动态内容"); } } [ChnProperty("图形数据", "Base64编码的图形数据,缩放后序列化的字节码。双击图像元素进行选择和预览。"), Category("图形属性")] private string _imageData; [Browsable(false)] public string BinaryData { get { return _imageData; } set { _imageData = value; NotifyPropertyChanged("BinaryData"); } } private string _formatString = string.Empty; [ChnProperty("格式掩码", "如yyyyMMdd,HH:MM:SS。"), Category("表达式")] public string FormatString { get { return _formatString; } set { _formatString = value; NotifyPropertyChanged("FormatString"); } } public event PropertyChangedEventHandler PropertyChanged; public Image GetElmentNodeImage(PrintData data) { if (string.IsNullOrEmpty(BinaryData)) throw new Exception("图片元素没有绑定图片资源!"); Bitmap bmp = new Bitmap(Size.Width-3, Size.Height-3); Graphics g = Graphics.FromImage(bmp); g.Clear(Color.White); g.SmoothingMode = SmoothingMode.HighQuality; byte[] arr = Convert.FromBase64String(BinaryData); using (MemoryStream ms = new MemoryStream(arr)) { Bitmap tempBmp = new Bitmap(ms); using (PictureBox pb = new PictureBox()) { pb.Size = Size; pb.SizeMode = ImageBoxSizeMode; pb.Image = tempBmp; return ImageRote.RoteImage(RoteDescription, pb.Image); } } } private void NotifyPropertyChanged(string property) { PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(property)); } public ImageElementNode() { StaticMapProperty = StaticMapProperty.None; DynamicMapProperty = DynamicMapProperty.None; 动态内容 = ""; } }
4)GDI+Windows驱动打印
/// <summary> /// GDI Printer驱动类 /// </summary> public class CoolGDIPrinter : WindowsVirtualPrinter { protected PrintDocument printDocument; protected Dictionary<string, TemplateItemInfo> dict = new Dictionary<string, TemplateItemInfo>(); private object printSync = new object(); private string templateFile = AppDomain.CurrentDomain.BaseDirectory + "Config\\GDITemplate.Config"; private string current_data = string.Empty; private PrintData pd = null; private string current_Template = string.Empty; public CoolGDIPrinter(HardwarePort port, ILog log) : base(port, log) { if (!System.IO.File.Exists(templateFile)) { throw new Exception(string.Format("模板文件{0}不存在!", templateFile)); } DataList<TemplateItemInfo> list = WinFormHelper.LoadFromXML<DataList<TemplateItemInfo>>(templateFile); if (list != null && list.Count > 0) { foreach (TemplateItemInfo template in list) { dict.Add(template.Code, template); } } if (string.IsNullOrEmpty(port.PortName)) { throw new Exception("打印机名称不能为空!"); } var findPrinter = GetAllLocalPrinters().Find(obj => obj.Contains(port.PortName)); if (string.IsNullOrEmpty(findPrinter)) { throw new Exception(string.Format("本地没有打印机{0},请检查打印机驱动是否正确安装。", port.PortName)); } if (dict.Count == 0) { throw new Exception(string.Format("模板文件{0}内容为空,或者格式不符!", templateFile)); } #region PrintDocument printDocument = new PrintDocument { PrinterSettings = { PrinterName = PortName, } }; printDocument.PrintController = new StandardPrintController(); printDocument.BeginPrint += PrintDocument_BeginPrint; printDocument.PrintPage += PrintDocument_PrintPage; printDocument.EndPrint += PrintDocument_EndPrint; #endregion var isvali = printDocument.PrinterSettings.IsValid; if (!isvali) { throw new Exception(string.Format("没有指定有效的打印机,{0}不可用!", PortName)); } } /// <summary> /// 获取本机所有打印机列表 /// </summary> /// <returns>本机所有打印机列表</returns> private List<string> GetAllLocalPrinters() { var fPrinters = new List<string>(); foreach (string fPrinterName in PrinterSettings.InstalledPrinters) { if (!fPrinters.Contains(fPrinterName)) { fPrinters.Add(fPrinterName); } } return fPrinters; } /// <summary> /// 获取打印机的当前状态 /// </summary> /// <param name="PrinterDevice">打印机设备名称</param> /// <returns>打印机状态</returns> private PrinterStatus GetPrinterStat(string PrinterDevice) { PrinterStatus ret = 0; string path = @"win32_printer.DeviceId='" + PrinterDevice + "'"; ManagementObject printerMgrObj = new ManagementObject(path); printerMgrObj.Get(); ret = (PrinterStatus)Convert.ToInt32(printerMgrObj.Properties["PrinterStatus"].Value); return ret; } /// <summary> /// WMI检测指定的打印机是否可用 /// </summary> /// <param name="printerNameIn">指定的打印机名称</param> /// <returns></returns> protected bool CheckPrinter(string printerNameIn) { var scope = new ManagementScope(@"\root\cimv2"); scope.Connect(); ManagementObjectSearcher searcher = new ManagementObjectSearcher("SELECT * FROM Win32_Printer"); string printerName = ""; foreach (ManagementObject printer in searcher.Get()) { printerName = printer["Name"].ToString().ToLower(); if (printerName.IndexOf(printerNameIn.ToLower()) > -1) { if (printer["WorkOffline"].ToString().ToLower().Equals("true")) { return false; } else { return true; } } } return false; } private object printlock = new object(); /// <summary> /// 内部执行打印 /// </summary> /// <param name="data"></param> /// <param name="templateKey"></param> protected override void WritePrinter(PrintData data, string templateKey) { var template = dict[templateKey]; if (template == null) { throw new Exception(string.Format("不存在打印机模板[{0}]的配置文件", templateKey)); } lock (printlock) { try { printDocument.PrinterSettings.Copies = (short)template.Quantity; printDocument.PrinterSettings.DefaultPageSettings.PaperSize = new PaperSize("GDI_LableSize", template.Width + 2, template.Height + 2); printDocument.OriginAtMargins = true; printDocument.DefaultPageSettings.Margins = new Margins(2, 2, 2, 2); var printerIsonline = CheckPrinter(printDocument.PrinterSettings.PrinterName); if (!printerIsonline) { RaiseException(new Exception(string.Format("打印机状态{0},请稍候进行打印!", "离线,请检查打印机是否正常开启"))); return; } current_data = data.Code; pd = data; current_Template = templateKey; PostData(data); printDocument.Print(); } catch (Exception ex) { RaiseReceived(ex, null); RaiseException(new Exception(current_data + "打印失败,原因:" + ex.Message)); } } } /// <summary> /// 外部调用驱动程序,发送打印数据 /// </summary> /// <param name="data">打印数据</param> /// <param name="templateKey">模板键</param> public override void WriteData(PrintData data, string templateKey) { base.WriteData(data, templateKey); IsPrinting = true; WritePrinter(data, templateKey); IsPrinting = false; } /// <summary> /// 启动生成标签图形 /// </summary> /// <param name="sender"></param> /// <param name="e"></param> private void PrintDocument_BeginPrint(object sender, PrintEventArgs e) { Log.Info(string.Format("开始打印数据:[{0}],[{1}]", PortName, current_data)); } /// <summary> /// 执行标签图形生成产生画布 /// </summary> /// <param name="sender"></param> /// <param name="e"></param> private void PrintDocument_PrintPage(object sender, PrintPageEventArgs e) { lock (printSync) { try { var imgData = BuildTemplateImage(pd, current_Template); if (imgData != null) { e.Graphics.SmoothingMode = SmoothingMode.HighQuality; e.Graphics.DrawImage(imgData, 0, 0, imgData.Width, imgData.Height); if (IsDebug) { string debugPath = AppDomain.CurrentDomain.BaseDirectory + "output"; if (!System.IO.Directory.Exists(debugPath)) { System.IO.Directory.CreateDirectory(debugPath); } string bmpFile = System.IO.Path.Combine(debugPath, current_data + DateTime.Now.Ticks + ".PNG"); imgData.Save(bmpFile, ImageFormat.Png); } } } catch (Exception ex) { Log.Error(ex); throw new Exception("构建标签图形失败,错误描述:" + ex.Message + Environment.NewLine + ex.StackTrace); } } } /// <summary> /// 结束打印绘图,发送给打印机打印数据 /// </summary> /// <param name="sender"></param> /// <param name="e"></param> private void PrintDocument_EndPrint(object sender, PrintEventArgs e) { Log.Info(string.Format("[{0}],[{1}],发送打印数据完成", PortName, current_data)); } /// <summary> /// 内部通过模板生成图形 /// </summary> /// <param name="data"></param> /// <param name="templateKey"></param> /// <returns></returns> protected override Bitmap BuildTemplateImage(PrintData data, string templateKey) { var template = dict[templateKey]; Bitmap bmp = new Bitmap(template.Width, template.Height); Graphics graphics = Graphics.FromImage(bmp); graphics.SmoothingMode = SmoothingMode.HighQuality; graphics.Clear(Color.White); //1、画图片元素节点 foreach (var bmpElement in template.ImageElementList) { Rectangle rect = new Rectangle(bmpElement.Location.X, bmpElement.Location.Y, bmpElement.Size.Width, bmpElement.Size.Height); graphics.DrawImage(bmpElement.GetElmentNodeImage(data), rect); } //2、画条码元素节点 foreach (var barcodeElement in template.BarcodeElementList) { Rectangle rect = new Rectangle(barcodeElement.Location.X, barcodeElement.Location.Y, barcodeElement.Size.Width, barcodeElement.Size.Height); graphics.DrawImage(barcodeElement.GetElmentNodeImage(data), rect); } //3、画静态文本元素节点 foreach (var txtElement in template.TextBoxList) { Rectangle rect = new Rectangle(txtElement.Location.X, txtElement.Location.Y, txtElement.Size.Width, txtElement.Size.Height); graphics.DrawImage(txtElement.GetElmentNodeImage(data), rect); } return bmp; } }
PrintDocument对象就是对打印机画布进行的封装,主要就是GDI+的操作了。
成果物
+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
"作者:" 数据酷软件工作室
"出处:" http://datacool.cnblogs.com
"专注于CMS(综合赋码系统),MES,WCS(智能仓储设备控制系统),WMS,商超,桑拿、餐饮、客房、足浴等行业收银系统的开发,15年+从业经验。因为专业,所以出色。"
+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
"作者:" 数据酷软件工作室
"出处:" http://datacool.cnblogs.com
"专注于CMS(综合赋码系统),MES,WCS(智能仓储设备控制系统),WMS,商超,桑拿、餐饮、客房、足浴等行业收银系统的开发,15年+从业经验。因为专业,所以出色。"
+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++