Developing a scriptable Web ADF control
开发一个能应用脚本的WebADF控件
A key consideration when designing a custom(定制的) web control is where the control抯 logic will execute. While all the logic could be put in either the client or the web tier(层,等级), the best approach(途径) is usually to place some logic in each tier. User interface data and operations should usually be situated in the client tier, while data retrieval(取回,回收) and heavy computational logic should (and often must) reside in the web tier. Distributing(分发,分配) the control in this way maximizes the use of client resources and reduces server load. Additionally, in order to minimize network traffic, client tier functionality should initiate server tier logic only when necessary (e.g. to query a database housed in the web tier). Adhering(浮着,粘着,坚守) to this architecture(风格) enables the development of controls with rich and responsive(反应快的) user interfaces。
在设计一个定制的Web Control控件的时候,一个最需要考虑的地方为,在什么地方这个控件逻辑上是运行的。应为所有的逻辑都可以被放置到客户端或者Web层,因此最有的途径就是在每一个WEB层放置一些Logic。 用户接口数据和操作通常都应该处于客户层,而数据回收以及重计算逻辑短应该放置到Web阶层。使用这样方式来分配控件可以最大化的利用客户端资源以及减轻服务器的负担。另外,为了最大限度的减小网络的拥挤,客户端只能在需要的情况下初始化服务器层逻辑。坚持使用这种风格能够让你开发除丰富、反应迅速的用户接口。
Given the advantages of this architecture, the question becomes one of implementation. How can we efficiently create an implementation that is intuitive(直觉的), maintainable, extensible(可扩展的), and re-distributable? One effective approach is to create a scriptable ASP.NET server control that inherits from the Web ADF WebControl. Scriptable server controls offer a framework for combining server and client logic into one easily re-distributable control. This framework allows for the partitioning(分开、分割) of client and server logic as described above while keeping that logic housed in one control and adherent(信徒、用户) to object-oriented programming standards. Furthermore, inheriting from the Web ADF WebControl will automatically package the Microsoft ASP.NET AJAX and Web ADF JavaScript Libraries with the control, making these available to client tier operations.
知道了这种开发风格的优越性后,如何实施这样的风格就成为了一个问题。我们如何高效的创建一个直观的、可维护的、可扩展的以及可重复使用的控件呢。一个有效的途径就是创建一个继承与Web ADF 网络控件的支持脚本的ASP.NET 服务器端控件,支持脚本的的服务器端控件提供了一个框架用来合并服务器和客户端逻辑为一个容易被重复使用的控件。这个框架就像上面描述的那样当在一个控件上保持这种逻辑并拥护基于对象的编程,允许客户端和服务器端逻辑上的分割。 此外,应为继承与Web ADF WebControl因此能够很自然的将Microsoft ASP.NET AJAX and Web ADF JavaScript库包含进来,让客户端层的操作成为现实。
This is the approach used by developers of the Web ADF in implementing web controls, and an example of its use is presented below. The walkthrough illustrates implementing a MapCoordinateDisplay control, which displays the current position of the mouse cursor over a buddied Map control.
这是Web ADF开发者在实现Web 控件的时候采用的一种途径,一个关于她的应用的例子就在下面。后面的说明介绍了一个地图坐标现实控件,她现实了当鼠标在在一个绑定的地图控件上面的时候现实光标的坐标。
>>>Create a new ASP.Net Server Control project in Visual Studio 2008 or a new Web Control Library?in Visual Studio 2005.
在VS2008里面创建一个ASP.NET服务器端控件工程,或者在VS2005里面创建一个Web 控件库。
>>>2 Add references to the ESRI.ArcGIS.ADF.Web.UI.WebControls.dll
System.Web.Extensions.dll?and AjaxControlToolkit?assemblies.
添加引用:ESRI.ArcGIS.ADF.Web.UI.WebControls.dll,System.Web.Extensions.dll引用,以及AjaxControlToolkit组件。
>>>Rename the default name of the Server control (ServerControl1) to the more intuitive(直观的) MapCoordinateDisplay and derive the class from the base ADF web control class. Make sure to change the name in the ToolboxData attribute as well as the class declaration.
重新将Server控件的命名为一个更加直观的地图坐标现实名称,并继承于Base ADF 网络控件类。我们需要确定的是,我们既要在ToolboxData的属性里面改正还要在类的声明里面进行改正。
C#]
namespace MapCoordinateDisplay
{
[DefaultProperty("Text")]
[ToolboxData("<{0}:MapCoordinateDisplay runat=server></{0}:MapCoordinateDisplay>")]
public class MapCoordinateDisplay : ESRI.ArcGIS.ADF.Web.UI.WebControls.WebControl
{
>>>Delete the default stub implementation of RenderContents inserted by Visual Studio.
删除有VS添加了的一些默认的声明文字。
>>>Replace the getter and setter for the Text property to use StateManager to store state.
取代Text的设置和获取属性,使用状态管理器来存储状管理这些状态信息。
[C#]
public string Text
{
get
{
return StateManager.GetProperty("text") as string;
}
set
{
StateManager.SetProperty("text", value);
}
}
>>>In order to buddy to the Map control, we add a property to store the ID of the map. We store the ID and not an actual reference to the control so that the property can be specified in markup and the control is not stored in session state. As explained in the best practices outlined(提供,外形) later in this post,storing web controls in session state causes memory leaks and is therefore bad practice.
为了和Map控件绑定,我们添加了一个属性用来保存地图控件的ID。我们保存控件的ID,而不是实际的接口,因此这个属性能够在创建的时候被指定并且控件也不用保存在Seesion里面。在session保存网络控件,会导致内存的泄露,因此会导致不要的性能表现。
/// <summary>The ID of the Map control to associate with this control.</summary>
public string Map
{
get
{
return StateManager.GetProperty("map") as string;
}
set
{
StateManager.SetProperty("map", value);
}
}
>>>Add a Label control as a member variable(变量). This will be used to display the map coordinates.
添加一个Label控件作为变量,这个将会被用来显示地图的坐标。
public class MapCoordinateDisplay : ESRI.ArcGIS.ADF.Web.UI.WebControls.WebControl
{
private Label m_displayLabel;
>>> Override the CreateChildControls method to control the creation of sub controls.
重载创建子对象的控件的方法,来控制sub控件的创建。
protected override void CreateChildControls()
{
Controls.Clear();
base.CreateChildControls();
m_displayLabel = new Label();
m_displayLabel.ID = "DisplayLabel";
m_displayLabel.Text = Text;
m_displayLabel.Font.Bold = true;
Controls.Add(m_displayLabel);
}
For the client script component
>>>For the client script component, add a new folder named "javascript" then add a new JScript file called "MapCoordinateDisplay.js". Set the file's build action to "Embedded Resource".
对于客户端的脚本组件,添加一个名称为“javascript“的组件,然后添加一个名称为”MapCoordinatDisplaye.js“的脚本文,设置文件的编译动作指向”Embedded Resource”
>>>Mark the JavaScript file as an embedded resource in the AssemblyInfo.cs file (in the Properties folder). Pay special attention to the namespace and casing of the WebResource name attribute.
在AssemblyInfo文件里面设置脚本文件为Embedded资源,对于命名空间以及WebResource Name这个属性,我们要格外的注意。
[C#]
[assembly(议会、集合): System.Web.UI.WebResource("MapCoordinateDisplay.javascript.MapCoordinateDisplay.js", "text/javascript")]
>>>To include the JavaScript auto-magically with the server control, add the following custom attribute to the class definition. Again, pay special attention to the namespace and casing of the ClientScriptResource attribute.
为了让脚本自动的添加到Server 控件,需要将下面的定制的属性添加到类的定义里面,在重申一次,对于命名空间以及客户脚本资源的属性我们应该特别的注意。
namespace MapCoordinateDisplay
{
[DefaultProperty("Text")]
[ToolboxData("<{0}:MapCoordinateDisplay runat=server></{0}:MapCoordinateDisplay>")]
[AjaxControlToolkit.ClientScriptResource("MapCoordinateDisplay.MapCoordinateDisplay",
"MapCoordinateDisplay.javascript.MapCoordinateDisplay.js")]
public class MapCoordinateDisplay : ESRI.ArcGIS.ADF.Web.UI.WebControls.WebControl
{
12>>>Add the following code in MapCoordinateDisplay.js to create a client-side representation(描写、表现) of the control. Here we use the ASP.NET AJAX framework for convenient(省力的、方便的) namespace and class registration(注册、挂号). We also give the control AJAX client control functionality by specifying inheritance from the ASP.NET AJAX client control base class (Sys.UI.Control) in the call to registerClass. Recall that, since our server control inherits from the Web ADF WebControl base class, we can safely assume that ASP.NET AJAX functions are available to be called.
[JavaScript]
/// <reference name="MicrosoftAjax.js"/>
/// <reference assembly="ESRI.ArcGIS.ADF.Web.UI.WebControls" name="ESRI.ArcGIS.ADF.Web.UI.WebControls.Runtime.JavaScript.references.js"/>
Type.registerNamespace('MapCoordinateDisplay');
MapCoordinateDisplay.MapCoordinateDisplay = function(element) {
MapCoordinateDisplay.MapCoordinateDisplay.initializeBase(this, [element]);
}
MapCoordinateDisplay.MapCoordinateDisplay.prototype = {
initialize : function() {
MapCoordinateDisplay.MapCoordinateDisplay.callBaseMethod(this, 'initialize');
// Add custom initialization here
},
dispose : function() {
//Add custom dispose actions here
MapCoordinateDisplay.MapCoordinateDisplay.callBaseMethod(this, 'dispose');
}
}
MapCoordinateDisplay.MapCoordinateDisplay.registerClass('MapCoordinateDisplay.MapCoordinateDisplay', Sys.UI.Control);
if (typeof(Sys) !== 'undefined') Sys.Application.notifyScriptLoaded();
Add a property to the component to encapsulate access to the buddied map control.
[JavaScript]
MapCoordinateDisplay.MapCoordinateDisplay = function(element) {
MapCoordinateDisplay.MapCoordinateDisplay.initializeBase(this, [element]);
this._map = null;
}
MapCoordinateDisplay.MapCoordinateDisplay.prototype = {
initialize : function() {
MapCoordinateDisplay.MapCoordinateDisplay.callBaseMethod(this, 'initialize');
// Add custom initialization here
},
get_map : function() {
return this._map;
},
set_map : function(value) {
this._map = value;
},
dispose : function() {
//Add custom dispose actions here
MapCoordinateDisplay.MapCoordinateDisplay.callBaseMethod(this, 'dispose');
}
}
Add another property to store the reference to the DOM element rendered by the label (child) control we included in the server control implementation. This element can then be manipulated using DHTML and JavaScript.
[JavaScript]
MapCoordinateDisplay.MapCoordinateDisplay = function(element) {
MapCoordinateDisplay.MapCoordinateDisplay.initializeBase(this, [element]);
this._map = null;
this._displayLabel = null;
}
MapCoordinateDisplay.MapCoordinateDisplay.prototype = {
initialize : function() {
MapCoordinateDisplay.MapCoordinateDisplay.callBaseMethod(this, 'initialize');
// Add custom initialization here
},
get_map : function() {
return this._map;
},
set_map : function(value) {
this._map = value;
},
get_displayLabel : function() {
return this._displayLabel;
},
set_displayLabel : function(value) {
this._displayLabel = value;
},
dispose : function() {
//Add custom dispose actions here
MapCoordinateDisplay.MapCoordinateDisplay.callBaseMethod(this, 'dispose');
}
}
Attach an event listener to the map抯 mouse move event. We wrap the listener in a call to createDelegate so that the "this" keyword in the listener will refer to the MapCoordinateDisplay control.
[JavaScript]
MapCoordinateDisplay.MapCoordinateDisplay.prototype = {
initialize : function() {
MapCoordinateDisplay.MapCoordinateDisplay.callBaseMethod(this, 'initialize');
// Add custom initialization here
if(this._map) {
this._map.add_mouseMove(Function.createDelegate(this, this._onMapMouseMove));
}
},
_onMapMouseMove : function(sender, args) {
// Display the co-ordinates
},
get_map : function() {
return this._map;
},
Define the handler for the mouseMove event to display the coordinates by manipulating the innerHTML property of the label using DHTML and JavaScript.
[JavaScript]
_onMapMouseMove : function(sender, args) {
// Display the co-ordinates
if(this._displayLabel) {
this._displayLabel.innerHTML = args.coordinate.toString();
}
},
The only remaining part of our scriptable control implementation is including code that will create a corresponding MapCoordinateDisplay client control for any web tier instance. To do this, we construct JavaScript to instantiate the client control with the relevant server control properties and then register this script to so that it executes on application initialization. Note that, since our control derives from the base ADF WebControl, we can safely reference ASP.NET AJAX events and functions. This initialization code should be specified in the OnPreRender method as follows:
[C#]
protected override void OnPreRender(EventArgs e)
{
base.OnPreRender(e);
if (!base.IsAsync)
{
StringBuilder script = new StringBuilder();
script.Append("Sys.Application.add_init(function() {");
script.Append("$create(MapCoordinateDisplay.MapCoordinateDisplay,");
script.AppendFormat("{{\"displayLabel\":$get('{0}')", m_displayLabel.ClientID);
script.AppendFormat("}}, null, {{\"map\":\"{0}\"}}, $get('{1}'));", Map, ClientID);
script.AppendLine("});");
ScriptManager.RegisterStartupScript(this, typeof(MapCoordinateDisplay), this.ClientID+"_startup",script.ToString(), true);
}
}
To test the control, Build the control assembly and deploy it in an application with a test page that includes a map and a map resource manager. Buddy the new control to the map. At runtime, as the mouse cursor moves over the map, he map coordinates in the new script control change accordingly.
For additional information on best practices regarding creating custom Web ADF controls, see the topic Best Practices > Developing custom Web ADF controls .