`
lovnet
  • 浏览: 6704766 次
  • 性别: Icon_minigender_1
  • 来自: 武汉
文章分类
社区版块
存档分类
最新评论

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

阅读更多

(一). 概述

前几天做了一个自定义控件AutoComputeControl, 具体请见:

http://blog.csdn.net/ChengKing/archive/2007/04/12/1562765.aspx

在读本文章之前请先读一下上面链接所指向的文章.

此控件在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 ; 另外本控件还用来存储显示的计算结果.

<shapetype id="_x0000_t75" stroked="f" filled="f" path="m@4@5l@4@11@9@11@9@5xe" o:preferrelative="t" o:spt="75" coordsize="21600,21600"><stroke joinstyle="miter"></stroke><formulas><f eqn="if lineDrawn pixelLineWidth 0"></f><f eqn="sum @0 1 0"></f><f eqn="sum 0 0 @1"></f><f eqn="prod @2 1 2"></f><f eqn="prod @3 21600 pixelWidth"></f><f eqn="prod @3 21600 pixelHeight"></f><f eqn="sum @0 0 1"></f><f eqn="prod @6 1 2"></f><f eqn="prod @7 21600 pixelWidth"></f><f eqn="sum @8 21600 0"></f><f eqn="prod @7 21600 pixelHeight"></f><f eqn="sum @10 21600 0"></f></formulas><path o:connecttype="rect" gradientshapeok="t" o:extrusionok="f"></path><lock aspectratio="t" v:ext="edit"></lock></shapetype><shape id="_x0000_i1025" style="WIDTH: 415.5pt; HEIGHT: 45.75pt" type="#_x0000_t75"><imagedata o:title="" src="file:///D:%5CDOCUME~1%5CZHENGJ~1%5CLOCALS~1%5CTemp%5Cmsohtml1%5C01%5Cclip_image002.png"></imagedata></shape>

2. 功能描述: 当页面运行时, 修改A B C D E控件值(比如: A=5, B=6, …), 并且A B C D E等其中任一个控件失去焦点时, E控件会根据表达式重新计算更新到最新值.

3. 以上a) b)是用一组控件(只含一组表达式)进行示例, 它还支持页面上同时放置多组表达式控件, 并且组间控件能够进行交叉.

<shape id="_x0000_i1026" style="WIDTH: 415.5pt; HEIGHT: 142.5pt" type="#_x0000_t75"><imagedata o:title="" src="file:///D:%5CDOCUME~1%5CZHENGJ~1%5CLOCALS~1%5CTemp%5Cmsohtml1%5C01%5Cclip_image004.png"><font face="Times New Roman" size="3"></font></imagedata></shape>

4. 运行状态, 支持嵌套表达式运算. 比如TextBox控件中也能够输入表达式(比如: A中输入的不是6, 而是6*(8+2)). 也能够正确计算出结果.

<shape id="_x0000_i1027" style="WIDTH: 417pt; HEIGHT: 41.25pt" type="#_x0000_t75"><imagedata o:title="" src="file:///D:%5CDOCUME~1%5CZHENGJ~1%5CLOCALS~1%5CTemp%5Cmsohtml1%5C01%5Cclip_image006.png"><font face="Times New Roman" size="3"></font></imagedata></shape>

u 实现方案概要

运行时只需设置一个属性: 运算表达式字串, 以下简称 Expression.

其中E包括了本自动计算组件所需的两个重要参数条件: 1. 控件的ID; 2. 控件之间的关系运算, 以下就是通过这两个参数条件进行展开运算.

算法概要流程图:

<shape id="_x0000_i1028" style="WIDTH: 415.5pt; HEIGHT: 284.25pt" type="#_x0000_t75"><imagedata o:title="" src="file:///D:%5CDOCUME~1%5CZHENGJ~1%5CLOCALS~1%5CTemp%5Cmsohtml1%5C01%5Cclip_image008.emz"><font face="Times New Roman" size="3"></font></imagedata></shape>

(图一)

1. Expression用编译算法进行扫描, 区分出:

哪些是: 数据结点(用于输入数据的控件, : A B C D ETextBox控件);

哪些是: 运算符(JavaScript中的运算符, : + - * / )

并确定每个数据结点在E中的起始/结束位置索引等信息.

2. 根据 a) 得出的数据信息和运算表达式运算关系, C# 动态生成每组控件的计算表达式JavaScrpt代码, 并注册A B C D ETextBox控件的引发事件, 组装成的JavaScript脚本格式如下:

1//计算表达式代码
2<scriptlanguage='javascript'>
3functioncompute1()
4{
5var_C=parseFloat(eval(document.getElementById('C').value));
6var_D=parseFloat(eval(document.getElementById('D').value));
7document.getElementById('A').value=_C+_D;
8}
9</script>
10
11<scriptlanguage='javascript'>
12functioncompute2()
13{
14var_A=parseFloat(eval(document.getElementById('A').value));
15var_E=parseFloat(eval(document.getElementById('E').value));
16document.getElementById('B').value=_A*_E;
17}
18</script>
19
20//注册引发事件
21<scriptlanguage='javascript'>
22functiononblurA()
23{
24compute1();
25compute2();
26}
27</script>
28

(代码一)

注意: 表达式不是固定不变的, 在运行状态, 动态改变表达式字串属性, 动态

生成的JavaScript也不同. 可以任意改变计算表达式之间的运算关系.

2). 详细设计算法流程图

u 编译字符串算法

Ø 得到操作符结点信息(+-*/)

扫描字符串, 当遇到有操作符字符{ "+", "-", "*", "/", "(", ")", "," },

其存储到数组中.

Ø 根据以上得到的操作符信息集合, 得到数据结点信息(A B C D ETextBoxID)

<shape id="_x0000_i1033" style="WIDTH: 414.75pt; HEIGHT: 321pt" type="#_x0000_t75"><imagedata o:title="" src="file:///D:%5CDOCUME~1%5CZHENGJ~1%5CLOCALS~1%5CTemp%5Cmsohtml1%5C01%5Cclip_image010.emz"></imagedata></shape>

算法规则概述:

一般情况下, 两个操作符(+-*/)之间的字符串为[数据变量结点(: A B CTextBoxID)], 但下面几种情况要排除:

a) 提取的相邻字符中, 右边字符为"("操作符时, 中间不是数据变量结点.

b) 两个操作符的索引位置相邻时, 其中间没有字符串, 显然也就没有数据变量结点.

c) 数据变量结点必须是字符串变量, 不能为数值字符串(:<chmetcnv w:st="on" tcsc="0" numbertype="1" negative="False" hasspace="False" sourcevalue="568" unitname="”"><span lang="EN-US" style="FONT-FAMILY: 新宋体; mso-hansi-font-family: 'Times New Roman'; mso-bidi-font-size: 10.5pt; mso-font-kerning: 0pt; mso-no-proof: yes">568</span><span lang="EN-US" style="mso-fareast-font-family: 新宋体; mso-ascii-font-family: 新宋体; mso-bidi-font-size: 10.5pt; mso-font-kerning: 0pt; mso-no-proof: yes"><font face="Times New Roman">”</font></span></chmetcnv>).

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<scripttype="text/javascript">
2<!--
3document.getElementById('B').onblur=onblurB;
4//-->
5</script>
6
7<scriptlanguage='javascript'>
8functiononblurB()
9{
10compute1();
11compute2();
12compute1();
13}
14</script>
15

(代码二)

这里算法比较复杂, 下面举两个例子描述一下, 请先看一个运行的示例界面:

<shape id="_x0000_i1032" style="WIDTH: 263.25pt; HEIGHT: 102pt" type="#_x0000_t75"><imagedata o:title="" src="file:///D:%5CDOCUME~1%5CZHENGJ~1%5CLOCALS~1%5CTemp%5Cmsohtml1%5C01%5Cclip_image012.png"></imagedata></shape>

其中左边五个TextBoxID从上到下依次为: 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的值变了, 则会影响到BC框的值,那么就要重新计算BC的值, 必须执行: ComputeBComputeC.

* 依次检查刚刚修改过值的BC. B修改之后, 检查一下右边的表达式, 发现没有包括B的表达式. 则这个B点往下分支循环结束; 检查一下C, 会发现A的表达式中, 应该执行A表达式, 这里有条约束, 由于是A触发的onblur事件(A开始展开循环执行), 不能再继续修改A的值, 也中断循环. 整个循环结束.

总结以上执行过程, 是这样的:

<shape id="_x0000_i1031" style="WIDTH: 401.25pt; HEIGHT: 324.75pt" type="#_x0000_t75"><imagedata o:title="" src="file:///D:%5CDOCUME~1%5CZHENGJ~1%5CLOCALS~1%5CTemp%5Cmsohtml1%5C01%5Cclip_image014.emz"></imagedata></shape>

2. 再举个有些复杂的执行情况, 当输入D框值后执行的步骤.

<shape id="_x0000_i1030" style="WIDTH: 263.25pt; HEIGHT: 102pt" type="#_x0000_t75"><imagedata o:title="" src="file:///D:%5CDOCUME~1%5CZHENGJ~1%5CLOCALS~1%5CTemp%5Cmsohtml1%5C01%5Cclip_image012.png"></imagedata></shape>

* D控件输入值, 并且让D控件失去焦点

* 看右边表达式, 如果D值改变会影响到控件A和控件C的值,

则要执行ComputeA()ComputeC()方法, 重新计算AC的值.

* 由于A的值和C的值修改了, 则要继续判断AC影响到哪些控件

的值. 其中, A影响到BC; C影响到A.

AC共影响到A,B,C结点的值, 依次执行 ComputeA(), ComputeB(), ComputeC()方法.

* 再继续判断刚刚值改变的A, B, C的值, 看看 A, B, C分别都会影响

到哪些值改变, 一直遵循这样的规律判断 … … .

会发现它一直这样循环下去, 无终结.

总结用图来直观地看一下以上执行过程, 是这样执行的:

<shape id="_x0000_i1029" style="WIDTH: 415.5pt; HEIGHT: 282pt" type="#_x0000_t75"><imagedata o:title="" src="file:///D:%5CDOCUME~1%5CZHENGJ~1%5CLOCALS~1%5CTemp%5Cmsohtml1%5C01%5Cclip_image016.emz"></imagedata></shape>

为了不让它们限入死循环, 我们限定循环的终止条件有三个:

1. 正确结束: 当前结点值改变后不会影响其它任何结点的值改变.

2. 触发起始结点结束: 假如是从A开始的, 即先执行Aonblur(鼠标失去焦点,然后导致触发ComputeA)事件, 则当其它结点再影响到A, 则中断执行.

3. 深度超过3层则强制循环结束.

这里分两种情况:

a) 用户输入的一系列表达式之间是不符合正确逻辑的, :

{

A := B + 1

B := A + 1

}

显然程序执行时会使AB不断依次加1, 限入无限死循环

.

b) 用户输入一系列表达式之间是符合正确逻辑的.

有时候也会限入死循环, 但当循环到一定次数时, 由于数据是符合正确逻辑的, 虽然是产生死循环, 但数据是正确的.:

{

A := B + 1

B := A - 1

}

这样AB会互相影响, 显然会产生死循环, 依次执行:

ComputeA();

ComputeB();

ComputeA();

ComputeB();

ComputeA();

ComputeB();

… …

… …

但这时数值不会计算错误, 举个实例, 假如在B控件中

输入: 5, 则会导致A变为colo

分享到:
评论

相关推荐

Global site tag (gtag.js) - Google Analytics