TextBox控件实现的自动计算

导读:
  前几天做了一个自定义控件AutoComputeControl, 具体请见:
  在读本文章之前请先读一下上面链接所指向的文章.
  此控件在99%情况下, 能够很方便地处理页面上TextBox控件之间的自动计算. 但有一种情况, 它有点不完善.
  举例, 假如要计算这样的表达式, 如图:
  
  
  
  从图上可以看出, 要同时计算的两个表达式中, 其中 ID为:price 的TextBox同时参与了两个表达式的运算.
  如果按上个控件的计算方法, 经过编译分析表达式后最终是产生的脚本如下:
  
  
  
  由控件自动生成的JavaScript可以看出, 脚本代码能够正确的生成; 但发现一个小问题, price控件同时注册了两个onblur事件, 也就是说当price控件失去焦点时要同时执行方法compute1和compute2, 结果才能够计算正确; 但基于JavaScript语法限制, 当注册我个onblur方法(别的事件也一样)时, 默认最后一个起效, 也就是说在上面的代码中, 当price 控件失去焦点时, 只有compute2方法计算, 从程序逻辑讲这是不合理的.
  先看一下采用新算法生成的客户端代码是什么样的(JavaScript伪代码):
  
  
  以上是新算法生成的JS伪代码, 从图有些读者已经能够看出里面的弦机, 事实
  上在生成js时, 算法也没有上图看起来这么简单, 有些情况还涉及到无限递归.
  下面就详细说一下新的设计思路. 在看代码以前请先看看新的设计思路文档. 新生成
  的JS代码也会在下面发布.
   (二). 新设计方案文档
   1). 自动完成组件功能概述
  
  u 通过使用场景理解:
  1. 举例: 页面上有一组控件, 其中包括: 五个TextBox控件, 并且它们的ID属性依次为: A B C D E; 另外有个表达式控件F(此控件带个表达式属性, 用来建立要计算的控件的值之间的运算关系). 其中表达式属性设置为: A*(B+C) *D*0.98 + E ;另外本控件还用来存储显示的计算结果.
  
  
  
  2. 功能描述: 当页面运行时, 修改A B C D E控件值(比如: A=5, B=6, …), 并且A B C D E等其中任一个控件失去焦点时, E控件会根据表达式重新计算更新到最新值.
  3. 以上a) 和b)是用一组控件(只含一组表达式)进行示例, 它还支持页面上同时放置多组表达式控件, 并且组间控件能够进行交叉.
  
  
  
  4. 运行状态, 支持嵌套表达式运算. 比如TextBox控件中也能够输入表达式(比如: A中输入的不是6, 而是6*(8+2)). 也能够正确计算出结果.
  
  
  
  u 实现方案概要
  运行时只需设置一个属性: “运算表达式”字串, 以下简称Expression.
  其中E包括了本自动计算组件所需的两个重要参数条件: 1. 控件的ID; 2. 控件之间的关系运算, 以下就是通过这两个参数条件进行展开运算.
  算法概要流程图:
  
  
  (图一)
  1. 对Expression用编译算法进行扫描, 区分出:
  哪些是: 数据结点(用于输入数据的控件, 如: A B C D E等TextBox控件);
  哪些是: 运算符(JavaScript中的运算符, 如: + - * / )
  并确定每个数据结点在E中的起始/结束位置索引等信息.
  2. 根据a) 得出的数据信息和运算表达式运算关系, 用C# 动态生成每组控件的计算表达式JavaScrpt代码, 并注册A B C D E等TextBox控件的引发事件, 组装成的JavaScript脚本格式如下:
  
  1 //计算表达式代码
  2 <script language='javascript'> 3 function compute1() 4 { 5 var _C = parseFloat(eval(document.getElementById('C').value)); 6 var _D = parseFloat(eval(document.getElementById('D').value)); 7 document.getElementById('A').value=_C+_D; 8 } 9 </script>
  10
  11 <script language='javascript'> 12 function compute2() 13 { 14 var _A = parseFloat(eval(document.getElementById('A').value));15 var _E = parseFloat(eval(document.getElementById('E').value));16 document.getElementById('B').value=_A*_E;17 }18 </script>
  19
  20 //注册引发事件
  21 <script language='javascript'> 22 function onblurA() 23 { 24 compute1();25 compute2();26 }27 </script>
  28
  
  (代码一)
  
  注意: 表达式不是固定不变的, 在运行状态, 动态改变表达式字串属性, 则动态
  生成的JavaScript也不同. 可以任意改变计算表达式之间的运算关系.
  
  2). 详细设计算法流程图
  
  u 编译字符串算法
  得到操作符结点信息(如+-*/等)
  扫描字符串, 当遇到有操作符字符{ "+", "-", "*", "/", "(", ")", ","}, 将
  其存储到数组中.
  根据以上得到的操作符信息集合, 得到数据结点信息(如A B C D E等TextBox的ID)
  
  
  算法规则概述:
  一般情况下, 两个操作符(+-*/等)之间的字符串为[数据变量结点(如: A B C等TextBox的ID)], 但下面几种情况要排除:
  a) 提取的相邻字符中, 右边字符为"("操作符时, 中间不是数据变量结点.
  b) 两个操作符的索引位置相邻时, 其中间没有字符串, 显然也就没有数据变量结点.
  c) 数据变量结点必须是字符串变量, 不能为数值字符串(如:”568”).
  d) 排除Math.E等常量情况,因为这些常量也满足条件1, 包括(Math.E/Math.LN10/Math.LN2/Math.LOG10E/Math.LOG2E/ Math.PI/Math.SQRT1_2/Math.SQRT2).
  
  u 注册生成JavaScript算法
  生成并注册客户端脚本Compute核心方法
  根据上一步编译字符串得到的数据结点和运算关系, 用C#组装生成并注册一系列的Compute方法, 为避免多个表达式之间的冲突, 这里用累计数值的方式, 如:第一个表达式生成的计算方法为Compute1(){}, 第二个为: Compute2(){}, 第三个为: Compute3(){} ….
  具体代码格式请看上面的: (代码一).
  生成并注册客户端脚本Onblur方法.
  同Compute, 也是根据编译字符串得到的数据结点和运算关系表达式来用C#
  组装生成客户端能够直接运行的JavaScript脚本. 其中数据结点控件B 生成的代码格式如下:
  1 <script type="text/javascript">
  2
  5 </script>
  6
  7 <script language='javascript'> 8 function onblurB() 9 { 10 compute1();11 compute2();12 compute1();13 }14 </script>
  15
  
  (代码二)
  
  
  这里算法比较复杂, 下面举两个例子描述一下, 请先看一个运行的示例界面:
  
  其中左边五个TextBox的ID从上到下依次为: A B C D E;
  右边的TextBox表示: A的值= 下面的C的值+ D的值.
  B的值= 下面的A的值* E的值.
  …….
  …….
  并且设A B C D E控件根据表达式生成的客户端的计算方法依次为:
  ComputeA();ComputeB();ComputeC();ComputeD();ComputeE();
  
  1. 具体算法思路, 举例当输入A框值后执行的步骤.
  
  在A控件中输入值后,并让A框失去焦点.
  
  根据右边的五个表达式可以看出, 如果A的值变了, 则会影响到B和C框的值,那么就要重新计算B和C的值, 必须执行: ComputeB和ComputeC.
  
  依次检查刚刚修改过值的B和C. 当B修改之后, 检查一下右边的表达式, 发现没有包括B的表达式. 则这个B点往下分支循环结束检查一下C, 会发现A的表达式中, 应该执行A表达式, 这里有条约束, 由于是A触发的onblur事件(从A开始展开循环执行), 不能再继续修改A的值, 也中断循环. 整个循环结束.
  
  总结以上执行过程, 是这样的:
  
  
  
  2. 再举个有些复杂的执行情况, 当输入D框值后执行的步骤.
  
  在D控件输入值, 并且让D控件失去焦点
  看右边表达式, 如果D值改变会影响到控件A和控件C的值,
  则要执行ComputeA()和ComputeC()方法, 重新计算A和C的值.
  由于A的值和C的值修改了, 则要继续判断A和C影响到哪些控件
  的值. 其中, A影响到B和C; C影响到A.
  则A和C共影响到A,B,C结点的值, 依次执行ComputeA(), ComputeB(), ComputeC()方法.
  再继续判断刚刚值改变的A, B, C的值, 看看A, B, C分别都会影响
  到哪些值改变, 一直遵循这样的规律判断…….
  会发现它一直这样循环下去, 无终结.
  总结用图来直观地看一下以上执行过程, 是这样执行的:
  
  
  为了不让它们限入死循环, 我们限定循环的终止条件有三个:
  
  1. 正确结束: 当前结点值改变后不会影响其它任何结点的值改变.
  2. 触发起始结点结束: 假如是从A开始的, 即先执行A的onblur(鼠标失去焦点,然后导致触发ComputeA)事件, 则当其它结点再影响到A时, 则中断执行.
  3. 深度超过3层则强制循环结束.
  这里分两种情况:
  a) 用户输入的一系列表达式之间是不符合正确逻辑的, 如:
  {
  A := B + 1
  B := A + 1
  }
  显然程序执行时会使A和B不断依次加1, 限入无限死循环
  中.
  b) 用户输入一系列表达式之间是符合正确逻辑的.
  有时候也会限入死循环, 但当循环到一定次数时, 由于数据是符合正确逻辑的, 虽然是产生死循环, 但数据是正确的.如:
  {
  A := B + 1
  B := A - 1
  }
  这样A和B会互相影响, 显然会产生死循环, 依次执行:
  ComputeA();
  ComputeB();
  ComputeA();
  ComputeB();
  ComputeA();
  ComputeB();
  ……
  ……
  但这时数值不会计算错误, 举个实例, 假如在B控件中
  输入值: 5, 则会导致A变为6, 由于A变了6又会导致B变
  为5,一直这样循环下去 但A值永远为6, B值永远为5.
  由此我们控制它最多由上到下执行3层.
  
  3). 使用方法
  主类为ConvertHelper.cs , 直接声明实例调用其方法即可. 调用示例:
  
  1 //声明实例
  2
  3 ConvertHelper ch = new ConvertHelper(); 4
  5 6
  7 ///
  8
  9 ///注册一系列(这里是五个)表达式, 并生成一系列Compute()方法10
  11 /// 12
  13 /// 当前页面对象
  14
  15 /// 控件ID
  16
  17 /// 第second 个参数控件的表达式
  18
  19 /// 存放计算结果的控件
  20
  21 ch.RegisterClientScript_Compute(this.Page, "A", this.Expression1.Text, "A");22
  23 ch.RegisterClientScript_Compute(this.Page, "B", this.Expression2.Text, "B");24
  25 ch.RegisterClientScript_Compute(this.Page, "C", this.Expression3.Text, "C");26
  27 ch.RegisterClientScript_Compute(this.Page, "D", this.Expression4.Text, "D");28
  29 ch.RegisterClientScript_Compute(this.Page, "E", this.Expression5.Text, "E");30
  31 //注册所有的Onblur方法
  32
  33 ch.RegisterClientScript_Onblur(this.Page);34
  35
  
   (三). 新方案核心源代码
   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 Node12 {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. 新增BlurNode类文件BlurNode.cs
  1 ///
  2 /// Author: [ ChengKing(ZhengJian) ] 3 /// Blog: Http://blog.csdn.net/ChengKing 4 ///
  5 ///
  6 /// 用于存储一个数据结点以及与其它表达式之间关系的类 7 ///
  8 class BlurNode 9 {10 public string strDataNodeName; //存放一个数据结点字串, 如: "price"
  11 public string strRelationDataNodes; //表达式中与之有关联的字串, 如: "price * sum"
  12 public string strBlurFuncName; //存储动态生成的JavaScript焦点失去函数Blur方法名称, 如: Blur_price
  13
  14 public string strExpression;15
  16 public BlurNode()17 {18 }19
  20 public BlurNode(string strDataNode, string strRelationDataNodes, string strBlurFuncName, string strExpression)21 {22 this.strDataNodeName = strDataNode;23 this.strRelationDataNodes = strRelationDataNodes;24 this.strBlurFuncName = strBlurFuncName;25
  26 this.strExpression = strExpression;27 }28 }
  3. 主要控件类 AutoCompute.cs 代码
  1 ///
  2 /// Author: [ ChengKing(ZhengJian) ] 3 /// Blog: Http://blog.csdn.net/ChengKing 4 ///
  5 [DefaultProperty("Text")] 6 [ToolboxData("<{0}:AutoCompute runat=server>")] 7 public class AutoCompute : Control 8 { 9 [Bindable(true)]10 //[Category("外观")]
  11 [DefaultValue("[AutoCompute /"AutoCompute1/"]")]12 [Localizable(true)]13 public string Text14 {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 Expression31 {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 List dataNodes = _ConvertHelper.BuildDataNode(this.Expression); 68 for (int i = 0 i < dataNodes.Count; i++)69 {70 _ConvertHelper.RegisterClientScript_Compute(this.Page, dataNodes[i].str, this.Expression);71 }72 _ConvertHelper.RegisterClientScript_Onblur(this.Page);73 }74 else
  75 {76 strClientScript = " alert('No Set [Expression] Property!');"
  77 if (!Page.ClientScript.IsStartupScriptRegistered("Default_Property"))78 {79 Page.ClientScript.RegisterStartupScript(this.GetType(), "Default_Property", strClientScript, true);80 }81 }82 }83 catch
  84 {85 strClientScript = " alert('The [Expression] format is not correct!');"
  86 if (!Page.ClientScript.IsStartupScriptRegistered("Default_Property"))87 {88 Page.ClientScript.RegisterStartupScript(this.GetType(), "Default_Property", strClientScript, true);89 }90 }91
  92 }93
  94 }
  4. 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 /// 表示当前处理哪个控件的Onblur事件 52 ///
  53 private string strTheStartOnblurNodeName = "" 54
  55 ///
  56 /// 抽取出运算符结点[其中包括运算符结点的位置信息] 57 ///
  58 ///
  59 ///
  60 private List BuildOPNode(string strObject) 61 { 62 int beginIndex = 0 //记录当前处理结点的起始索引
  63 List nodes = new List (); 64
  65
  66 while (true) 67 { 68 if (beginIndex == strObject.Length) 69 { 70 break
  71 } 72
  73 for (int j = 0 j < OP_Chars.Length; j++) 74 { 75 if (strObject.Length - beginIndex >= OP_Chars[j].Length) 76 { 77 if (OP_Chars[j] == strObject.Substring(beginIndex, OP_Chars[j].Length)) 78 { 79 //操作符
  80 Node node = new Node(beginIndex, beginIndex + OP_Chars[j].Length - 1, strObject.Substring(beginIndex, OP_Chars[j].Length)); 81 nodes.Add(node); 82 break
  83 } 84 } 85 } 86 beginIndex++
  87 } 88 return nodes; 89 } 90
  91 ///
  92 /// 根据运算符结点抽取出数据结点[其中包括数据结点的位置信息] 93 ///
  94 ///
  95 ///
  96 public List BuildDataNode(string strObject) 97 { 98 if (strObject.IndexOf("=") >= 0) 99 {100 strObject = strObject.Substring(0, strObject.IndexOf("=")); ;101 }102 strObject = ClearSpace(strObject);103 List dataNodes = new List ();104 List opNodes = this.BuildOPNode(strObject);105
  106 //考虑表达式最左边是数据结点情况, 如: A+B 表达式中的A
  107 if (opNodes.Count > 0 && opNodes[0].startIndex != 0 && opNodes[0].str != "(")108 {109 string str = strObject.Substring(0, opNodes[0].startIndex);110 if (this.JudgeFigure(str) == false && this.IsIndexOfMath(str) == false)111 {112 Node node = new Node(0, opNodes[0].startIndex - 1, str);113 dataNodes.Add(node);114 }115
  116 }117
  118 //根据操作运算符求得中间的一系列数据结点
  119 for (int i = 0 i < opNodes.Count - 1 i++)120 {121 if (this.IsDataNodeBetweenOPNodes(opNodes[i], opNodes[i + 1], strObject))122 {123 Node node = new Node(opNodes[i].endIndex + 1, opNodes[i + 1].startIndex - 1, strObject.Substring(opNodes[i].endIndex + 1, opNodes[i + 1].startIndex - opNodes[i].endIndex - 1));124 dataNodes.Add(node);125 }126 }127
  128 //考虑最右端是数据结点情况, 如: A+B 表达式中的B
  129 if (opNodes.Count > 0 && (opNodes[opNodes.Count - 1].endIndex != strObject.Length - 1))130 {131 string str = strObject.Substring(opNodes[opNodes.Count - 1].endIndex + 1);132 if (this.JudgeFigure(str) == false && this.IsIndexOfMath(str) == false)133 {134 Node node = new Node(opNodes[opNodes.Count - 1].endIndex + 1, strObject.Length - 1, str);135 dataNodes.Add(node);136 }137 }138 return dataNodes;139 }140
  141 ///
  142 /// 判断相邻结点中间是否是数据结点143 ///
  144 ///
  145 ///
  146 ///
  147 /// 根据以下定理进行判断148 /// a. 提取的相邻字符中, 右边字符为"("操作符时, 中间不是数据变量结点.149 /// b. 两个操作符相邻时, 其中间没有字符串, 显然也就没有数据变量结点.150 /// c. 数据变量结点必须是字符串变量, 不能为数值.151 /// d. 排除Math.E等常量情况(Math.E/Math.LN10/Math.LN2/Math.LOG10E/Math.LOG2E/ Math.PI/Math.SQRT1_2/Math.SQRT2).
  152 private bool IsDataNodeBetweenOPNodes(Node leftNode, Node rightNode, string strObject)153 {154 //条件a
  155 if (rightNode.str == "(")156 {157 return false
  158 }159
  160 //条件b
  161 if (leftNode.endIndex + 1 == rightNode.startIndex)162 {163 return false
  164 }165
  166 //条件c
  167 if (this.JudgeFigure(strObject.Substring(leftNode.endIndex + 1, rightNode.startIndex - leftNode.endIndex - 1)) == true)168 {169 return false
  170 } 171 172 if (this.IsIndexOfMath(strObject.Substring(leftNode.endIndex + 1, rightNode.startIndex - leftNode.endIndex - 1)))173 {174 return false
  175 }176
  177 return true
  178 }179
  180 ///
  181 /// //判断是否Math.开头 排除(Math.E/Math.LN10/Math.LN2/Math.LOG10E/Math.LOG2E/ Math.PI/Math.SQRT1_2/Math.SQRT2)等常量182 ///
  183 ///
  184 ///
  185 public bool IsIndexOfMath(string str)186 {187 if (str.IndexOf("Math.") == 0)188 {189 return true
  190 }191 return false
  192 }193
  194 ///
  195 /// 判断是否是数字196 ///
  197 ///
  198 ///
  199 private bool JudgeFigure(string str)200 {201 if (str.Trim().Length <= 0)202 return true
  203 int dot = 0
  204 if (str[0] == '.' || str[str.Length - 1] == '.')205 return false
  206 for (int i = 0 i < str.Length; i++)207 {208 if (dot > 1) return false
  209 if (Char.IsDigit(str, i))210 {211 continue
  212 }213 if (str[i] == '.')214 {215 dot = dot + 1
  216 continue
  217 }218 return false
  219 }220 return true
  221 }222
  223 ///
  224 /// 生成客户端计算表达式的脚本(Compute客户端方法)225 ///
  226 ///
  227 ///
  228 /// 显示结果的容器
  229 ///
  230 private string CreateClientScript_Compute(Page page, string strControl, string strExpression, List dataNodes, string strEnd)231 {232 ///
  233 /// 生成并注册compute方法脚本234 ///
  235 int intNumDataNodeCount = dataNodes.Count;236
  237 //调整方法名, 防止多个表达式运算时, 方法名冲突
  238 while (true)239 {240 if (!page.ClientScript.IsClientScriptBlockRegistered(this.GetType(), "compute" + SequenceNum.ToString()))241 {242 break
  243 }244 SequenceNum++
  245 }246
  247 //生成脚本头JS字串
  248 string strJSHead = "<script language='javascript'> /n function compute" + this.SequenceNum.ToString() + "() /n { /n"
  249
  250 //生成脚本体JS字串
  251 string strJSBody = ""
  252 for (int i = 0 i < intNumDataNodeCount; i++)253 { 254 strJSBody += " var " + VarPreSymbol + dataNodes[i].str + " = parseFloat(eval(document.getElementById('" + ((Control)page.FindControl(dataNodes[i].str)).ClientID + "')" + ValueSymbol + "));/n" 255 }256 257 strJSBody += " document.getElementById('" + ((Control)page.FindControl(strEnd)).ClientID + "')" + ValueSymbol;258 259 strJSBody += "="
  260
  261 string strFormatExpression = strExpression; //格式化表达式
  262 for (int i = 0 i < intNumDataNodeCount; i++)263 {264 strFormatExpression = strFormatExpression.Remove(dataNodes[i].startIndex, dataNodes[i].str.Length);265 strFormatExpression = strFormatExpression.Insert(dataNodes[i].startIndex, "_" + dataNodes[i].str);266 this.RepairNodes(ref dataNodes, i + 1);267 }268 strFormatExpression += ""
  269 strJSBody += strFormatExpression;270 string strJSFoot = "/n }/n  271
  272 string strReturnScript = strJSHead + strJSBody + strJSFoot;273 this.ComputeScript = strReturnScript;274
  275
  276 #region 存储BurlNodes信息
  277
  278 //存储BlurNodes, 备以后用来生成所有Blur事件
  279 List list;280 if (page.Session["BlurNodes"] == null)281 {282 list = new List ();283 }284 else
  285 {286 list = (List )page.Session["BlurNodes"];287 }288
  289 //与本结点表达式有关的其它结点集合
  290 string strRelationNodes = ""
  291 for (int i = 0 i < intNumDataNodeCount; i++)292 {293 if (i == dataNodes.Count - 1)294 {295 strRelationNodes += dataNodes[i].str;296 continue
  297 }298 strRelationNodes += dataNodes[i].str + ","
  299 }300
  301 list.Add(new BlurNode(strControl, strRelationNodes, "compute" + this.SequenceNum.ToString(), strExpression));302
  303 //保存到Session
  304 if (page.Session["BlurNodes"] != null)305 {306 page.Session.Remove("BlurNodes");307 }308 page.Session["BlurNodes"] = list;309
  310 #endregion
  311
  312
  313 //strReturnScript += strOnBlur;
  314 return strReturnScript;315 }316
  317
  318 ///
  319 /// 重新调整数据节点集合的索引值320 ///
  321 ///
  322 ///
  323 private void RepairNodes(ref List nodes, int index)324 {325 for (int i = index; i < nodes.Count; i++)326 {327 //6相当于前面数据结点插入的 ".value" 的长度
  328 nodes[i].startIndex = nodes[i].startIndex + VarPreSymbol.Length;329 nodes[i].endIndex = nodes[i].endIndex + VarPreSymbol.Length;330 }331 }332
  333 ///
  334 /// 对输入表达式进行验证335 ///
  336 ///
  337 ///
  338 ///
  339 private bool ValidateExpression(Page page, string str)340 {341 str = this.ClearSpace(str);342 if (CheckParenthesesMatching(str) == false)343 {344 page.Response.Write("




括号不匹配!");345 return false
  346 }347 return true
  348 }349
  350 //检查括号是否匹配
  351 private bool CheckParenthesesMatching(string strCheck)352 {353 int number = 0
  354 for (int i = 0 i < strCheck.Length; i++)355 {356 if (strCheck[i] == '(') number++
  357 if (strCheck[i] == ')') number--
  358 if (number < 0) return false//右括号不能在前面
  359 }360 if (number != 0)361 {362 return false
  363 }364 return true
  365 }366
  367 //消去空格
  368 private string ClearSpace(string str)369 {370 return str.Replace(" ", "");371 }372
  373 ///
  374 /// 注册客户端脚本_Compute375 ///
  376 ///
  377 /// 表达式字串
  378 /// /// 显示结果的容器
  379 ///
  380 public string RegisterClientScript_Compute(Page page, string strControl, string strExpression)381 {382 strExpression = this.ClearSpace(strExpression);383 if (this.ValidateExpression(page, strExpression) == false)384 {385 return "表达式有误!"
  386 }387
  388 string strLeft = strExpression.Substring(0, strExpression.IndexOf("=")); ;389 string strRight = strExpression.Substring(strExpression.IndexOf("=") + 1);390
  391 string strReturn = this.CreateClientScript_Compute(page, strControl, strLeft, BuildDataNode(strExpression), strRight);392
  393 if (this.ComputeScript.Length == 0)394 {395 return "没有可注册的脚本!"
  396 }397
  398 if (!page.ClientScript.IsClientScriptBlockRegistered(this.GetType(), "compute" + this.SequenceNum.ToString()))399 {400 page.ClientScript.RegisterClientScriptBlock(this.GetType(), "compute" + this.SequenceNum.ToString(), this.ComputeScript, false);401 }402
  403 return strReturn;404 }405
  406 ///
  407 /// 注册所有客户端Onblur脚本408 ///

  409 public void RegisterClientScript_Onblur(Page page)410 {411 List list;412 if (page.Session["BlurNodes"] == null)413 {414 return
  415 }416
  417
  418 list = (List )page.Session["BlurNodes"];419
  420 foreach (BlurNode node in list)421 {422 string strOnblurScript = CreateClientScript_Onblur(node, list);423 if (!page.ClientScript.IsStartupScriptRegistered(this.GetType(), "onblur_" + node.strDataNodeName))424 {425 page.ClientScript.RegisterStartupScript(this.GetType(), "onblur_" + node.strDataNodeName, strOnblurScript, false);426 }427
  428 string strOnBlur = " document.getElementById('" + ((Control)page.FindControl(node.strDataNodeName)).ClientID + "')" + ".οnblur=onblur" + node.strDataNodeName + "/n"
  429 if (!page.ClientScript.IsStartupScriptRegistered(this.GetType(), "set_onblur_" + node.strDataNodeName))430 {431 page.ClientScript.RegisterStartupScript(this.GetType(), "set_onblur_" + node.strDataNodeName, strOnBlur, true);432 }433
  434 }435 }436
  437 ///
  438 /// 建立每个TextBox控件的生成脚本方法439 ///
440 /// 每个控件的Onblur事件执行的js脚本
  441 private string CreateClientScript_Onblur(BlurNode currentNode, List list)442 {443 string strJSHead = "/n
  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值