技术开发 频道

使用 ASP.NET 控件封装 Silverlight


  问题的症结是确定如何将服务器端属性引入客户端脚本。很幸运,ASP.NET AJAX 扩展提供了一种简洁的方法,即通过 System.Web.Extensions 程序集定义的 IScriptControl 接口将服务器端控件与客户端属性关联起来。此接口定义了两个方法:GetScriptReferences 和 GetScriptDescriptors。实现这些方法并将其与页面的 ScriptManager 控件关联后,它们会分别生成对 JavaScript 文件的引用和声明 JavaScript 类的实例:
public interface IScriptControl { IEnumerable<ScriptDescriptor> GetScriptDescriptors(); IEnumerable<ScriptReference> GetScriptReferences(); }
  您可以将描述符视作服务器端控件的客户端代理,其中含有初始化为服务器控件定义的属性。这会将 Silverlight 和 JavaScript 处理程序可使用的服务器控件属性和客户端属性联系起来。描述符还提供了一个简洁的范围机制,用于隔离命名空间和类下所有控件的客户端功能(ASP.NET AJAX 客户端库至少在 JavaScript 中定义了命名空间和类)。

  当生成承载 Silverlight 内容的 ASP.NET AJAX 控件时,首先创建客户端类定义,其中含有定义好的与 Silverlight 控件交互的方法和属性。可以将 Title 和 XamlUrl 属性都添加到此控件,因此开始时先定义带有这些属性的类,将类命名为 MsdnMagazine.SilverlightAjaxSphere:
// File: SilverlightAjaxSphere.js Type.registerNamespace('MsdnMagazine'); MsdnMagazine.SilverlightAjaxSphere = function(element) { MsdnMagazine.SilverlightAjaxSphere.initializeBase( this, [element]); this._title = null; this._xamlUrl = null; }
  现在需要为类的原型填充方法,开始初始化(请参见图 6)。与将脚本嵌入 <div> 元素(就像在第一个控件实现中那样)不同,这次是在客户端类的初始化方法中创建 Silverlight 插件。请注意,只有在调用此方法之前,用服务器端值填充类中定义的 _xamlUrl 属性,才有可能创建。还可以访问 <div> 的标识符,它是由服务器端控件使用从 Sys.UI.Control 基类继承的 get_id 方法呈现的。

  当在类中调用初始化方法时,即会创建 Silverlight 插件,但尚未对 XAML 求值。因此,我使用 Sys.Silverlight 的 createObject 方法中定义的 onLoad 事件处理程序指定加载 XAML 后调用的回调——这便可以绑定事件处理程序并初始化各 XAML 元素的属性。此处理程序(我已将其称为 _onXamlLoaded)以引用的方式传递给根 Silverlight 元素,可以使用此元素检索 XAML 中的命名元素。

  当与命名 XAML 元素交互时,必须保持耐心,因为这要求必须检查这些元素是否存在,然后才能真正修改其内容或绑定事件处理程序。如果缺少元素且元素对控件的操作不重要(就像控件的 XAML 中的 titleText 元素对控件不重要一样),请跳过该元素的初始化步骤。通过这种方式,设计者可以选择删除某些元素,而控件仍正常工作。

  类的其余实现是定义标题和 xamlUrl 的属性访问器,并实现 _onSphereButtonDown 函数以启动变大和变小动画,这与前面类似。但是,此时方法是在类中封装的。

  客户端脚本的最后步骤是注册新类,指定基类为 Sys.UI.Control。

  余下的任务是生成通过 IScriptControl 接口使用此类的服务器端控件(请参见图 7)。与客户端类相似,服务器控件公开两个属性:Title 和 XamlUrl。(ViewState 支持这些属性,因为 ViewState 总是支持所有服务器端控件属性。)我将 XamlUrl 属性初始化为 URL,使用上个控件实现中使用过的同一个 GetWebResourceUrl 方法引用嵌入的 XAML 文件。通过在 OnInit 的重写中设置此属性,确保该属性默认设为此 URL,但客户端指定的任何重写版本将始终优先,因为状态在生命周期的后期加载。

  接下来的两个方法 GetScriptDescriptors 和 GetScriptReferences 都包含 IScriptControl 接口的实现。因为将在程序集中将 JavaScript 文件作为资源嵌入,所以使用 ScriptReference 的重载构造函数,该函数使用资源名称和程序集名称。这会生成对嵌入脚本资源的引用,其方式与在前面的控件实现中生成对 RegisterClientScriptResource 的实现大致相同。GetScriptDescriptors 是创建使用服务器端控件中属性初始化客户端 MsdnMagazine.SilverlightAjaxSphere 类的实例关键。

  ScriptControlDescriptor 类采用客户端类的名称和其构造函数中关联客户端元素的 ID。然后,使用类的 AddProperty 方法指定新客户端类的每个属性的初始值。通过传递控件中的当前属性值,将确保使用控件中的当前属性值创建客户端类。

  仍需要最后关注 OnPreRender 的重写,在此重写中,使用页面上的当前 ScriptManager(若无 ScriptManager 将引发异常)以及脚本描述符将控件注册为脚本控件。这会导致 ScriptManager 调用控件上 IScriptControl 的两个方法获取脚本引用并创建初始的客户端类。Render 方法与前面的控件实现相同,仅创建 <div> 元素充当 Silverlight 插件的宿主。

  现在就得到了封装 Silverlight 的完整的服务器端控件。在此实现中,控件公开用于初始化 XAML 元素的属性(或如需要,甚至替换整个 XAML)。可以在页面上通过下列声明创建新控件的实例,且标题文本设置为自定义字符串:
<%@ Register Assembly="SlAjaxSphere" Namespace="MsdnMagazine" TagPrefix="sas" %> ... <sas:SilverlightSphere ID="_silverlightSphere" runat="server" Title="My custom title!" />
0
相关文章