silverlight:Silverlight 2 中的浏览器互操作性

  本文举例源代码或素材下载

Silverlight 2 中<img src='/icons/89736de.gif' />浏览器互操作性目录

  配置 Silverlight Control控件

  从 Silverlight 访问 DOM

  深入了解浏览器互操作性层

  将托管代码附加到 DOM 事件

  同步和 Silverlight 2

  XAML、托管代码和 JavaScript

  跨域访问和 Silverlight 插件

  整理总结

  利用 Silverlight 2 可构建整页 Windows Presentation Foundation (WPF) 式 Web 应用也可借助其他些功能(如动画、广告和特定小)来丰富基于 HTML 页面浏览器并不直接处理构成 Silverlight 应用“可扩展应用标记语言”(XAML) 内容实际上在 HTML 页面中会有个 <object> 标记指向 Silverlight 2 插件并且其参数中会包含用于下载所有必需 XAML 资源 URL

  最基本点在于始终都存在个封装了 Silverlight 插件 HTML 页面即使通常会由于 Silverlight 应用被配置为全屏模式运行而看不到它此外在同站点上Silverlight 插件所承载内容和周围页面并非隔离如果 Silverlight 内容来自托管 HTML 页面以外其他域它和页面才是真正隔离

  Silverlight 带有个浏览器互操作性层它允许托管代码访问基础页面文档对象模型 (DOM)并允许注册页面级事件托管处理同时页面中运行所有 JavaScript 代码均可获得对插件 XAML 内容访问权限甚至可以进行修改最后只要正确公开页面中运行 JavaScript 代码也可以托管本月我将介绍 Silverlight 2 浏览器互操作性层以及如何在应用中充分利用它

  配置 Silverlight Control控件

  如前所述Silverlight 插件是通过 <object> 标记进行但是此标记可通过 ASP.NET 页面中 ASP.NET Control控件来创建如果在 Visual Studio 2008 中创建个举例 Silverlight 应用则下列用于 Silverlight 服务器Control控件标记会自动插入到测试页面中:

<asp:Silverlight ID="Xaml1" runat="server"
   Source="~/ClientBin/SilverTestApp.xap"
   MinimumVersion="2.0.30523"
   Width="100%"
   Height="100%" />


  MinimumVersion 属性指出运行 Source 属性中指定应用所需 Silverlight 运行时最低版本Width 和 Height 定义 Silverlight 窗口相对于宿主页面尺寸正如您所看到默认情况下Silverlight 插件被配置为以标准尺寸模式运行

  图 1 列出了配置 Silverlight 插件时可设置特性图 1 中特性对应是 Silverlight ASP.NET Control控件此Control控件在服务器端执行且可生成可能包含区别名称参数或不包含等效参数 <object> 标记在本专栏中应格外注意 HtmlAccess 特性(请注意从 Beta 1 过渡到 Beta 2 后Silverlight SDK 部分有所变化)

Silverlight 2 中<img src='/icons/89736de.gif' />浏览器互操作性图 1 Silverlight ASP.NET 服务器Control控件特性

特性 介绍说明
AutoUpgrade 指示是否应自动升级 Silverlight 插件默认值为 false
DefaultScriptType 用于创建 Silverlight 插件并和的建立关联客户端 JavaScript 对象默认类型默认情况下它是 Sys.UI.Silverlight.Control
EnableFrameRateCounter 指示是否在托管浏览器状态栏中显示当前帧速率默认值为 true
EnableRedrawRegions 指示是否显示为每个帧重绘 Silverlight 插件区域默认值为 true
HtmlAccess 指示是否允许 Silverlight 应用访问页面 DOM
InitParameters 定义可选组用户定义化参数
MaxFrameRate 针对 Silverlight 文档每秒呈现最大帧数
MinimumVersion 当前应用所需插件最低版本默认值为 1.0
PluginBackground 指示插件背景色
PluginNotInstalledTemplate 未安装 Silverlight 插件时呈现 HTML 标记
ScaleMode 指示 Silverlight 插件如何填充可用空间:none、zoom 或 stretch默认值为 none
ScriptType 用于创建 Silverlight 插件并和的建立关联客户端 JavaScript 对象类型
Source 要下载 XAML 源文件或可扩展 Ajax 平台 (XAP) 源数据包 URL
SplashScreenSource 要在加载源文档时呈现启动画面文档 URL
Windowless 指示是直接在浏览器客户端区域中还是在适当创建窗口中呈现 Silverlight 插件默认值为 true



  HtmlAccess 特性指出 Silverlight 应用对基础页面 DOM 访问权限级别此特性接受来自 HtmlAccess 枚举类型这些值如图 2 所示

Silverlight 2 中<img src='/icons/89736de.gif' />浏览器互操作性图 2 用于控制对页面 DOM 访问权限

值 介绍说明
Disabled 不允许应用访问基础页面 DOM
Enabled 允许应用访问基础页面 DOM
SameDo 仅当页面来自和 Silverlight XAP 应用相同域时才允许应用访问基础页面 DOM这是默认设置



  显然Enabled 和 Disabled 都很容易理解默认设置为 SameDo它并不会在页面标记中注入任何脚本请注意Silverlight 应用可被承载在某个页面中而该页面又承载在其本机域以外某个帧中在这种情况下Silverlight 托管代码将能够以跨域方式访问宿主页面 DOM浏览器可使用自己屏障来阻止跨域脚本但它们对于阻止 Silverlight 插件中托管代码也无能为力Silverlight 页面编写者利用 HtmlAccess 来控制跨域访问

  从 Silverlight 访问 DOM

  经授予对基础页面 DOM 访问权限Silverlight 应用即可使用静态类 HtmlPage 成员来完成自己任务图 3 列出了 HtmlPage 类特性和思路方法

Silverlight 2 中<img src='/icons/89736de.gif' />浏览器互操作性图 3 HtmlPage 类成员

成员 介绍说明
BrowserInformation 获取有关浏览器常规信息如名称、版本和操作系统
Document 提供对页面文档对象访问权限
IsEnabled 指示是否允许访问页面 DOM
Plugin 获取对 Silverlight 对象引用
Window 提供对页面窗口对象访问权限
RegisterCreateableType 通过 JavaScript 代码将托管类型注册为可供创建时使用
RegisterScriptableObject 通过页面中 JavaScript 代码将托管对象注册为可编写脚本
UnregisterCreateableType 注销先前使用 RegisterCreteableType 思路方法注册为可供创建时使用类型



  Document 特性将返回对托管类型 .Windows.Browser.HtmlDocument 对象引用该类型代表基础页面 DOMDocument 特性是强类型化特性可向 Silverlight 应用提供用于以弱类型化方式编写脚本大部分功能这些特性包括 cookie 列表、查询串、文档正文以及对 DOM 根引用该对象还具有个布尔型 IsReady 特性可指示浏览器文档是否已完全加载这和浏览器 DOM 中 DocumentReady 事件变换相对应

  HtmlDocument 类还包含 Submit、AttachEvent、DetachEvent、GetElementById 和 CreateElement 等思路方法此外还可使用两个继承思路方法(GetProperty 和 SetProperty)在托管代码中获取和设置 HTML 元素属性

  完整浏览器信息可通过 BrowserInformation 特性来获取并且在这种情况下特性属于托管类型可封装在浏览器级别可用所有用户代理信息以下代码段显示了如何访问用户代理数据:

info = HtmlPage.BrowserInformation.UserAgent;

  BrowserInformation 对象还包含个布尔特性可指示浏览器是否支持 cookie

  以下代码显示了如何使用托管代码来检索对 DOM 元素引用:

HtmlElement label1 = HtmlPage.Document.GetElementById("Label1");
label1.SetProperty("innerHTML", "Dino");


  GetElementById 思路方法利用 ID 串尝试在基础 DOM 中定位相应元素返回对象类型为 HtmlElement这是种托管类型可作为对基础浏览器对象引用包装

  HtmlElement 具有组可使它看起来像是 HTML 元素特性它包含 AppendChild、RemoveChild、GetAttribute、RemoveAttribute 和 SetAttribute 等思路方法这些特性包括 Id、Parent、TagName 和 Children

  在 Silverlight 代码中操作所有 HTML 脚本对象均派生自基本 ScriptObject 类此类将定义所有派生类(如 HtmlDocument)继承两个思路方法——SetProperty 和 GetProperty

  获取和设置属性和获取和设置特性的间有何区别?属性始终作为串进行管理;而特性却作为强类型化值进行管理

  深入了解浏览器互操作性层

  图 4 中图形视图显示了 Silverlight 浏览器互操作性层和对页面 DOM 访问对互操作性层中任何类所提出请求均通过内部浏览器宿主服务进行解析信息被向下封送到浏览器非托管环境然后再封送回 Silverlight类型差异被隐藏起来均由互操作性层来处理DOM 级别对象通过新托管接口(HtmlDocument、HtmlWindow 等类似接口)包装成托管对象并提供给 Silverlight 代码我们将着重谈下 GetElementById 思路方法

Silverlight 2 中<img src='/icons/89736de.gif' />浏览器互操作性   图 4 Silverlight 2 中 HTML 桥

  首先此思路方法将确保代码是在 Silverlight UI 线程中被否则会抛出异常接下来将向基础浏览器发出个请求以获取对指定 DOM 元素引用如果请求成功此思路方法将得到 DOM 对象非托管引用然后它将会为此对象创建并返回托管包装

  将托管代码附加到 DOM 事件

  Silverlight 和 DOM 实现交互带来个极大好处是能够运行托管代码来响应 DOM 事件例如当用户单击某个按钮时您可执行 C# 代码而非 JavaScript 代码其实现方式如下:

Silverlight 2 中<img src='/icons/89736de.gif' />浏览器互操作性HtmlElement button1;
button1 = HtmlPage.Document.GetElementById("Button1");
button1.AttachEvent("click",
     .EventHandler(Button1_Click));


  首先检索对感兴趣按钮(或 DOM 元素)托管引用接下来托管 AttachEvent 思路方法以便为特定事件注册个处理真正亮点在于处理是托管代码而事件在浏览器非托管级别被触发例如获取 GUID 在 JavaScript 中几乎是不可能实现事情但是如果能够通过 Silverlight 利用托管代码功能则这将变得非常简单:

void Button1_Click(object sender, EventArgs e)
{
  // Get a GUID
  Guid g = Guid.NewGuid;
  // Display the GUID in the page user erface
  HtmlElement label1 = HtmlPage.Document.GetElementById("Label1");
  label1.SetProperty("innerHTML", g.);
}


  毫无疑问托管代码是 Silverlight 页面代码隐藏类个成员此操作效果可反映在宿主页面 HTML 中(如先前举例所示)也可直接反映在 Silverlight 用户界面中——完全取决于您以及您所处环境

  向事件附加个操作它通过浏览器互操作性层发生并以 DOM 对象 AttachEvent 思路方法结束当浏览器触发页面级事件时会逆向 Silverlight 以执行托管代码

  此类功能在何种情况下会被用到?Silverlight 这产品设计目是为了提供丰富 Web 前端对于浏览器托管代码完成工作如果您想让托管语言完成得比脚本更好更快则需要用到 Silverlight具体举例包括编译语言速度明显优于脚本那些代码密集型操作或者在功能有限环境中(如浏览器环境)不可用操作可以肯定如果您需要做只是操作 DOM则并非定要使用 Silverlight能够在托管代码中处理事件是个非常出色功能但它必须要用到 Silverlight而且您可能并不希望使用 Silverlight 只是用来处理事件

  在 Silverlight 2 中HtmlWindow 对象将提供 JavaScript 窗口对象托管表示它存储着对 DOM 对象引用并允许通过下面组托管思路方法来驱动它:Alert、Confirm、Prompt、Submit、Navigate 以及 EvalHtmlWindow 对象例子将通过 HtmlPage 类 Window 特性公开给 Silverlight 开发人员下列代码显示了如何通过 Silverlight 显示浏览器消息框:

Silverlight 2 中<img src='/icons/89736de.gif' />浏览器互操作性

HtmlPage.Window.Alert("Hello, world");

  让我们来研究下这简单代码段要实现功能我所要研究模型几乎在 HtmlWindow 类所有思路方法中都会重复用到:

public void Alert( message)
{
  HtmlPage.VeryThread;
   (message null)
  {
    message = .Empty;
  }
  this.Invoke("alert",
         object { message });
}


  对 UI 线程进行测试后如果消息串为空则此思路方法会修复它并继续针对基本 ScriptObject 类来 Invoke 思路方法Invoke 思路方法在 Silverlight 托管领域和浏览器的间搭建了座桥梁

  此思路方法接受两个参数如下所示:

public virtual object Invoke( name, params object args)

  第个参数指出要针对存储在 ScriptObject 当前例子中可编写脚本对象思路方法名称第 2个参数只是要思路方法参数列表

  此思路方法负责在两种区别运行时环境中正确封送类型在封送到浏览器过程中它会将托管对象转换成和 JavaScript 兼容类型;在返回过程中它将执行相反操作

  HtmlWindow 还有个值得关注思路方法——CreateInstance 思路方法:

public ScriptObject CreateInstance(
   typeName,
  params object args)


  此思路方法允许创建指定 JavaScript 对象例子类型名称参数表示要例子化 JavaScript 对象名称在内部此思路方法会准备个创建指定对象动态 JavaScript 然后再从 Silverlight :

ScriptObject xhr = HtmlPage.Window.CreateInstance("XMLHttpRequest");

  在获取对某个 JavaScript 对象可编写脚本引用时思路方法 CreateInstance 会非常有用接下来使用 Invoke 思路方法对其编写脚本如果确需要从 Silverlight 2 进行同步则这些功能也会非常有用但问题是在 Silverlight 2 中不允许进行同步

  同步和 Silverlight 2

  Silverlight 2 提供了各种 API 来对远程端点进行但它们都必须是异步您可以在后台线程中启动但却无法强制 UI 线程同步如果阻截了某个同步对象 UI 线程则实际上相当于无限期停止了该 UI 线程而且没有任何重置同步对象命令可以恢复它在社区中有关 Silverlight 中同步问题是最大争论焦点

  尽管如此目前仍不支持从 Silverlight API 中对远程端点进行同步它们存在关联延迟问题已经众所周知并经过了证实但是同步是浏览器环境中个重要功能在支持 XmlHttpRequest 所有浏览器中也都支持它们

  XmlHttpRequest 是个浏览器对象使用它可以实现 AJAX 方案此对象利用浏览器机制实现对同域中某个端点默认情况下XmlHttpRequest 异步执行并且大多数 AJAX 框架在使用它时都采用这方式但是可非常轻松地将 XmlHttpRequest 配置为同步执行

  通过从 Silverlight 创建和控制 XmlHttpRequest 例子您可以安排同步并修复所有采用同步可使编码更轻松特殊方案(就个人而言我并非 Silverlight 远程仅提供异步方式狂热拥趸者在大多数情况下异步即已足够;但我认为您可能会遇到同步会节省许多重新设计开销情形(尤其是要使某种现有前端适应 Silverlight 时)我是第个强调设计是关键但是如果特意编写同步可节省我数小时甚至数天工作我肯定会使用它)

  尽管如此Silverlight 团队(Team)还是有充足理由来推动仅异步思路方法对远程端点同步可能导致 Silverlight 应用冻结用户界面从而损害最终用户在使用浏览器和 Web 应用体验同步从技术上来说是可能但是出于对所有应用及其客户利益考虑团队(Team)并不会在平台中对此提供本机支持同样团队(Team)并不推荐依赖手动编写且使用 XmlHttpRequest 同步远程(如图 5 所示)

Silverlight 2 中<img src='/icons/89736de.gif' />浏览器互操作性图 5 从 Silverlight 2 进行同步

private void Button1_Click(object sender,
  .Windows.RoutedEventArgs e)
{
   url = "...";
  ScriptObject xhr = HtmlPage.Window.CreateInstance("XMLHttpRequest");
  xhr.Invoke("open", "POST", url, false);
  xhr.Invoke("RequestHeader", "Content-Type",
    "application/x-www-form-urlencoded");
  // Prepare the body as the endpo expects it to be
   body = "...";
  xhr.Invoke("send", body);
   response = () xhr.GetProperty("responseText");
  // Process the response and update the UI
  ProcessData(response)
}


  图 5 展示了如何使用 XmlHttpRequest 从 Silverlight 2 建立对某个 URL 同步同域其中关键是针对 XmlHttpRequest open 思路方法时机布尔型参数指示是否必须为异步如果为 false则指示对象以同步方式继续

  此窍门技巧缺点是它利用工具级别较低未提供任何功能来实现串和其他内容(例如 JavaScript Object Notation (JSON) 流)的间转换如果使用此窍门技巧则必须使用 DataContractJsonSerializer 类来处理所有 JSON 序列化和反序列化操作

  XAML、托管代码和 JavaScript

  JavaScript 可获取对 Silverlight 应用内容访问权限并执行读取和写入操作Silverlight 应用内容是 XAML 元素树

  以唯名称——x:Name 属性——为特征 XAML 文档中所有内容均可在 JavaScript 中进行访问和编写脚本步需要获取对 Silverlight 插件 DOM 引用在 ASP.NET AJAX 页面中需要使用以下代码:

var plugin = $get("SilverlightControl1");

  SilverlightControl1 是 Silverlight Control控件 ID或者是用于指向可下载内容 <object> 标记 ID接下来使用 content 特性指向实际 XAML 内容要定位特定 XAML 元素可使用 findName 思路方法:

// Retrieve the XAML element tagged with the name of TextBlock1
var xamlTree = $get("SilverlightControl1").content;
var textBlock1 = xamlTree.findName("TextBlock1");
// Mody the current content of the text block element
textBlock1.Text = "...";


  如果使用 JavaScript 代码来驱动 XAML 文档内容则建议您在页面中缓存Cache对遇到 XAML 元素所有引用这样就不必重复遍历 XAML 树来遍遍地寻找相同元素

  请注意对于 Silverlight 2 而言使用 JavaScript 来访问 XAML 文档内容略显过时如果目标是 Silverlight 1.0或者至少您要提供 Silverlight 应用仅由 XAML 和脚本代码组成则它仍是个不错选择如果可使用托管代码来决定要显示内容则很难想象使用 JavaScript 而非托管代码该如何实现要对用户界面进行任何改动

  在 Silverlight 2 中可使用 JavaScript 代码来托管代码如果方案包含基于 HTML 页面且该页面由些执行关键操作托管代码提供技术支持则可采用此选项

  您 JavaScript 代码可对 Silverlight 2 中承载先前已注册为可编写脚本任何托管对象编写脚本:

Silverlight 2 中<img src='/icons/89736de.gif' />浏览器互操作性

guidHelper = GuidHelper;
HtmlPage.RegisterScriptableObject("GuidTools", guidHelper);


  思路方法 RegisterScriptableObject 将会使用被注册对象非正式名称以及变量例子作为第个参数串是方 JavaScript 代码将要用来引用已注册对象名称

  对于的前代码以下代码显示了如何使用 JavaScript 来针对举例类 GuidHelper 某个思路方法:

// Invoke a method _disibledevent= (ScriptObject) content.GetProperty("Action");
action.Invoke("GetRandomNumber");


  最终效果是相同但是此思路方法需要在 Silverlight 和基础浏览器的间进行更多往返操作

  整理总结

  Silverlight 基类库包括用于连接到浏览器和宿主页面以及读取信息和访问 DOM 多种功能如果应用以 Silverlight 为中心则可能无需对宿主 HTML 页面执行过多操作但是对于兼具 ASP.NET AJAX 和 Silverlight 混合解决方案则必须从 JavaScript 执行托管代码或在托管代码中 JavaScript 对象

  浏览器互操作性层(也称为 HTML 桥)包含多种功能可实现 Silverlight 托管领域和 JavaScript 解释领域的间通信通信需要在各层的间封送类型和对象在最高级别抽象中您会发现个公共 API 和些文档其中解释了从 JavaScript 托管代码以及从托管代码 JavaScript 对象需要了解些知识

  如果有任何特殊问题或者希望了解在具体实施时应考虑细节可密切关注 Wilco Bauwer 信息丰富博客

  请将您想向 Dino 询问问题和提出意见发送至 [email protected]

  Dino Esposito 是 IDesign 架构师也是Microsoft .NET:Architecting Applications for the Enterprise(Microsoft Press, 2008) 合著者Dino 定居于意大利经常在世界各地业内活动中发表演讲您可加入他博客网址为 weblogs.asp.net/despos

Tags:  silverlight2 silverlight.2.0 silverlight是什么 silverlight

延伸阅读

最新评论

发表评论