现在的位置: 首页 > 综合 > 正文

Asp.net 2.0 自定义控件开发[实现自动计算功能(AutoComputeControl)][示例代码下载]

2013年11月09日 ⁄ 综合 ⁄ 共 9726字 ⁄ 字号 评论关闭

(一). 概述        

 业余时间做了一个非常有用的控件, 介绍一下.
         一般当我们要实现这样一个计算功能页面:
                      TextBox1(单价)  
* TextBox2(数量) = TextBox3(总和);
        并且当在TextBox1或TextBox2中输入数据, 鼠标离开时, TextBox3控件能够即时重新计算新值(乘积).

       一般我们的做法步骤是:
              1. 拖三个控件到页面上, 默认三个TextBox控件ID分别为: TextBox1, TextBox1, 
                  TextBox3.

              2. 写个JavaScript 函数, 能够计算 TextBox1和TextBox2的乘积, 并赋值给TextBox3
                  即时最新值.

                      script language='javascript'> 
                          function compute() 
                          {
                                  var _num 
= parseFloat(document.getElementById('TextBox1').value);
                                  var _price 
= parseFloat(document.getElementById('TextBox2').value);
                                  document.getElementById(
'TextBox3').value=_num*_price;
                         }
                      
script>

              3. 注册TextBox1和TextBox2的onblur事件(TextBox控件失去焦点时触发).
                  
this.TextBox1.Attributes.Add("onblur","compute()");
                  
this.TextBox2.Attributes.Add("onblur","compute()"); 

             OK, 这样固然能够完成.  也存在以下缺点:
             1. 不够通用, 如果好多页面都需要这么一个控件,  那得写上面这些代码到所有
                 页面中. 开发效率不高. 且容易出错.

             2. 页面中代码比较乱. 嵌入好多JavaScript代码. 难以维护.
             
3. 假如运算表达式非常复杂[如:  A*B+(B*C)+Math.E  ] 每次设置JS也比较麻烦.

            基于以上缺陷,   
            下面是一个通用的自定义控件AutoComputeControl(自动计算),  能够弥补以上缺点, 
    且具有通用性, 

            特点如下:
              1. 使用简单,  只需设置一个表达式属性Expression, 控件能够自动完成所有JS脚本.
                  其中表达式由: 运算符和变量组成(控件的ID), 等一下会详细介绍使用方法.

              2. 不仅支持简单运算, 更大的优势是支持复杂表达式, 如:

                  price*(num+2*(3+6))*Math.E = sum ,  即
                 TextBox1
*(TextBox2+2*(3+6)) * Math.E =TextBox3            

                  仅通过将控件的: ID和运算符 任意组合成计算表达式赋值给控件Expression属性, 

                  其它的工作由本控件来完成, 本控件能够自动生成所有JS脚本. 并最终在页面客
     户端呈现.  

             3. 另外, 支持Math对象下面的属性和方法:
                       Math属性:   Math.E    Math.LN10 Math.LN2  Math.LOG10E  Math.LOG2E  
                                             Math.PI   Math.SQRT1_2     Math.SQRT2

                      Math方法:    Math.abs(x)   Math.acos(x)   Math.asin(x)   Math.atan(x)   
                                             Math.atan2(x,y)    Math.ceil(x)   Math.floor(x)   Math.cos(x) 
                                             Math.exp(x)   Math.log(x)   Math.max(x,y)   Math.min(x,y)   
                                             Math.pow(x,y)   Math.random()   Math.round(20.49)   Math.sin(x)
                                             Math.sqrt(x)   Math.tan(x)

                       例如:   Math.sin(Math.sqrt(price1*price2))+Math.E*num=sum

                           即    Math.sin(Math.sqrt(TextBox1*TextBox2))+Math.E*TextBox3=TextBox4

             4. 最终用户还可以在控件中输入表达式:

                      例如, 用户在TextBox1框中输入: 6*(5+2)

                              再在TextBox2中输入: 3+Math.E*Math.PI

                              再把表达式: TextBox1*(TextBox2+2*(3+6)) * Math.E =TextBox3            

                              赋值给本控件属性Expression, 仍然能够正确计算出结果.

             5. 在一个页面中放多个本控件.

                 比如: 拖个本控件到页面跟 TextBox1/TextBox2/TextBox3 组合,

                         再拖另一个控件跟另一组  TextBox4/TextBox5/TextBox5  组合.

                 注意: 不要两组表达式产生冲突, 比如把TextBox1即在第一组又在

                         第二组, 这样脚本生成是正确的, 但这样自动生成的客户端脚本, 会

                         为TextBox1注册两个onblur事件, 那么默认第二个onblur起效, 不能

                         同时起效.  这是JavaScript 语法规定的.  

                                                


          实现原理:   本自定义控件的Expression属性, 如: TextBox1*(TextBox2+TexBox3)*0.90=TextBox4

                             中包括:

                                  1. 控件的ID.  2. 表达式之间的关系.

                             然后自定义控件内核代码会:

                                  1. 用编译算法扫描表达式属性(Expression)值, 分析运算关系.

                                  2. 根据运算关系动态生成要呈现到客户端的JavaScript, 再最终由自定义控件呈现.

(二).  使用步骤

         1. 拖三个TextBox控件到页面上,  如图:

        2. 设置TextBox1的ID属性为: price;   TextBox2的ID属性为: num;  TextBox3的ID属性为: sum.

        3. 再添加一个AutoCompute控件(本文章主讲控件), 并设置其: Expression 属性值为:

             price * num = sum ,  意思是: [单价] * [数量] = [总额]

        4. 运行即可.  运行后当输入[单价]和[数量]时, 程式能够自动计算[总额]的值.

            测试: 当鼠标从[单价]或[数量]控件失去焦点时, 总额值能够重新被计算显示.

       5. 扩展: 

               a. 您也可以输入更复杂的表达式, 比如从上面DropDownList(测试用的控件)里面随便选几个.

               b. 不仅能够计算三个TextBox的表达式(如上), 您还可以添加N个TextBox表达式.

        功能比较强大吧  :) :) 

(三). 表达式规则:

        支持JavaScript运算规则,  并支持Math对象的所有属性和方法.

(四). 核心代码

       1. Node类文件Node.cs, 用于编译算法中存储数据结点和字符结点

 1/// 


 2    /// Author: [ ChengKing(ZhengJian) ] 
 3    /// Blog:   Http://blog.csdn.net/ChengKing
 4    /// 
 5    /// 


 6    /// Node 的摘要说明
 7    /// 
 8    /// 


 9    /// 结点类[操作符结点] 
10    /// 
11    public class Node
12    {
13        public string str;       //存储本节点字串
14        public int startIndex;   //用于存储一个结点所在[运算表达式]的开始索引位置
15        public int endIndex;     //用于存储一个结点所在[运算表达式]的结束索引位置    
16
17
18        public Node(int startIndex, int endIndex, string str)
19        {
20            this.str = str;
21            this.startIndex = startIndex;
22            this.endIndex = endIndex;        
23        }

24    }

       2. 主要控件类 AutoCompute.cs 代码

 1 /// 


 2     /// Author: [ ChengKing(ZhengJian) ] 
 3     /// Blog:   Http://blog.csdn.net/ChengKing
 4     /// 
 5     [DefaultProperty("Text")]
 6     [ToolboxData("{0}:AutoCompute>")]
 7     public class AutoCompute : Control
 8     {
 9         [Bindable(true)]
10         //[Category("外观")]
11         [DefaultValue("[AutoCompute /"AutoCompute1/"]")]
12         [Localizable(true)]
13         public string Text
14         {
15             get
16             {
17                 String s = (String)ViewState["Text"];
18                 return ((s == null? String.Empty : s);
19             }
20 
21             set
22             {
23                 ViewState["Text"= value;
24             }
25         }
26 
27         [Bindable(true)]        
28         [DefaultValue("")]
29         [Localizable(true)]
30         public string Expression
31         {
32             get
33             {
34                 string s = (string)this.ViewState["Expression"];
35                 return ((s == null? String.Empty : s);
36             }
37             set
38             {
39                 this.ViewState["Expression"= value;
40             }
41         }
42 
43         protected override void Render(HtmlTextWriter writer)
44         {
45             if (DesignMode)
46             {
47                 this.Controls.Clear();
48                 LiteralControl lc = new LiteralControl();
49                 lc.Text = this.Text;
50                 this.Controls.Add(lc);
51             }         
52             base.Render(writer);
53         }
54 
55         protected override void OnPreRender(EventArgs e)
56         {
57             base.OnPreRender(e);
58 
59             ConvertHelper _ConvertHelper = new ConvertHelper();
60             string strClientScript;
61             try
62             {
63                 if (this.Expression.Trim().Length != 0)
64                 {
65                     _ConvertHelper.Main(Page, this.Expression);
66                     _ConvertHelper.RegisterClientScript(this.Page);
67                 }
68                 else
69                 {
70                     strClientScript = "   alert('No Set [Expression] Property!');";
71                     if (!Page.ClientScript.IsStartupScriptRegistered("Default_Property"))
72                     {
73                         Page.ClientScript.RegisterStartupScript(this.GetType(), "Default_Property", strClientScript, true);
74                     }
75                 }
76             }
77             catch
78             {
79                 strClientScript = "   alert('The [Expression] format is not correct!');";
80                 if (!Page.ClientScript.IsStartupScriptRegistered("Default_Property"))
81                 {
82                     Page.ClientScript.RegisterStartupScript(this.GetType(), "Default_Property", strClientScript, true);
83                 }
84             }
85 
86         }
87 
88     }

       3. ConvertHelper.cs类文件, 主要实现编译算法以及JavaScript脚本生成注册功能.

  1  /// 


  2     /// Author: [ ChengKing(ZhengJian) ] 
  3     /// Blog:   Http://blog.csdn.net/ChengKing
  4     /// 
  5     /// 


  6     /// ConvertHelper 的摘要说明
  7     /// 
  8     /// 


  9     /// 算法概述:
 10     ///  引用概念: [数据变量结点]: 用户命名的字串, 如: total = price*num, 则 "price" 字串就为数据变量结点
 11     ///  1. 抽取出操作运算符. 并记住所有运算符的索引
 12     ///  2. 两个操作符之间的字符串为[数据变量结点(用户命名的字串)], 但下面几种情况要排除:
 13     ///       a. 提取的相邻字符中, 右边字符为"("操作符时, 中间不是数据变量结点.
 14     ///       b. 两个操作符相邻时, 其中间没有字符串, 显然也就没有数据变量结点.
 15     ///       c. 数据变量结点必须是字符串变量, 不能为数值.
 16     ///       d. 排除Math.E等常量情况(Math.E/Math.LN10/Math.LN2/Math.LOG10E/Math.LOG2E/ Math.PI/Math.SQRT1_2/Math.SQRT2).
 17     /// 
 18     public class ConvertHelper
 19     {
 20         /// 


 21         /// 存放JavaScript运算符的各种结点
 22         /// 
 23         private string[] OP_Chars = new string[7] { "+""-""*""/""("")""," };
 24 
 25         /// 


 26         /// 自定义变量前缀符号
 27         /// 
 28         private string VarPreSymbol = "_";
 29 
 30         /// 


 31         /// 存储要读取控件的属性(如: t.text/t.Value etc)
 32         /// 
 33         private string ValueSymbol = ".value";
 34 
 35         /// 


 36         /// 存储compute方法脚本变量
 37         ///   
 38         private string ComputeScript = "";
 39 
 40         /// 


 41         /// 存储onblur方法脚本变量
 42         ///   
 43         private string OnblurScript = "";
 44 
 45         /// 


 46         /// 区别于方法名的序列号[依次递增, 如: compute1, compute2, compute3  ]
 47         /// 
 48         private int SequenceNum = 1;
 49 
 50         /// 


 51         /// 抽取出运算符结点[其中包括运算符结点的位置信息]
 52         /// 
 53         /// 
 54         /// 
 55         private ListNode> BuildOPNode(string strObject)
 56         {
 57             int beginIndex = 0//记录当前处理结点的起始索引       
 58             ListNode> nodes = new ListNode>();
 59 
 60 
 61             while (true)
 62             {
 63                 if (beginIndex ==

抱歉!评论已关闭.