使用Atlas创建自己的Client Control
2006-10-04 22:54 Jeffrey Zhao 阅读(2851) 评论(24) 编辑 收藏 举报
Atlas客户端脚本提供了数个继承于Sys.UI.Control的类,从简单如Sys.UI.Button,到复杂如Sys.UI.Data.ListView,在一定程度上方便了开发人员,另外可以使用Declarative Syntax也可谓一大进步。但是一般仅仅使用Atlas提供的那些类是远远不够的,开发人员必须自行使用Atlas进行扩展,并且将自己的扩展融入到Atlas模型中去。
一般来说:使用Client Script开发Atals的Control,需要进行以下几步:
1、编写一个类,并继承于Sys.UI.Control或其子类。
2、重载getDescriptor方法提供对于类成员的描述。
3、重载initialize方法添加初始化代码。
4、重载dispose方法添加销毁该控件时代码。
5、添加该类对于Declarative Syntax的支持。
这里以一个Culture Selector的开发作为例子,直观和详细地说明上述这些步骤,也能提到编写时需要注意的一些细节。
Culture Selector控件是一个封装了<select />的Atlas控件,用于切换当前页面的Culture环境。我之前写过了一篇文章《本地化与Atlas对于本地化的支持》比较详细地分析了Atlas中Culture相关问题。Culture Selector将利用这个Atlas的Culture支持实现Culture切换。当然,它还相当的不成熟,只是一个为了说明问题而作的演示。
我们一步一步地来实现这个控件:
1、编写一个类,并继承于Sys.UI.Control或其子类。
Jeffz.UI.CultureSelector类的代码框架如下:
作为演示,在这里Culture Selector直接继承于Control控件,而不是继承于Sys.UI.Select控件,以避免Select纷繁的现有成员。上面定义了一个RW属性(culture),一个只读属性(isLoading)和一个事件(culturechanging),并且填写了Atlas中继承的基础代码。这样的框架存在于所有Sys.UI.Control之类中。其中属性的写法,事件的写法等等,都是Atlas的规范。如果违反了这种规范,运行就可能不正确。
在定义属性时,需要注意可能当时还没有执行initialize方法,需要“特别对待”(16-20行)。另外,为了支持binding,在合适的时候需要调用this.raisePropertyChanged方法,这一点,在编写整个类的时候都需要注意。
另外,在cultureChanging事件里使用了CancelEventArgs,这样可以响应这个事件并取消Culture改变。
2、重载getDescriptor方法提供对于类成员的描述。
这是个相当重要的方法。在Atlas中,一个类是靠Sys.TypeDescriptor类的实例来描述自己的对外成员。Atlas在得到该实例后即可以进行一些操作,例如非常著名的“Binding”。Atlas中从一个类获得Sys.TypeDescriptor对象的方式有几种,其中对于实现了Sys.ITypeDescriptorProvider接口的类,就会调用接口中的getDescriptor方法来获得类成员的描述。Atlas中的类大都实现了这个接口,因为它在Sys.Component中就被实现了。自然,对于继承了Sys.Control的类都有这个方法,我们需要重载它来提供对于自身的描述。代码如下:
首先,调用父类的getDescriptor方法获得描述父类成员的对象。然后调用各个方法来加入类自身的描述。
Sys.TypeDescriptor添加Property描述的方法定义如下:
第一个参数为属性名,第二个为属性类型,从String,Boolean,Object,Array等javascript内定类型到自定义的类或者接口都能作为属性的类型。不同的类型会有不同的处理方式,阅读代码可以帮助我们理解更多,更好地掌握这些方法。如果该属性为只读属性,则需要将第三个参数设为true。一般来说,只需要了解这三个阐述即可。associateAttribute是可以任意附加于该属性的描述,这片文章不对其作用进行分析。
Sys.TypeDescriptor还有添加Event描述的方法,定义如下:
第一个参数为事件名,第二个参数如果设为true,则该方法还能够支持各个Action。
另外,Sys.TypeDescriptor也有addMethod和addAttribute方法,它们和addEvent一样都是非常简单的方法,大家可以通过阅读Atlas代码来知道它们的定义和使用方式。
3、重载initialize方法添加初始化代码。
一个Control必须经过初始化工作才能使用,因此,最后必须调用它的initialize方法才能生效。自然,重载initialize方法也是编写一个control的基本要素之一。这个方法代码如下:
首先需要调用父类的initialize方法进行初始化,然后再执行自己的代码。在初始化代码中往往会处理控件状态(5-17行),attach控件事件(19-20行),并做一些函数封装等其余工作(22到28行)。
在作函数封装时,一个最常用的方法就是Function.createDelegate。该方法会返回一个回调函数,定义如下:
如果直接将一个函数交给一些HTML控件作为事件的回调函数,在事件发生时this所引用的对象就是该控件,而不是我们自定义的对象,例如把一个函数交给window.setTimeout后,this就变成的window。Function.createDelegate则解决了这一点,一般要响应HTML控件的回调时,都使用该方法比较妥当。它保证了method参数所引用的方法被调用时,this所引用的一定是instance参数对象。
4、重载dispose方法添加销毁该控件时代码。
Sys.Component也实现了Sys.IDisposable接口。因此重载dispose方法也是销毁对象,释放资源的重要做法。代码如下:
一般来说,要做的就是detach事件,释放引用等等。记得在最后要调用父类的dispose函数。
到现在,CultureSelector类已经写得差不多了,我们先来使用一下。首先是HTML:
HTML代码
注意ScriptManager将EnableScriptGlobalization设为了false,因为Culture的选择将由我们来控制。
然后定义一些javascript函数:
javascript代码
可以看到init函数内通过javascript使用了Jeffz.UI.CultureSelector。并将一个Label的visible属性和cultureSelector的isLoading属性通过binding联系了起来。
最后是Atlas Xml Scripts:
可以点击这里查看使用效果。选择下拉框可以发现日期显示的文字被改变了,这就说明Culture Selector功能生效了。
5、添加该类对于Declarative Syntax的支持。
最后,就要添加对于Xml Script的支持了。实现这一步其实可以非常的简单,只要如下一句代码:
这样,我们就可以在代码里使用像使用<lable />一般地使用<cultureSelector />了。但是为了避免冲突,最好我们还是加上自己的prefix比较好。首先看一下Sys.TypeDescriptor.addType的定义:
于是我们只需要使用第一个参数就能够加上一个prefix:
显然,如果prefix被设为了script那么则可以省略。
现在,我们就能够通过Atlas Xml Scripts来方面地使用CultureSelector控件了。很自然,上面的init方法也就不需要了。Atlas Xml Scripts代码如下:
需要注意的是,既然使用了prefix,那么我们就需要添加一个xml namespace,如上例。这样,这个xml才能被正确地解析。现在的效果和之前javascript方法相同。
可能有人会问(比如我在刚接触Declarative Syntax时就很想知道),这个XML是如何被解析的呢?我们能否控制呢?答案是肯定的。事实上,Atlas现在是通过Sys.UI.Control.parseFromMarkup方法来解析这个xml element。Atlas在遇到代表一个类型的xml element时,会去找这个类型的“静态”方法parseFromMarkup去解析。如果没有找到,则会沿着继承树一路向上。显然,如果我们需要自定义xml解析方式的话,我们只需要定义一个为Sys.UI.CultureSelector定义一个parseFromMarkup即可。例如:
这就是相当高级的作法了,比较复杂,超出了我想在这篇文章中谈论的问题。可能我会在以后写一系列文章讨论一些深入Atlas的话题,把我研究Atlas各方面的心得和大家分享一下。感兴趣的朋友现在可以阅读一下Atlas中现有的方法实现。阅读代码能够帮助我们理解Atlas的工作方式,即使遇到了问题,往往也能很快地解决。
点击这里可以下载到这篇文章使用的例子“Culture Selector”以及使用代码。
点击这里可以查看例子的使用效果。
一般来说:使用Client Script开发Atals的Control,需要进行以下几步:
1、编写一个类,并继承于Sys.UI.Control或其子类。
2、重载getDescriptor方法提供对于类成员的描述。
3、重载initialize方法添加初始化代码。
4、重载dispose方法添加销毁该控件时代码。
5、添加该类对于Declarative Syntax的支持。
这里以一个Culture Selector的开发作为例子,直观和详细地说明上述这些步骤,也能提到编写时需要注意的一些细节。
Culture Selector控件是一个封装了<select />的Atlas控件,用于切换当前页面的Culture环境。我之前写过了一篇文章《本地化与Atlas对于本地化的支持》比较详细地分析了Atlas中Culture相关问题。Culture Selector将利用这个Atlas的Culture支持实现Culture切换。当然,它还相当的不成熟,只是一个为了说明问题而作的演示。
我们一步一步地来实现这个控件:
1、编写一个类,并继承于Sys.UI.Control或其子类。
Jeffz.UI.CultureSelector类的代码框架如下:
1
Type.registerNamespace("Jeffz.UI");
2![](https://www.cnblogs.com/Images/OutliningIndicators/None.gif)
3
Jeffz.UI.CultureSelector = function(associatedElement)
4
{
5
Jeffz.UI.CultureSelector.initializeBase(this, [associatedElement]);
6![](https://www.cnblogs.com/Images/OutliningIndicators/InBlock.gif)
7
...
8![](https://www.cnblogs.com/Images/OutliningIndicators/InBlock.gif)
9
this.get_culture = function()
10
{
11
return _culture;
12
}
13
14
this.set_culture = function(value)
15
{
16
if (!this.get_isInitialized())
17
{
18
_culture = value;
19
return;
20
}
21
22
if (_culture == value)
23
{
24
return;
25
}
26
27
var cancelArgs = new Sys.CultureCancelEventArgs(value);
28
this.cultureChanging.invoke(this, cancelArgs);
29
canceled = cancelArgs.get_canceled();
30
31
if (!canceled)
32
{
33
_culture = value;
34
this.__updateCulture();
35
this.raisePropertyChanged('culture');
36
}
37
38
this.__changeSelectedOption(_culture);
39
}
40
41
this.get_isLoading = function()
42
{
43
return _isLoading
44
}
45
46
this.cultureChanging = new Type.Event(this);
47
48
...
49
}
50
Jeffz.UI.CultureSelector.registerClass('Jeffz.UI.CultureSelector', Sys.UI.Control);
![](https://www.cnblogs.com/Images/OutliningIndicators/None.gif)
2
![](https://www.cnblogs.com/Images/OutliningIndicators/None.gif)
3
![](https://www.cnblogs.com/Images/OutliningIndicators/None.gif)
4
![](https://www.cnblogs.com/Images/OutliningIndicators/ExpandedBlockStart.gif)
5
![](https://www.cnblogs.com/Images/OutliningIndicators/InBlock.gif)
6
![](https://www.cnblogs.com/Images/OutliningIndicators/InBlock.gif)
7
![](https://www.cnblogs.com/Images/OutliningIndicators/InBlock.gif)
8
![](https://www.cnblogs.com/Images/OutliningIndicators/InBlock.gif)
9
![](https://www.cnblogs.com/Images/OutliningIndicators/InBlock.gif)
10
![](https://www.cnblogs.com/Images/OutliningIndicators/ExpandedSubBlockStart.gif)
11
![](https://www.cnblogs.com/Images/OutliningIndicators/InBlock.gif)
12
![](https://www.cnblogs.com/Images/OutliningIndicators/ExpandedSubBlockEnd.gif)
13
![](https://www.cnblogs.com/Images/OutliningIndicators/InBlock.gif)
14
![](https://www.cnblogs.com/Images/OutliningIndicators/InBlock.gif)
15
![](https://www.cnblogs.com/Images/OutliningIndicators/ExpandedSubBlockStart.gif)
16
![](https://www.cnblogs.com/Images/OutliningIndicators/InBlock.gif)
17
![](https://www.cnblogs.com/Images/OutliningIndicators/ExpandedSubBlockStart.gif)
18
![](https://www.cnblogs.com/Images/OutliningIndicators/InBlock.gif)
19
![](https://www.cnblogs.com/Images/OutliningIndicators/InBlock.gif)
20
![](https://www.cnblogs.com/Images/OutliningIndicators/ExpandedSubBlockEnd.gif)
21
![](https://www.cnblogs.com/Images/OutliningIndicators/InBlock.gif)
22
![](https://www.cnblogs.com/Images/OutliningIndicators/InBlock.gif)
23
![](https://www.cnblogs.com/Images/OutliningIndicators/ExpandedSubBlockStart.gif)
24
![](https://www.cnblogs.com/Images/OutliningIndicators/InBlock.gif)
25
![](https://www.cnblogs.com/Images/OutliningIndicators/ExpandedSubBlockEnd.gif)
26
![](https://www.cnblogs.com/Images/OutliningIndicators/InBlock.gif)
27
![](https://www.cnblogs.com/Images/OutliningIndicators/InBlock.gif)
28
![](https://www.cnblogs.com/Images/OutliningIndicators/InBlock.gif)
29
![](https://www.cnblogs.com/Images/OutliningIndicators/InBlock.gif)
30
![](https://www.cnblogs.com/Images/OutliningIndicators/InBlock.gif)
31
![](https://www.cnblogs.com/Images/OutliningIndicators/InBlock.gif)
32
![](https://www.cnblogs.com/Images/OutliningIndicators/ExpandedSubBlockStart.gif)
33
![](https://www.cnblogs.com/Images/OutliningIndicators/InBlock.gif)
34
![](https://www.cnblogs.com/Images/OutliningIndicators/InBlock.gif)
35
![](https://www.cnblogs.com/Images/OutliningIndicators/InBlock.gif)
36
![](https://www.cnblogs.com/Images/OutliningIndicators/ExpandedSubBlockEnd.gif)
37
![](https://www.cnblogs.com/Images/OutliningIndicators/InBlock.gif)
38
![](https://www.cnblogs.com/Images/OutliningIndicators/InBlock.gif)
39
![](https://www.cnblogs.com/Images/OutliningIndicators/ExpandedSubBlockEnd.gif)
40
![](https://www.cnblogs.com/Images/OutliningIndicators/InBlock.gif)
41
![](https://www.cnblogs.com/Images/OutliningIndicators/InBlock.gif)
42
![](https://www.cnblogs.com/Images/OutliningIndicators/ExpandedSubBlockStart.gif)
43
![](https://www.cnblogs.com/Images/OutliningIndicators/InBlock.gif)
44
![](https://www.cnblogs.com/Images/OutliningIndicators/ExpandedSubBlockEnd.gif)
45
![](https://www.cnblogs.com/Images/OutliningIndicators/InBlock.gif)
46
![](https://www.cnblogs.com/Images/OutliningIndicators/InBlock.gif)
47
![](https://www.cnblogs.com/Images/OutliningIndicators/InBlock.gif)
48
![](https://www.cnblogs.com/Images/OutliningIndicators/InBlock.gif)
49
![](https://www.cnblogs.com/Images/OutliningIndicators/ExpandedBlockEnd.gif)
50
![](https://www.cnblogs.com/Images/OutliningIndicators/None.gif)
作为演示,在这里Culture Selector直接继承于Control控件,而不是继承于Sys.UI.Select控件,以避免Select纷繁的现有成员。上面定义了一个RW属性(culture),一个只读属性(isLoading)和一个事件(culturechanging),并且填写了Atlas中继承的基础代码。这样的框架存在于所有Sys.UI.Control之类中。其中属性的写法,事件的写法等等,都是Atlas的规范。如果违反了这种规范,运行就可能不正确。
在定义属性时,需要注意可能当时还没有执行initialize方法,需要“特别对待”(16-20行)。另外,为了支持binding,在合适的时候需要调用this.raisePropertyChanged方法,这一点,在编写整个类的时候都需要注意。
另外,在cultureChanging事件里使用了CancelEventArgs,这样可以响应这个事件并取消Culture改变。
2、重载getDescriptor方法提供对于类成员的描述。
这是个相当重要的方法。在Atlas中,一个类是靠Sys.TypeDescriptor类的实例来描述自己的对外成员。Atlas在得到该实例后即可以进行一些操作,例如非常著名的“Binding”。Atlas中从一个类获得Sys.TypeDescriptor对象的方式有几种,其中对于实现了Sys.ITypeDescriptorProvider接口的类,就会调用接口中的getDescriptor方法来获得类成员的描述。Atlas中的类大都实现了这个接口,因为它在Sys.Component中就被实现了。自然,对于继承了Sys.Control的类都有这个方法,我们需要重载它来提供对于自身的描述。代码如下:
this.getDescriptor = function()
{
var td = Jeffz.UI.CultureSelector.callBaseMethod(this, 'getDescriptor');
td.addProperty("culture", String);
td.addProperty("isLoading", Boolean, true);
td.addEvent("cultureChanging", true);
return td;
}
{
var td = Jeffz.UI.CultureSelector.callBaseMethod(this, 'getDescriptor');
td.addProperty("culture", String);
td.addProperty("isLoading", Boolean, true);
td.addEvent("cultureChanging", true);
return td;
}
首先,调用父类的getDescriptor方法获得描述父类成员的对象。然后调用各个方法来加入类自身的描述。
Sys.TypeDescriptor添加Property描述的方法定义如下:
Sys.TypeDescriptor.addProperty(propertyName, type, isReadonly, associateAttribute1, associateAtribute2...);
第一个参数为属性名,第二个为属性类型,从String,Boolean,Object,Array等javascript内定类型到自定义的类或者接口都能作为属性的类型。不同的类型会有不同的处理方式,阅读代码可以帮助我们理解更多,更好地掌握这些方法。如果该属性为只读属性,则需要将第三个参数设为true。一般来说,只需要了解这三个阐述即可。associateAttribute是可以任意附加于该属性的描述,这片文章不对其作用进行分析。
Sys.TypeDescriptor还有添加Event描述的方法,定义如下:
Sys.TypeDescriptor.addEvent(eventName, supportsActions)
第一个参数为事件名,第二个参数如果设为true,则该方法还能够支持各个Action。
另外,Sys.TypeDescriptor也有addMethod和addAttribute方法,它们和addEvent一样都是非常简单的方法,大家可以通过阅读Atlas代码来知道它们的定义和使用方式。
3、重载initialize方法添加初始化代码。
一个Control必须经过初始化工作才能使用,因此,最后必须调用它的initialize方法才能生效。自然,重载initialize方法也是编写一个control的基本要素之一。这个方法代码如下:
1
this.initialize = function()
2
{
3
Jeffz.UI.CultureSelector.callBaseMethod(this, 'initialize');
4![](https://www.cnblogs.com/Images/OutliningIndicators/InBlock.gif)
5
while (this.element.options.length > 0)
6
{
7
this.element.options.remove(0);
8
}
9
10
var cultures = ["en-US", "zh-CN", "fr-FR", "ko-KR"];
11
for (var i = 0; i < cultures.length; i++)
12
{
13
var option = document.createElement("option");
14
option.text = cultures[i];
15
option.value = cultures[i];
16
this.element.options.add(option);
17
}
18
19
_selectionChangedHandler = Function.createDelegate(this, this.__onSelectionChanged);
20
this.element.attachEvent('onchange', _selectionChangedHandler);
21
22
_loadCompleteHander = Function.createDelegate(this, this.__loadComplete);
23
24
if (_culture.toLowerCase() != Sys.CultureInfo.Name.toLowerCase())
25
{
26
this.__updateCulture();
27
this.__changeSelectedOption(_culture);
28
}
29
}
30
Jeffz.UI.CultureSelector.registerBaseMethod(this, 'initialize');
![](https://www.cnblogs.com/Images/OutliningIndicators/None.gif)
2
![](https://www.cnblogs.com/Images/OutliningIndicators/ExpandedBlockStart.gif)
3
![](https://www.cnblogs.com/Images/OutliningIndicators/InBlock.gif)
4
![](https://www.cnblogs.com/Images/OutliningIndicators/InBlock.gif)
5
![](https://www.cnblogs.com/Images/OutliningIndicators/InBlock.gif)
6
![](https://www.cnblogs.com/Images/OutliningIndicators/ExpandedSubBlockStart.gif)
7
![](https://www.cnblogs.com/Images/OutliningIndicators/InBlock.gif)
8
![](https://www.cnblogs.com/Images/OutliningIndicators/ExpandedSubBlockEnd.gif)
9
![](https://www.cnblogs.com/Images/OutliningIndicators/InBlock.gif)
10
![](https://www.cnblogs.com/Images/OutliningIndicators/InBlock.gif)
11
![](https://www.cnblogs.com/Images/OutliningIndicators/InBlock.gif)
12
![](https://www.cnblogs.com/Images/OutliningIndicators/ExpandedSubBlockStart.gif)
13
![](https://www.cnblogs.com/Images/OutliningIndicators/InBlock.gif)
14
![](https://www.cnblogs.com/Images/OutliningIndicators/InBlock.gif)
15
![](https://www.cnblogs.com/Images/OutliningIndicators/InBlock.gif)
16
![](https://www.cnblogs.com/Images/OutliningIndicators/InBlock.gif)
17
![](https://www.cnblogs.com/Images/OutliningIndicators/ExpandedSubBlockEnd.gif)
18
![](https://www.cnblogs.com/Images/OutliningIndicators/InBlock.gif)
19
![](https://www.cnblogs.com/Images/OutliningIndicators/InBlock.gif)
20
![](https://www.cnblogs.com/Images/OutliningIndicators/InBlock.gif)
21
![](https://www.cnblogs.com/Images/OutliningIndicators/InBlock.gif)
22
![](https://www.cnblogs.com/Images/OutliningIndicators/InBlock.gif)
23
![](https://www.cnblogs.com/Images/OutliningIndicators/InBlock.gif)
24
![](https://www.cnblogs.com/Images/OutliningIndicators/InBlock.gif)
25
![](https://www.cnblogs.com/Images/OutliningIndicators/ExpandedSubBlockStart.gif)
26
![](https://www.cnblogs.com/Images/OutliningIndicators/InBlock.gif)
27
![](https://www.cnblogs.com/Images/OutliningIndicators/InBlock.gif)
28
![](https://www.cnblogs.com/Images/OutliningIndicators/ExpandedSubBlockEnd.gif)
29
![](https://www.cnblogs.com/Images/OutliningIndicators/ExpandedBlockEnd.gif)
30
![](https://www.cnblogs.com/Images/OutliningIndicators/None.gif)
首先需要调用父类的initialize方法进行初始化,然后再执行自己的代码。在初始化代码中往往会处理控件状态(5-17行),attach控件事件(19-20行),并做一些函数封装等其余工作(22到28行)。
在作函数封装时,一个最常用的方法就是Function.createDelegate。该方法会返回一个回调函数,定义如下:
Function.createDelegate(instance, method)
如果直接将一个函数交给一些HTML控件作为事件的回调函数,在事件发生时this所引用的对象就是该控件,而不是我们自定义的对象,例如把一个函数交给window.setTimeout后,this就变成的window。Function.createDelegate则解决了这一点,一般要响应HTML控件的回调时,都使用该方法比较妥当。它保证了method参数所引用的方法被调用时,this所引用的一定是instance参数对象。
4、重载dispose方法添加销毁该控件时代码。
Sys.Component也实现了Sys.IDisposable接口。因此重载dispose方法也是销毁对象,释放资源的重要做法。代码如下:
this.dispose = function()
{
if (_selectionChangedHandler)
{
this.element.detachEvent('onchange', _selectionChangedHandler);
_selectionChangedHandler = null;
_loadCompleteHander = null;
}
Jeffz.UI.CultureSelector.callBaseMethod(this, 'dispose');
}
Jeffz.UI.CultureSelector.registerBaseMethod(this, 'dispose');
{
if (_selectionChangedHandler)
{
this.element.detachEvent('onchange', _selectionChangedHandler);
_selectionChangedHandler = null;
_loadCompleteHander = null;
}
Jeffz.UI.CultureSelector.callBaseMethod(this, 'dispose');
}
Jeffz.UI.CultureSelector.registerBaseMethod(this, 'dispose');
一般来说,要做的就是detach事件,释放引用等等。记得在最后要调用父类的dispose函数。
到现在,CultureSelector类已经写得差不多了,我们先来使用一下。首先是HTML:
![](https://www.cnblogs.com/Images/OutliningIndicators/ContractedBlock.gif)
注意ScriptManager将EnableScriptGlobalization设为了false,因为Culture的选择将由我们来控制。
然后定义一些javascript函数:
![](https://www.cnblogs.com/Images/OutliningIndicators/ContractedBlock.gif)
可以看到init函数内通过javascript使用了Jeffz.UI.CultureSelector。并将一个Label的visible属性和cultureSelector的isLoading属性通过binding联系了起来。
最后是Atlas Xml Scripts:
<script type="text/xml-script">
<page xmlns:jeffz="http://www.jeffzlive.net">
<components>
<application load="init" />
<timer interval="1000" tick="onTick" enabled="true" />
</components>
</page>
</script>
<page xmlns:jeffz="http://www.jeffzlive.net">
<components>
<application load="init" />
<timer interval="1000" tick="onTick" enabled="true" />
</components>
</page>
</script>
可以点击这里查看使用效果。选择下拉框可以发现日期显示的文字被改变了,这就说明Culture Selector功能生效了。
5、添加该类对于Declarative Syntax的支持。
最后,就要添加对于Xml Script的支持了。实现这一步其实可以非常的简单,只要如下一句代码:
Sys.TypeDescriptor.addType('script', 'cultureSelector', Jeffz.UI.CultureSelector);
这样,我们就可以在代码里使用像使用<lable />一般地使用<cultureSelector />了。但是为了避免冲突,最好我们还是加上自己的prefix比较好。首先看一下Sys.TypeDescriptor.addType的定义:
Sys.TypeDescriptor.addType(tagPrefix, tagName, type);
于是我们只需要使用第一个参数就能够加上一个prefix:
Sys.TypeDescriptor.addType('jeffz', 'cultureSelector', Jeffz.UI.CultureSelector);
显然,如果prefix被设为了script那么则可以省略。
现在,我们就能够通过Atlas Xml Scripts来方面地使用CultureSelector控件了。很自然,上面的init方法也就不需要了。Atlas Xml Scripts代码如下:
<script type="text/xml-script">
<page xmlns:jeffz="http://www.jeffzlive.net">
<components>
<jeffz:cultureSelector id="cultureSelector" cultureChanging="changing" />
<timer interval="1000" tick="onTick" enabled="true" />
<label id="loading" visibilityMode="Collapse">
<bindings>
<binding dataContext="cultureSelector" dataPath="isLoading" property="visible" />
</bindings>
</label>
</components>
</page>
</script>
<page xmlns:jeffz="http://www.jeffzlive.net">
<components>
<jeffz:cultureSelector id="cultureSelector" cultureChanging="changing" />
<timer interval="1000" tick="onTick" enabled="true" />
<label id="loading" visibilityMode="Collapse">
<bindings>
<binding dataContext="cultureSelector" dataPath="isLoading" property="visible" />
</bindings>
</label>
</components>
</page>
</script>
需要注意的是,既然使用了prefix,那么我们就需要添加一个xml namespace,如上例。这样,这个xml才能被正确地解析。现在的效果和之前javascript方法相同。
可能有人会问(比如我在刚接触Declarative Syntax时就很想知道),这个XML是如何被解析的呢?我们能否控制呢?答案是肯定的。事实上,Atlas现在是通过Sys.UI.Control.parseFromMarkup方法来解析这个xml element。Atlas在遇到代表一个类型的xml element时,会去找这个类型的“静态”方法parseFromMarkup去解析。如果没有找到,则会沿着继承树一路向上。显然,如果我们需要自定义xml解析方式的话,我们只需要定义一个为Sys.UI.CultureSelector定义一个parseFromMarkup即可。例如:
Sys.UI.CultureSelector.parseFromMarkup = function(type, node, markupContext)
{
...
}
{
...
}
这就是相当高级的作法了,比较复杂,超出了我想在这篇文章中谈论的问题。可能我会在以后写一系列文章讨论一些深入Atlas的话题,把我研究Atlas各方面的心得和大家分享一下。感兴趣的朋友现在可以阅读一下Atlas中现有的方法实现。阅读代码能够帮助我们理解Atlas的工作方式,即使遇到了问题,往往也能很快地解决。
点击这里可以下载到这篇文章使用的例子“Culture Selector”以及使用代码。
点击这里可以查看例子的使用效果。