一起谈.NET技术,以 .NET 创建 Code 39 条码图片 供水晶报表打印

简介: 教导如何用 C# 创建 Code 39 编码的「条码 (barcode)」图片,以供 ASP.NET + Crystal Reports 水晶报表呈现和打印此条码。本帖提供 ASP.NET 3.5 示例下载。

教导如何用 C# 创建 Code 39 编码的「条码 (barcode)」图片,以供 ASP.NET + Crystal Reports 水晶报表呈现和打印此条码。本帖提供 ASP.NET 3.5 示例下载。

本帖的示例下载点:http://files.cnblogs.com/WizardWu/100914.zip

执行本示例,需要 SQL Server 的 Northwind 数据库,以及 VS 2008 或 IIS,另还需要 Crystal Reports 2008 标准版 (SAP 公司的网站可下载完整的安装程序,无使用限制,但安装前需要输入安装序号)。
若是 VS 2005/2008 内置的免费简易版 Crystal Reports,由于不具备「动态截取网络图片」的功能、无法抓取既有的条码图片,因此不适用本帖的教学。
  日前做 ASP.NET 的项目用到 Crystal Reports 水晶报表,必须要能在浏览器中的报表显示和打印条码。原本我采用「字体 (font)」的方式产生条码 (水晶报表内置将某个数据库字段,直接转成条形码的功能),但后来发现这种做法,布署时必须在每一台客户端的 Windows 上安装特定的条码字体,如:free3of9 (可免费下载),才能在客户端浏览器正确显示和打印条码。因此后来弃用这种做法,改用「图片」的方式产生条码。
      做法是先用 C# 和 .NET 的绘图 API,搭配一维条码里最普遍的 Code 39 编码其规则,写一个可创建条码图片的 .ashx (HttpHandler) 或 .aspx,(这个文件放在报表的同一个 ASP.NET 项目里即可,不必发布成 service)。接着在 Crystal Reports 文件里,随便插入一张图片,透过水晶报表标准版才有的「动态截取网络图片」功能 (Visual Studio 内置的免费版水晶报表无此功能),去抓取这张已创建的条码图片,并要能动态传入参数,以让报表在换页时,条码可跟着变动内容。
      首先用 C# 和 .NET 的绘图 API,搭配一维条码里最普遍的 Code 39 编码其规则,写一个可创建条码图片的组件。请参考本帖的下载示例,直接执行 Code39Handler.ashx,并透过浏览器的 URL 地址栏,手动输入条码的参数作测试。执行结果和源代码 (这种组件通常是要钱的) 如下:

 

图 1 用 C# 和 .NET 的绘图 API,搭配 Code 39 编码规则产生的条码图片
     

以下代码,是用 C# 和 .NET 的绘图 API,搭配 Code 39 编码规则产生条码图片。

Code39Handler
<% @ WebHandler Language = " C# "  Class = " Code39Handler "   %>

using  System;
using  System.Web;
using  System.Drawing;
using  System.Drawing.Imaging;
using  System.Drawing.Text;

///   <summary>
///  用 .NET 繪圖 API,搭配條碼最普遍的 Code 39 編碼規則 (一般超商的讀條碼機都可讀),產生條碼圖檔
///   </summary>
public   class  Code39Handler : IHttpHandler {
    
    
public   void  ProcessRequest (HttpContext context) {
        
// context.Response.ContentType = "text/plain";
        
// context.Response.Write("Hello World");

        
// Logic to retrieve the image file
        
// context.Response.ContentType = "image/jpeg";
        
// context.Response.WriteFile("MyImage01.jpg");

        
string  mycode  =  context.Request[ " code " ];

        
string  字串;
        
string  字元;
        
// 字串 = "*-%$*"
        字串  =   " * "   +  mycode  +   " * " ;   // Code 39 的特性是前、後置碼會標識「星號(*)」,表示開始和結束

        
int  畫布高  =   35 ;
        
int  畫布寬  =   0 ;
        
int  筆x  =   0 ;
        
int  筆y  =   20 ;
        
// int 筆寬 = 0;
        
        
if  ( ! string .IsNullOrEmpty(mycode))
        {
            畫布寬 
=  字串.Length  *   13 ;

            Bitmap BMP 
=   new  Bitmap(畫布寬, 畫布高, System.Drawing.Imaging.PixelFormat.Format32bppPArgb);
            Graphics G 
=  Graphics.FromImage(BMP);
            G.TextRenderingHint 
=  TextRenderingHint.AntiAlias;
            G.Clear(Color.White);

            Brush 筆刷1 
=   new  SolidBrush(Color.White);
            G.SmoothingMode 
=  System.Drawing.Drawing2D.SmoothingMode.HighQuality;
            G.FillRectangle(筆刷1, 
0 0 , 畫布寬, 畫布高);

            
for  ( int  i  =   0 ; i  <  字串.Length; i ++ )
            {
                
// 取得 Code 39 碼的規則
                字元  =   this .genBarcode(字串.Substring(i,  1 ).ToUpper());

                
for  ( int  j  =   0 ; j  <   4 ; j ++ )
                {
                    
if  (字元.Substring(j,  1 ).Equals( " 0 " ))
                    {
                        G.DrawLine(Pens.Black, 筆x, 
0 , 筆x, 筆y);
                    }
                    
else
                    {
                        G.DrawLine(Pens.Black, 筆x, 
0 , 筆x, 筆y);
                        G.DrawLine(Pens.Black, 筆x 
+   1 0 , 筆x  +   1 , 筆y);
                        筆x 
+=   1 ;
                    }

                    筆x 
+=   1 ;

                    
if  (字元.Substring(j  +   5 1 ).Equals( " 0 " ))
                    {
                        G.DrawLine(Pens.White, 筆x, 
0 , 筆x, 筆y);
                    }
                    
else
                    {
                        G.DrawLine(Pens.White, 筆x, 
0 , 筆x, 筆y);
                        G.DrawLine(Pens.White, 筆x 
+   1 0 , 筆x  +   1 , 筆y);
                        筆x 
+=   1 ;
                    }

                    筆x 
+=   1 ;
                } 
// end of loop

                
if  (字元.Substring( 4 1 ).Equals( " 0 " ))
                {
                    G.DrawLine(Pens.Black, 筆x, 
0 , 筆x, 筆y);
                }
                
else
                {
                    G.DrawLine(Pens.Black, 筆x, 
0 , 筆x, 筆y);
                    G.DrawLine(Pens.Black, 筆x 
+   1 0 , 筆x  +   1 , 筆y);
                    筆x 
+=   1 ;
                }

                筆x 
+=   2 ;
            } 
// end of loop

            
int  x  =   0 ;
            
int  addx  =   13 ;

            G.DrawString(
" - " new  Font( " Arial " 10 , FontStyle.Italic), SystemBrushes.WindowText,  new  PointF(x,  20 ));
            x 
+=  addx;

            
for  ( int  k  =   0 ; k  <  mycode.Length; k ++ )
            {
                G.DrawString(mycode.Substring(k, 
1 ),  new  Font( " Arial " 10 , FontStyle.Italic), SystemBrushes.WindowText,  new  PointF(x,  20 ));
                x 
=  x  +  addx;
            }

            G.DrawString(
" - " new  Font( " Arial " 10 , FontStyle.Italic), SystemBrushes.WindowText,  new  PointF(x,  20 ));
            BMP.Save(context.Response.OutputStream, ImageFormat.Jpeg);
            G.Dispose();
            BMP.Dispose();

        }
        
else
        {
            畫布寬 
=   100 ;

            Bitmap BMP 
=   new  Bitmap(畫布寬, 畫布高, System.Drawing.Imaging.PixelFormat.Format32bppPArgb);
            Graphics G 
=  Graphics.FromImage(BMP);
            G.TextRenderingHint 
=  TextRenderingHint.AntiAlias;
            G.Clear(Color.White);
            
            
// 未給參數時顯示的提示內容
            G.DrawString( " 無條碼產生 " new  Font( " 宋体 " 12 , FontStyle.Regular), SystemBrushes.WindowText,  new  PointF( 0 20 ));

            BMP.Save(context.Response.OutputStream, ImageFormat.Jpeg);
            G.Dispose();
            BMP.Dispose();
        }
    }


    
//  規則可參考網址 1: http://blog.csdn.net/xuzhongxuan/archive/2008/05/28/2489358.aspx
    
//  規則可參考網址 2: http://blog.163.com/zryou/blog/static/6903184200971704226450/
     ///   <summary>
    
///  Code 39 碼的規則。
    
///  Code 39 碼可使用的字元如下:0~9、A~Z、+、-、*、/、%、$、. 及空白字元。    
    
///   </summary>
    
///   <param name="code"></param>
    
///   <returns></returns>
     public   string  genBarcode( string  code)
    {
        
switch  (code)
        {
            
case   " 0 " :
                code 
=   " 001100100 " ;
                
break ;
            
case   " 1 " :
                code 
=   " 100010100 " ;
                
break ;
            
case   " 2 " :
                code 
=   " 010010100 " ;
                
break ;
            
case   " 3 " :
                code 
=   " 110000100 " ;
                
break ;
            
case   " 4 " :
                code 
=   " 001010100 " ;
                
break ;
            
case   " 5 " :
                code 
=   " 101000100 " ;
                
break ;
            
case   " 6 " :
                code 
=   " 011000100 " ;
                
break ;
            
case   " 7 " :
                code 
=   " 000110100 " ;
                
break ;
            
case   " 8 " :
                code 
=   " 100100100 " ;
                
break ;
            
case   " 9 " :
                code 
=   " 010100100 " ;
                
break ;
            
case   " A " :
                code 
=   " 100010010 " ;
                
break ;
            
case   " B " :
                code 
=   " 010010010 " ;
                
break ;
            
case   " C " :
                code 
=   " 110000010 " ;
                
break ;
            
case   " D " :
                code 
=   " 001010010 " ;
                
break ;
            
case   " E " :
                code 
=   " 101000010 " ;
                
break ;
            
case   " F " :
                code 
=   " 011000010 " ;
                
break ;
            
case   " G " :
                code 
=   " 000110010 " ;
                
break ;
            
case   " H " :
                code 
=   " 100100010 " ;
                
break ;
            
case   " I " :
                code 
=   " 010100010 " ;
                
break ;
            
case   " J " :
                code 
=   " 001100010 " ;
                
break ;
            
case   " K " :
                code 
=   " 100010001 " ;
                
break ;
            
case   " L " :
                code 
=   " 010010001 " ;
                
break ;
            
case   " M " :
                code 
=   " 110000001 " ;
                
break ;
            
case   " N " :
                code 
=   " 001010001 " ;
                
break ;
            
case   " O " :
                code 
=   " 101000001 " ;
                
break ;
            
case   " P " :
                code 
=   " 011000001 " ;
                
break ;
            
case   " Q " :
                code 
=   " 000110001 " ;
                
break ;
            
case   " R " :
                code 
=   " 100100001 " ;
                
break ;
            
case   " S " :
                code 
=   " 010100001 " ;
                
break ;
            
case   " T " :
                code 
=   " 001100001 " ;
                
break ;
            
case   " U " :
                code 
=   " 100011000 " ;
                
break ;
            
case   " V " :
                code 
=   " 010011000 " ;
                
break ;
            
case   " W " :
                code 
=   " 110001000 " ;
                
break ;
            
case   " X " :
                code 
=   " 001011000 " ;
                
break ;
            
case   " Y " :
                code 
=   " 101001000 " ;
                
break ;
            
case   " Z " :
                code 
=   " 011001000 " ;
                
break ;
            
case   " * " :
                code 
=   " 001101000 " ;
                
break ;
            
case   " - " :
                code 
=   " 000111000 " // 好像辨識不出來
                 break ;
            
case   " % " :
                code 
=   " 100101000 " // 好像辨識不出來
                 break ;
            
case   " $ " :
                code 
=   " 010101000 " // 好像辨識不出來
                 break ;
            
default :
                code 
=   " 010101000 " // 都不是就印 $
                 break ;
        }
        
        
return  code;
    }
    
 
    
public   bool  IsReusable {
        
get  {
            
return   false ;
        }
    }

}

 

执行 Default.aspx 里的水晶报表,结果如下图 2。条码是图片,不是字型,不必担心客户端的浏览器或打印机无法辨识某种条码字体。另下图 2 里,Employees 表的 EmployeeID 字段是 int 类型,在水晶报表默认会被当作 Number 类型,而自动显示小数点及后两位数字。本文后续会提到解决方式。

 



图 2 报表换页时,会传入不同的参数内容到我们写的条码组件里,因此条码内容也会跟着变动

 


      若您想测试本帖示例,可去 SAP 公司的官方网站,下载标准版的 Crystal Reports 2008 软件 (下载页面标识的 SP 版或 V1 版,表示已内置安装主程序 + 修补程序,并非只有修补程序)。该软件和 Oracle 数据库的策略一样,提供网络下载完整的安装主程序、无使用时间限制或功能限制,但安装 Crystal Reports 前需要输入安装序号 (怎么找序号本文不再赘述)。安装过程如下图 3,加选「数据访问」的 ADO.NET 功能,以配合本帖示例的 ASP.NET 报表做法,透过网站 App_Code 文件夹里,事先定义好要访问的数据库内容的 .xsd (DataSet) 文件,作为设计 Crystal Reports 报表时的数据来源。

 



图 3 Crystal Reports 2008 安装过程,加选「数据访问」的 ADO.NET 选项

 


      如下图 4、图 5,在新建的水晶报表文件里,随便插入一个图片,在上面单击右键选择「设置图形格式」,再选择 Crystal Reports 标准版才有的「图形位置」功能。

 



图 4 在水晶报表里先随便插入一张图片



图 5 Crystal Reports 标准版才有的「图形位置」功能,VS 2005/2008 内置的版本无此功能

 


      如下图 6,在水晶报表的「公式编辑器」里,输入以下内容和参数。此处动态传入的参数,会传入上图 1 里,我们事先用 C# 写好的条码生成组件。此例中,参数内容是 Employees 表的 EmployeeID 字段。若您报表里的条码,无法正确透过浏览器呈现,多半是这里的地址、端口号或内容打错,或此创建条码的服务未正确启用,此时浏览器只会显示原始插入报表的图片,而非条码。

 



图 6 公式编辑器里的语法,较类似 VB 或 VB.NET

 


      另在上图 2 里,我们提到数据库里的 EmployeeID 字段,类型是 int,在水晶报表里会被当作 Number 类型,而在条码里自动加上小数点及后两位数字。解决方式如下图 7,用 水晶报表自带的 CStr 函数,将该字段转型成字符串即可。

 



图 7 若报表里的条码,无法正确透过浏览器呈现,多半是这里的地址或内容打错,或这里未改成您 VS 2008 内置 Web server 或 IIS 执行时的正确地址、端口号

 


      附带一提,在布署 ASP.NET 水晶报表至 IIS 时,必须将 IIS 默认目录:
            C:\Inetpub\wwwroot\
底下的 aspnet_client 文件夹和里面的文件 (图 8),一并拷贝至我们的 ASP.NET 网站底下 (图 9),这样透过 IIS 执行的水晶报表,才能正确显示报表 Toolbar 里的 icon,并正确展示相关的功能。

 


图 8 此文件夹在安装完 Crystal Reports 主程序后,内容会自动增加



图 9 ASP.NET 网站布署至 IIS 时,必须一并将 aspnet_client 文件夹拷贝至网站的根目录

 

 

附带一提,水晶报表的打印方式分两种,一是透过报表 Toolbar 自带的打印按钮 (参考上图 2),二是自己撰码调用 ReportDocument 类的 PrintToPrinter 方法。

      第一种打印方式,才能突破浏览器的安全限制,自动下载 ActiveX 程序以呈现打印预览的窗体,并能自动抓取到客户端的打印机名称。这种做法适合跨互联网打印的用户。
      第二种打印方式,虽然客制能力较强,但只能抓取服务器端的打印机名称,因此报表只能在服务器端打印。这种做法只适合同一个 LAN 或 Intranet 共用打印机的用户。 

目录
相关文章
|
5天前
|
人工智能 开发框架 量子技术
【专栏】.NET 技术:驱动创新的力量
【4月更文挑战第29天】.NET技术,作为微软的开发框架,以其跨平台、开源和语言多样性驱动软件创新。它在云计算、AI/ML、混合现实等领域发挥关键作用,通过Azure、ML.NET等工具促进新兴技术发展。未来,.NET将涉足量子计算、微服务和无服务器计算,持续拓宽软件开发边界,成为创新的重要推动力。掌握.NET技术,对于开发者而言,意味着握有开启创新的钥匙。
|
5天前
|
开发框架 .NET C#
【专栏】理解.NET 技术,提升开发水平
【4月更文挑战第29天】本文介绍了.NET技术的核心概念和应用,包括其跨平台能力、性能优化、现代编程语言支持及Web开发等特性。文章强调了深入学习.NET技术、关注社区动态、实践经验及学习现代编程理念对提升开发水平的重要性。通过这些,开发者能更好地利用.NET构建高效、可维护的多平台应用。
|
5天前
|
机器学习/深度学习 vr&ar 开发者
【专栏】.NET 技术:引领开发新方向
【4月更文挑战第29天】本文探讨了.NET技术如何引领软件开发新方向,主要体现在三方面:1) 作为跨平台开发的先锋,.NET Core支持多操作系统和移动设备,借助.NET MAUI创建统一UI,适应物联网需求;2) 提升性能和开发者生产力,采用先进技术和优化策略,同时更新C#语言特性,提高代码效率和可维护性;3) 支持现代化应用架构,包括微服务、容器化,集成Kubernetes和ASP.NET Core,保障安全性。此外,.NET还不断探索AI、ML和AR/VR技术,为软件开发带来更多创新可能。
|
5天前
|
开发框架 Cloud Native 开发者
【专栏】剖析.NET 技术的核心竞争力
【4月更文挑战第29天】本文探讨了.NET框架在软件开发中的核心竞争力:1) .NET Core实现跨平台与云原生技术的融合,支持多操作系统和容器化;2) 提升性能和开发者生产力,采用JIT、AOT优化,提供C#新特性和Roslyn编译器平台;3) 支持现代化应用架构,包括微服务和容器化,内置安全机制;4) 丰富的生态系统和社区支持,拥有庞大的开发者社区和微软的持续投入。这些优势使.NET在竞争激烈的市场中保持领先地位。
|
5天前
|
开发框架 .NET 开发者
【专栏】领略.NET 技术的创新力量
【4月更文挑战第29天】.NET技术自ASP.NET起历经创新,现以.NET Core为核心,展现跨平台能力,提升性能与生产力,支持现代化应用架构。.NET Core使开发者能用同一代码库在不同操作系统上构建应用,扩展至移动和物联网领域。性能提升,C#新特性简化编程,Roslyn编译器优化代码。拥抱微服务、容器化,内置安全机制,支持OAuth等标准。未来.NET 6将引入更快性能、Hot Reload等功能,预示着.NET将持续引领软件开发潮流,为开发者创造更多机会。
|
5天前
|
物联网 vr&ar 开发者
【专栏】.NET 技术:为开发注入活力
【4月更文挑战第29天】本文探讨了.NET技术的创新,主要体现在三个方面:1) .NET Core实现跨平台开发革命,支持多种操作系统和硬件,如.NET MAUI用于多平台UI;2) 性能提升与生产力飞跃,C#新特性简化编程,JIT和AOT优化提升性能,Roslyn提供代码分析工具;3) 引领现代化应用架构,支持微服务、容器化,内置安全机制。未来,.NET 7将带来更多新特性和前沿技术整合,如量子计算、AI,持续推动软件开发创新。开发者掌握.NET技术将赢得竞争优势。
|
5天前
|
人工智能 前端开发 Cloud Native
【专栏】洞察.NET 技术的开发趋势
【4月更文挑战第29天】本文探讨了.NET技术的三大发展趋势:1) 跨平台与云原生技术融合,通过.NET Core支持轻量级、高性能应用,适应云计算和微服务;2) 人工智能与机器学习的集成,如ML.NET框架,使开发者能用C#构建AI模型;3) 引入现代化前端开发技术,如Blazor,实现前后端一致性。随着.NET 8等新版本的发布,期待更多创新技术如量子计算、AR/VR的融合,.NET将持续推动软件开发的创新与进步。
|
5天前
|
人工智能 前端开发 Devops
【专栏】洞察.NET 技术在现代开发中的作用
【4月更文挑战第29天】本文探讨了.NET技术在现代软件开发中的核心价值、应用及挑战。.NET提供语言统一性与多样性,强大的Visual Studio工具,丰富的类库,跨平台能力及活跃的开发者社区。实际应用包括企业级应用、Web、移动、云服务和游戏开发。未来面临性能优化、容器化、AI集成等挑战,需持续创新。开发者应深入理解.NET,把握技术趋势,参与社区,共创美好未来。
|
5天前
|
开发工具 C# 开发者
【专栏】理解.NET 技术,开创美好未来
【4月更文挑战第29天】本文探讨了.NET技术在软件开发中的关键作用,强调其核心优势,如语言多样性、丰富类库、强大的开发工具和跨平台能力。.NET在现代应用开发中涉及企业级应用、云服务集成、微服务、移动应用和游戏开发。未来,.NET将持续创新,提升性能,拓展应用场景,并促进更紧密的社区合作,通过跨平台框架扩大应用范围。开发者应深入学习.NET,抓住技术趋势,共创美好未来。
|
5天前
|
机器学习/深度学习 人工智能 开发者
【专栏】.NET 技术:为开发带来新机遇
【4月更文挑战第29天】本文探讨了.NET技术如何为软件开发带来新机遇,分为三个部分:首先,.NET的跨平台革命,包括.NET Core的兴起、Xamarin与.NET MAUI的移动应用开发、开源社区的推动及性能优化;其次,介绍了云服务与微服务架构的集成,如Azure云服务、微服务支持、DevOps与CI/CD,以及Docker容器化;最后,讨论了AI与机器学习集成,如ML.NET、认知服务、TensorFlow和ONNX,使开发者能构建智能应用。面对这些机遇,开发者应不断学习和适应新技术,以创造更多价值。