评论

收藏

[R语言] Silverlight与HTML页面交互基本方法总结

编程语言 编程语言 发布于:2021-08-06 14:55 | 阅读数:558 | 评论:0

现在SL3/4与HTML页面交互和以前相比已经变化了不少,参考了些资料,自行整理了一下。
Silverlight与Html页面的交互功能是通过统称为HTML Bridge的一组类型和方法来实现的。要启用交互功能需要在创建Silverlight控件时指定enableHtmlAccess参数为true,否则很多方法的使用都会引发异常。
在object标签式创建方法中为:
<param name="enableHtmlAccess" value="true" />

Silverlight直接控制HTML
在Silverlight中可以使用System.Windows.Browser命名空间中的类和方法来操作HTML,主要涉及以下几个类:

  • BrowserInformation - 代表浏览器及客户端操作系统相关信息
  • HtmlDocument - 代表浏览器中的HTML文档
  • HtmlElement - 代表了一个HTML元素
  • HtmlPage - 提供了操作DOM的方法
  • HtmlWindow - 代表JavaScript中的window
其中,最重要的就是HtmlPage类,它提供了一组静态方法来获取其他类的实例,如:

  • 取得BrowserInformation:
    BrowserInformation browserInfo = HtmlPage.BrowserInformation;
  • 取得HtmlDocument:
    HtmlDocument htmlDocument = HtmlPage.Document;
  • 取得HtmlWindow:
    HtmlWindow htmlWindow = HtmlPage.Window;
下面列举一些常用操作:

  • 在新窗口中打开网页:
    HtmlPage.Window.Navigate(new Uri("https://www.google.com"), "__blank");
  • 修改页面标题:
    HtmlPage.Document.SetProperty("title", "New Title");
  • 修改、取得页面元素属性:
    HtmlElement elem = HtmlPage.Document.GetElementByID("elem1");
    elem.SetAttribute("value", "haha");
    string value = elem.GetAttribute("value");
  • 注册HTML元素事件:
    elem.AttachEvent("onclick", delegate(object sender, HtmlEventArgs he)
    {
        // ...
    });

JavaScript中调用Silverlight方法/属性
要从JavaScript中调用Silverlight方法,Silverlight必须首先注册ScriptableObject。这可以通过给要暴露给js的类型加上ScriptableTypeAttribute,这样就会暴露该类型的所有属性、方法和事件;如果只要暴露一部分成员,也可以仅给这一部分成员标记ScriptableMemberAttribute(在过去的版本中仅有ScriptableAttribute)。这个标记是非常宽松的,只要类中有成员是Scriptable的,就可以使用HtmlPage.RegisterScriptableObject方法来注册这个类使其能被js访问。比如:
public partial class MainPage : UserControl
{
  public MainPage()
  {
    InitializeComponent();
    HtmlPage.RegisterScriptableObject("Page", this);
  }
  [ScriptableMember]
  public string Process(string arg)
  {
    return "Called from js: " + arg;  }}
在html页面则可以用如下js代码调用Process方法:
function callSL() {
  var slHost = document.getElementById("SilverlightControl");
  var page = slHost.Content.Page;
  alert(page.Process('param from js'));
}
将callSL方法注册到按钮的onclick上:
<input type="button" value="Call SL"  />
点击运行结果如下:
DSC0000.png

Silverlight属性的调用方法同理。
(附带一提,这里的"SilverlightControl"是Silverlight控件的id,即Silverlight的object标签id。)

JavaScript注册Silverlight中的事件
  无参数事件
Silverlight中,事件用ScriptableMemberAttribute标记或者包含事件的类用ScriptableTypeAttribute标记后,再用HtmlPage.RegisterScriptableObject注册该类,在JavaScript中就可以访问该事件。比如Silverlight暴露如下事件:
public event EventHandler ButtonClicked;
那么在js中可以用下面的代码注册该事件:
function onSilverlightLoad() {
  var slHost = document.getElementById("SilverlightControl");
  var page = slHost.Content.Page;
  page.ButtonClicked = function () {
    alert("Button clicked in SL.");
  };}
那么当用户点击Silverlight中的按钮时,就会执行注册的js代码:
DSC0001.png

值得注意的是,这段js并不能放在页面的onload事件中执行,因为那时Silverlight控件可能还没加载完成,应该放在Silverlight控件的onload事件中。在Silverlight的标签中添加onLoad参数:
<param name="onLoad" value="onSilverlightLoad" />
带参数事件
然后是如何传递事件参数的问题,网上许多人说指定自定义参数时,在js中无法获取,提示不支持指定的方法或属性,其实只要将EventArgs类本身也指定为ScriptableType即可(当然也可将成员指定为ScriptableMember)。示例如下。
事件参数类:
[ScriptableType]
public class ButtonClickedEventArgs : EventArgs
{
  public string Message { get; set; }
}
MainPage类:
public partial class MainPage : UserControl
{  …private void Button_Click(object sender, System.Windows.RoutedEventArgs e)
  {    // 引发ButtonClicked事件并传入事件参数
    OnButtonClicked(new ButtonClickedEventArgs() { Message = "Message from SL." });
  }
  #region [ButtonClicked Event]
  [ScriptableMember]
  public event EventHandler<ButtonClickedEventArgs> ButtonClicked;
  [System.Diagnostics.DebuggerStepThrough]
  protected virtual void OnButtonClicked(ButtonClickedEventArgs e)
  {
    if(null != ButtonClicked)
    {
      ButtonClicked(this, e);
    }
  }
  #endregion
}
将js的事件处理函数改成:
page.ButtonClicked = function (sender, args) {
  alert(args.Message);
};
如此,当点击SL中按钮时结果如下:
DSC0002.png

可见event args被成功传递了出来。

Under the Hood
至此基本已满足一般使用的需要了。其实不论是从js调用Silverlight方法/属性,或者注册Silverlight事件,其本质问题都是如何将托管对象传给JavaScript,这里面就有一个对象marshal的问题。Marshal遵循以下原则:

  • 托管类型通过传递引用的方式传给JavaScript
  • JavaScript类型要传入Silverlight,必须要先进行一层托管的封装
  • 如果将托管类型marshal到JavaScript过程发生错误,抛出InvalidOperationException
  • 如果将JavaScript数据marshal入Silverlight时发生错误,JavaScript调用者将得到一个异常,异常文本描述了发生的错误
基本类型(即所谓的Primitive types)
以下类型属于基本类型,即不需要标记ScriptableTypeAttribute或者ScriptableMemberAttribute而可以直接在Silverlight和js之间传递的类型:
SilverlightJavaScript
String字符串
nullnull
BooleanBoolean
DateTimeDate
Char单字符的字符串
数字类型Double(来回转换可能引发精度及溢出问题)
枚举数字
Guid格式化的字符串
除此之外,基本的C#数组或者实现了IList接口的类型,可以直接转换为JavaScript中的array;实现了IDictionary的类型也可以直接转为JavaScript中的dictionary。
反过来,JavaScript中的数组传入Silverlight时,一般会被转为object[];dictionary则被转为Dictionary<string, object>.
复杂类型
对于用户创建的复杂类型,则需要通过标记ScriptableTypeAttribute或者ScriptableMemberAttribute,使其能够正确地传递给JavaScript.
对于Silverlight和JavaScript间的Marshal,以上依然只描述了各种最基本的情形。我也不想做那种照翻MSDN的事情,对于更加复杂的情形,以及各种注意事项,可自行参考Silverlight and JavaScript Marshaling。
------------
参考:

  • silverlight 与 javascript 交互
  • silverlight与javascript交互操作
  • HTML Bridge: Interaction Between HTML and Managed Code
  • Making Silverlight Scriptable by JavaScript
  • Silverlight and JavaScript Marshaling
  • Adding Silverlight to a Web Page by Using HTML or JavaScript
------------


关注下面的标签,发现更多相似文章