在上一次课程中,Component的成员还有四个没有涉及到。
beginUpdate方法:开始更新控件的属性,有点类似与线程锁一样,为了保证控件属性值的一致性 isUpdating只读属性:判断控件是否在更新中 endUpdate方法:完成更新控件的属性,类似与释放线程锁。 updated方法:提交组件的更新信息。
这里涉及到控件的Update状态。表示组件正在更新状态, 处于更新状态时组件的数据可能处于不一致的状态, 处于更新状态的组件,应该尽量避免与外界交互(尤其是与DOM元素有关的交互)。 有时候,合理利用Update状态也能够在一定程度上提高性能
如何使用Update状态
批量修改组件属性(在非Update状态下) - 调用组件的beginUpdate方法 - 设置组件属性 - 调用组件的endUpdate方法
在调用endUpdate方法后,组件会自动调用Updated方法。
下面是一个timer的例子,具体的注释已经被添加。
//
JScript 文件
Type.registerNamespace(
"
Demo
"
);
//
构造函数
Demo.Timer
=
function
()
...
{ // 调用基类的构造函数 Demo.Timer.initializeBase( this ); this ._interval = 1000 ; this ._enable = false ; this ._timer = null ; }
Demo.Timer.prototype
=
...
{ // 组件的tick事件 add_tick : function (handler) ... { this .get_events().addHandler( " tick " , handler); } , remove_tick : function (handler) ... { this .get_events().removeHandler( " tick " , handler); } , _timerCallback : function () ... { // 得到组件tick事件的事件处理器 var handler = this .get_events().getHandler( " tick " ); if (handler) ... { // 调用tick的事件处理器,并给空参数 handler( this , Sys.EventArgs.Empty); } }, _startTimer : function () ... { this ._timer = window.setInterval( // 创建一个timerCallback的委托, 通过这个委托调用组件的tick事件处理器 Function.createDelegate( this , this ._timerCallback), this ._interval ); } , _stopTimer : function () ... { window.clearInterval( this ._timer); this ._timer = null ; } , get_interval : function () ... { return this ._interval; } , // 这个属性的改变,会导致DOM元素在页面的表现形式 set_interval : function (value) ... { if ( this ._interval != value) ... { this ._interval = value; this .raisePropertyChanged( " interval " ); // 判断组件当前是否在Update状态中,如果在就不做操作 if ( ! this .get_isUpdating() && this ._timer != null ) ... { this ._stopTimer(); this ._startTimer(); } } }, // 当endUpdate()函数被调用后,此方法被自动调用 updated : function () ... { Demo.Timer.callBaseMethod( this , " updated " ); this ._stopTimer(); if ( this ._enabled) ... { this ._startTimer(); } }, get_enabled : function () ... { return this ._enabled; } , set_enabled : function (value) ... { if (value != this ._enabled) ... { this ._enabled = value; this .raisePropertyChanged( " enabled " ); if ( ! this .get_isUpdating()) ... { if (value) ... { this ._startTimer(); } else ... { this ._stopTimer(); } } } }, dispose : function () ... { this .set_enabled( false ); this ._stopTimer(); Demo.Timer.callBaseMethod( this , " dispose " ); } }
Demo.Timer.registerClass(
"
Demo.Timer
"
, Sys.Component);
调用方面主要提供修改组件属性时的样例
function
changeStatus()
...
{ var timer = $find( " timer " ); timer.beginUpdate(); timer.set_interval(parseInt($get( " txtInterval " ).value, 10 )); timer.set_enabled($get( " chkEnabled " ).checked); timer.endUpdate(); }
可以看到,我们在修改组件的属性时,首先beginUpdate,然后修改组件的属性,最后调用组件的endUpdate,在调用endUpdate之后,会自动调用组件的updated方法
Control模型(可视化组件模型)
封装一个DOM元素 提供统一的开发模型 可以用于开发复杂组件 构造函数接受一个element参数,表示这个组件封装的DOM元素
Sys.UI.Control类的成员
element只读属性 Control类封装的DOM元素 visibilityMode属性 显示模式属性,隐藏,相当于操作style.visibilityhide: 0,这相当与在页面上面隐藏,但是占位,收缩,相当于操作style.displaycollapse: 1,在页面上隐藏,但是不占位 visible 是否可视 addCssClass/removeCssClass/toggleCssClass 添加/移除/翻转Css样式表 parent 当前组件的容器元素 onBubbleEvent 处理由raiseBubbleEvent激发的事件。如果这个方法返回true,则该事件将不会被上传到其父元素中进行处理。注意,如果你不处理该事件的话,你应该把此事件交由父级来作默认处理。 raiseBubbleEvent 引发一个事件并交由父控件处理。总的来看,当你创建复杂的控件(经常是包含一个或多个子控件并且想把一个事件从子控件上交由其父控件来处理时)时往往要使用onBubbleEvent与这个方法。
下面来看一个简单的例子
首先是组件的组件的js文件
//
JScript 文件
Type.registerNamespace(
"
Demo
"
);
//
构造函数
Demo.TextBox
=
function
(element)
...
{ // 调用基类的构造函数,并且把一个DOM元素作为参数向上传递 Demo.TextBox.initializeBase( this , [element]); // 保存TextBox修改前的文本 this ._originalText = "" ; }
Demo.TextBox.prototype
=
...
{ initialize : function () ... { Demo.TextBox.callBaseMethod( this , " initialize " ); // 在组件初始化时将文本框的change事件绑定一个事件处理器_onTextChange // 并且把当前组件作为this上下文传递到_onTextChange函数中 $addHandler( this .get_element(), " change " , Function.createDelegate( this , this ._onTextChange)); } , _onTextChange : function (e) ... { // 为了避免在将当前属性值(文本)修改回去造成递归调用, // 判断组件当前的更新状态. if ( this .get_isUpdating()) ... { return ; } // 得到组件的textChange事件的处理器 var handler = this .get_events().getHandler( " textChange " ); if (handler) ... { var args = new Sys.CancelEventArgs(); // 调用事件处理器,并把当前组件作为sender和CancelEventArgs类型传递到处理函数中 handler( this , args); // 得到由外部处理的args参数,并依据此来判断 if (args.get_cancel()) ... { // 更新当前组件的属性,间接的更新textbox this .beginUpdate(); this .set_text( this ._originalText); this .endUpdate(); } } this ._originalText = this .get_element().value; } , // 给组件添加一个textChange事件处理器 add_textChange : function (handler) ... { this .get_events().addHandler( " textChange " , handler); } , remove_textChange : function (handler) ... { this .get_events().removeHandler( " textChange " , handler); } , get_text : function () ... { return this .get_element().value; } , set_text : function (value) ... { this .get_element().value = value; } }
Demo.TextBox.registerClass(
"
Demo.TextBox
"
, Sys.UI.Control);
再来看如何调用
<
script language
=
"
javascript
"
type
=
"
text/javascript
"
>
Sys.Application.add_init(
function
()
...
{ // 创建当前TextBox组件,并给textChange事件添加onTextChange事件处理器 // 并且把DOM元素textBox作为参数传递给构造函数 $create(Demo.TextBox, null , ... { " textChange " : onTextChange} , null , $get( " textBox " )); }
);
function
onTextChange(sender, args)
...
{ var message = String.format( " The text would be changed to '{0}', are you sure? " , sender.get_text()); // 根据用户确定的消息来设置args的cancel属性 if ( ! confirm(message)) ... { args.set_cancel( true ); } }
</
script
>