Cutting Edge
Silverlight 2 におけるブラウザの相互運用性
Dino Esposito
このコラムは、Silverlight 2 のプレリリース バージョンに基づいています。ここに記載されているすべての情報は、変更される場合があります。

目次
Silverlight 2 を使用すると、まるで Windows Presentation Foundation (WPF) で作成したようなフルページの Web アプリケーションを構築したり、アニメーション、広告、特定のアプレットなどの機能を追加して HTML ベースのページの質を高めたりできます。ブラウザは、Silverlight アプリケーションを構成する eXtensible Application Markup Language (XAML) コンテンツを直接処理しません。その代わりに、HTML ページ内で <object> タグを通じて Silverlight 2 プラグインを参照し、そのパラメータ間に URL を保持して、必要な XAML リソースをダウンロードします。
重要なのは、Silverlight プラグインにラップされた HTML ページが常に存在しているということです (ただし、Silverlight アプリケーションが全画面表示モードで実行されるように構成されているために、その HTML ページが表示されないこともよくあります)。また、サイトが同じ場合、Silverlight プラグインによってホストされているコンテンツは周囲のページから切り離されているわけではありません。Silverlight コンテンツがホスト HTML ページとは異なるドメインから提供されている場合は、そのコンテンツはページから切り離されていることになります。
Silverlight は、ブラウザの相互運用層を備えています。この相互運用層を通じて、マネージ コードで基になるページのドキュメント オブジェクト モデル (DOM) にアクセスし、ページレベル イベントのマネージ ハンドラを登録できます。同時に、ページで実行されている JavaScript コードでプラグインの XAML コンテンツにアクセスしたり、変更を加えたりできます。さらに、ページで実行されている JavaScript コードでマネージ関数を呼び出すこともできます (ただし、これらの関数が適切に公開されている場合)。今月は、Silverlight 2 が備えているブラウザの相互運用層と、それを実際のアプリケーションで活用する方法について説明します。
Silverlight コントロールを構成する
既に説明したように、Silverlight プラグインは <object> タグを通じて呼び出されます。ただし、このタグは、ASP.NET ページの ASP.NET コントロールで自動的に作成できます。Visual Studio 2008 で Silverlight のサンプル アプリケーションを作成すると、Silverlight サーバー コントロールの次のマークアップがテスト ページに自動的に挿入されます。
<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 コントロールを参照します。このコントロールはサーバー側で実行され、<object> タグを出力します。この <object> タグには、異なる名前の付けられているパラメータがある場合や、対応するパラメータがない場合もあります。このコラムの意図を意識して、HtmlAccess プロパティに注目してください (Silverlight SDK のこの部分は、Beta 1 から Beta 2 への移行に伴って若干変更されていることに注意してください)。

図 1 Silverlight ASP.NET サーバー コントロールのプロパティ
| プロパティ |
説明 |
| AutoUpgrade |
Silverlight プラグインを自動的にアップグレードするかどうかを指定します。既定値は false です。 |
| DefaultScriptType |
Silverlight プラグインの作成と関連付けに必要なクライアント JavaScript オブジェクトの既定の種類です。既定では、Sys.UI.Silverlight.Control です。 |
| EnableFrameRateCounter |
ホスト ブラウザのステータス バーに現在のフレーム レートを表示するかどうかを指定します。既定値は true です。 |
| EnableRedrawRegions |
各フレームを使用して再描画される Silverlight プラグインの領域を表示するかどうかを指定します。既定値は true です。 |
| HtmlAccess |
Silverlight アプリケーションがページ DOM にアクセスできるようにするかどうかを指定します。 |
| InitParameters |
ユーザー定義の初期化パラメータのオプション セットを定義します。 |
| MaxFrameRate |
1 秒あたりに描画する Silverlight ドキュメントのフレームの最大数です。 |
| MinimumVersion |
現在のアプリケーションに必要なプラグインの最小バージョンです。既定値は 1.0 です。 |
| PluginBackground |
プラグインの背景色を指定します。 |
| PluginNotInstalledTemplate |
Silverlight プラグインがインストールされていない場合に描画する HTML マークアップです。 |
| ScaleMode |
使用可能な領域に Silverlight プラグインをどのように配置するかを指定します (値なし、拡大/縮小、または伸縮)。既定は、値なしです。 |
| ScriptType |
Silverlight プラグインの作成と関連付けに必要なクライアント JavaScript オブジェクトの種類です。 |
| Source |
ダウンロードする XAML ソース ファイルまたは Extensible Ajax Platform (XAP) ソース パッケージの URL です。 |
| SplashScreenSource |
ソース ドキュメントの読み込み時に描画するスプラッシュ スクリーン ドキュメントの URL です。 |
| Windowless |
Silverlight プラグインをブラウザのクライアント領域に直接描画するか、適切に作成されたウィンドウ内部に描画するかを指定します。既定値は true です。 |
HtmlAccess プロパティは、Silverlight アプリケーションに許可する、基になるページ DOM へのアクセス レベルを示します。このプロパティは、HtmlAccess 列挙型から値を受け取ります。その値を図 2 に示します。

図 2 ページ DOM へのアクセスを制御する値
| 値 |
説明 |
| Disabled |
アプリケーションは、基になるページの DOM にアクセスできません。 |
| Enabled |
アプリケーションは、基になるページの DOM にアクセスできます。 |
| SameDomain |
アプリケーションは、基になるページが Silverlight XAP アプリケーションの同じドメインから提供されている場合にのみ、そのページの DOM にアクセスできます。これが既定の設定です。 |
Enabled と Disabled には、名前が示すとおりの効果があります。既定の設定は SameDomain です。この設定では、ページのマークアップにスクリプトは挿入されません。Silverlight アプリケーションは、ネイティブ ドメイン外部のフレーム内にホストされるページでホストできるという点に注意してください。この場合、Silverlight のマネージ コードは、ドメインをまたいでホスト ページの DOM にアクセスできます。ブラウザは、クロスドメイン スクリプティングを防ぐ独自のバリアを備えていますが、Silverlight プラグイン内のマネージ コードによるアクセスを防ぐことはできません。Silverlight ページの作成者は、HtmlAccess を使用してクロスドメイン アクセスを制御できます。
Silverlight から DOM にアクセスする
基になるページ DOM へのアクセスが許可された後、Silverlight アプリケーションは、静的クラスである HtmlPage のメンバを使用して独自のタスクを実行できます。図 3 に、HtmlPage クラスのプロパティとメソッドを示します。

図 3 HtmlPage クラスのメンバ
| メンバ |
説明 |
| BrowserInformation |
ブラウザに関する一般的な情報 (名前、バージョン、オペレーティング システムなど) を取得します。 |
| Document |
ページのドキュメント オブジェクトへのアクセスを提供します。 |
| IsEnabled |
ページ DOM へのアクセスが許可されているかどうかを示します。 |
| Plugin |
Silverlight オブジェクトへの参照を取得します。 |
| Window |
ページのウィンドウ オブジェクトへのアクセスを提供します。 |
| RegisterCreateableType |
JavaScript コードから作成できる型としてマネージ型を登録します。 |
| RegisterScriptableObject |
ページの JavaScript コードでスクリプト可能なオブジェクトとしてマネージ オブジェクトを登録します。 |
| UnregisterCreateableType |
RegisterCreteableType メソッドを使用して作成可能な型として登録されている型の登録を解除します。 |
Document プロパティは、基になるページの DOM を表す System.Windows.Browser.HtmlDocument マネージ型のオブジェクトへの参照を返します。Document プロパティは厳密に型指定されており、Silverlight アプリケーションに対して、柔軟に型指定された方法でスクリプトに使用できるほとんどの機能を公開します。プロパティには、Cookie の一覧、クエリ文字列、ドキュメントの本文、DOM のルートへの参照が含まれます。オブジェクトには、ブラウザのドキュメントが完全に読み込まれているかどうかを通知するブール型の IsReady プロパティもあります。このプロパティは、ブラウザの DOM における DocumentReady イベントの転位と対になっています。
HtmlDocument クラスには、Submit、AttachEvent、DetachEvent、GetElementById、CreateElement などのメソッドもあります。また、継承される 1 組のメソッド (GetProperty および SetProperty) を使用すると、マネージ コード内から HTML 要素の属性を取得および設定できます。
BrowserInformation プロパティを通じて、すべてのブラウザ情報を取得できます。また、この場合のプロパティは、ブラウザ レベルで取得できるあらゆるユーザー エージェント情報をラップするマネージ型です。次のコードは、ユーザー エージェント データへのアクセス方法を示しています。
string 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 などのすべての派生クラスが継承する 1 組のメソッド (SetProperty および GetProperty) を定義します。
取得および設定する属性とプロパティには、どのような違いがあるのでしょうか。属性は常に文字列として管理されますが、プロパティは厳密に型指定された値として管理されます。
ブラウザの相互運用層の内部
図 4 に、Silverlight のブラウザの相互運用層とページ DOM へのアクセスをグラフィックで示します。相互運用層でクラスに対して行われた要求は、内部ブラウザ ホスト サービスを通じて解決されます。情報はブラウザのアンマネージ環境にマーシャリングされてから、Silverlight に戻されます。型の違いは相互運用層で隠匿および処理されます。DOM レベルのオブジェクトはマネージ オブジェクトとしてラップされ、新しいマネージ インターフェイス (HtmlDocument、HtmlWindow など) を通じて Silverlight コードに提供されます。それでは、GetElementById メソッドについて詳しく見てみましょう。
図 4 Silverlight 2 における HTML ブリッジ
このメソッドは、まずコードが Silverlight の UI スレッドで呼び出されていることを確認します。コードがこのスレッドで呼び出されていない場合は、例外がスローされます。次に、指定した DOM 要素への参照を取得するために、基になるブラウザに対して要求を行います。要求が成功した場合は、メソッドは DOM オブジェクトのアンマネージ参照を取得し、そのオブジェクトに対してマネージ ラッパーを作成して返します。
DOM イベントにマネージ コードをアタッチする
すばらしいことに、Silverlight と DOM の対話の結果、DOM イベントに応答してマネージ コードを実行できるようになります。たとえば、ユーザーがボタンをクリックした際に、JavaScript コードの代わりに C# コードを実行できます。これを実現するための方法を次に示します。
HtmlElement button1;
button1 = HtmlPage.Document.GetElementById("Button1");
button1.AttachEvent("click",
new System.EventHandler(Button1_Click));
まず、目的のボタン (つまり DOM 要素) へのマネージ参照を取得します。次に、AttachEvent マネージ メソッドを呼び出し、特定のイベントのハンドラを登録します。すばらしいのは、ハンドラはマネージ コードですが、イベントはブラウザのアンマネージ レベルでトリガされるという点です。たとえば、JavaScript では GUID を取得することがほとんど不可能ですが、Silverlight を通じてマネージ コードを活用すれば、これをかなり簡単に行えるようになります。
void Button1_Click(object sender, EventArgs e)
{
// Get a new GUID
Guid g = Guid.NewGuid();
// Display the GUID in the page user interface
HtmlElement label1 = HtmlPage.Document.GetElementById("Label1");
label1.SetProperty("innerHTML", g.ToString());
}
言うまでもなく、マネージ コードは Silverlight ページの分離コード クラスのメンバです。処理の結果は、(前の例のように) ホスト ページの HTML に反映させることも、Silverlight のユーザー インターフェイスに直接反映させることもできます。開発者の裁量で、状況に応じて選んでください。
イベントのアタッチは、ブラウザの相互運用層を通じて実行され、最後に DOM オブジェクトで AttachEvent メソッドを呼び出す処理です。ブラウザがページレベルのイベントをトリガすると、Silverlight に対して呼び出しが行われ、マネージ コードが実行されます。
現実の世界では、こうした機能がどのような場合に役立つのでしょうか。Silverlight は、豊富な機能を備えた Web フロント エンドを提供するための製品です。スクリプトよりもマネージ言語の方が適切かつ迅速に処理できる操作を、マネージ コードを使用してブラウザで行う必要がある場合に、Silverlight が活躍します。このような操作の例としては、コンパイル言語の速度がスクリプトの速度をはるかに上回るような、コードの量が多い操作や、機能に制限のある環境 (ブラウザの環境など) では実行不可能な操作などが挙げられます。確かに、DOM の操作だけを行うのであれば、必ずしも Silverlight は必要ありません。マネージ コードでイベントを処理する機能があると非常に便利ですが、この機能を手に入れるために Silverlight が必要なのです。せっかく入手した Silverlight をイベントの処理だけに使用しようと考える開発者はいないでしょう。
Silverlight 2 では、HtmlWindow オブジェクトは、JavaScript ウィンドウ オブジェクトのマネージ表現を提供します。また、DOM オブジェクトへの参照を格納し、一連のマネージ メソッド (Alert、Confirm、Prompt、Submit、Navigate、および Eval) を使用してこれを処理できるようにします。HtmlWindow オブジェクトのインスタンスは、HtmlPage クラスの Window プロパティを通じて Silverlight 開発者に公開されます。次のコードは、ブラウザのメッセージ ボックスを Silverlight から表示する方法を示しています。
HtmlPage.Window.Alert("Hello, world");
このシンプルなコードの背後ではどのような処理が行われているのか、詳細に見てみましょう。ここで見ていくモデルは、HtmlWindow クラスのほとんどすべてのメソッドで繰り返し使用されます。
public void Alert(string message)
{
HtmlPage.VerifyThread();
if (message == null)
{
message = string.Empty;
}
this.Invoke("alert",
new object[] { message });
}
UI スレッドのテスト後、メソッドは、メッセージ文字列が null の場合はその文字列を修正し、ベースの ScriptObject クラスの Invoke メソッドを呼び出します。Invoke メソッドは、Silverlight のマネージ環境とブラウザを橋渡しする要素です。
このメソッドは、次のように 2 つのパラメータを受け取ります。
public virtual object Invoke(string name, params object[] args)
最初の引数は、ScriptObject の現在のインスタンスに格納されているスクリプト可能なオブジェクトで呼び出すメソッドの名前を示します。2 番目の引数は、単に呼び出すメソッドの引数の一覧です。
このメソッドは、2 つの異なるランタイム環境間で型を適切にマーシャリングします。マネージ オブジェクトをブラウザに渡す際には JavaScript 対応の型に変換し、このオブジェクトをブラウザから受け取る際には逆の処理を行います。
HtmlWindow には、注目に値するメソッドがもう 1 つあります。それは、CreateInstance メソッドです。
public ScriptObject CreateInstance(
string typeName,
params object[] args)
このメソッドを使用すると、指定した JavaScript オブジェクトのインスタンスを作成できます。型名のパラメータは、インスタンス化する JavaScript オブジェクトの名前を示します。内部では、このメソッドは指定のオブジェクトを作成する動的な JavaScript 関数を準備し、その関数を Silverlight から呼び出します。
ScriptObject xhr = HtmlPage.Window.CreateInstance("XMLHttpRequest");
CreateInstance メソッドは、JavaScript オブジェクトへのスクリプト可能な参照を取得するのに役立ちます。次に、Invoke メソッドを使用してそれをスクリプト実行します。これらの機能は、Silverlight 2 から同期呼び出しを行う必要がある場合に非常に役立ちます。ただ問題なのは、Silverlight 2 では同期呼び出しがサポートされていないことです。
同期呼び出しと Silverlight 2
Silverlight 2 にはリモート エンドポイントの呼び出しを行うためのさまざまな API が用意されていますが、これらの呼び出しはすべて非同期である必要があります。呼び出しはバックグラウンド スレッドで開始できますが、UI スレッドを同期させることはできません。同期オブジェクトで UI スレッドをブロックすると、UI スレッドが永久に停止してしまい、同期オブジェクトをリセットするコマンドがスレッドを再開できなくなります。コミュニティでは、Silverlight における同期呼び出しについて活発な議論が行われています。
それにもかかわらず、Silverlight API からのリモート エンドポイントの同期呼び出しは現時点ではサポートされていません。遅延が伴うことが判明しているためです。とはいえ、ブラウザ環境では同期呼び出しは重要な機能です。XmlHttpRequest をサポートしているすべてのブラウザでは、同期呼び出しがサポートされています。
XmlHttpRequest は、AJAX シナリオを実現するブラウザ オブジェクトです。このオブジェクトは、ブラウザのメカニズムを活用して、同じドメイン内のエンドポイントの呼び出しを行います。既定では、XmlHttpRequest は非同期で動作し、ほとんどの AJAX フレームワークがこの方法で XmlHttpRequest を使用します。ただし、XmlHttpRequest を同期的に動作するように構成するのは、非常に簡単です。
Silverlight から XmlHttpRequest のインスタンスを作成および制御することで、同期呼び出しを調整し、同期呼び出しによってコーディングが非常に簡単になるようなあらゆるシナリオを修正できます (私個人としては、Silverlight リモート呼び出しの "非同期のみ" の特性は好きではありません。非同期呼び出しだけで事足りるケースがほとんどですが、既存のフロント エンドを Silverlight に適合させる際には、同期呼び出しによって再設計の手間が大幅に省ける可能性があります。鍵となるのは設計だと言い出したのは私ですが、意識的に記述された同期呼び出しによって数時間分または数日分の作業を省けるのであれば、私も採用したいと思います)。
とは言うものの、Silverlight チームには、非同期のみのアプローチを推す正当な理由があります。リモート エンドポイントの同期呼び出しを行うと、Silverlight アプリケーションのユーザー インターフェイスがフリーズすることがあるのです。その場合、エンド ユーザーによるブラウザと Web アプリケーションの使用が妨げられてしまいます。同期呼び出しは技術的には可能ですが、すべてのアプリケーションとそのコンシューマの利便性を考え、チームはプラットフォームにおける同期呼び出しをネイティブにはサポートしていません。同様に、図 5 に示すような、XmlHttpRequest を使用して手動で記述された同期リモート呼び出しを用いることも推奨していません。

図 5 Silverlight 2 内から同期呼び出しを行う
private void Button1_Click(object sender,
System.Windows.RoutedEventArgs e)
{
string url = "...";
ScriptObject xhr = HtmlPage.Window.CreateInstance("XMLHttpRequest");
xhr.Invoke("open", "POST", url, false);
xhr.Invoke("setRequestHeader", "Content-Type",
"application/x-www-form-urlencoded");
// Prepare the body as the endpoint expects it to be
string body = "...";
xhr.Invoke("send", body);
string response = (string) 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 コントロールの 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");
// Modify the current content of the text block element
textBlock1.Text = "...";
JavaScript コードを使用して XAML ドキュメントのコンテンツにアクセスする場合は、途中で出現する XAML 要素への参照をページでキャッシュすることをお勧めします。そうすることで、同じ要素を探すために XAML ツリーを何度もトラバースする必要がなくなります。
JavaScript を使用して XAML ドキュメントのコンテンツにアクセスする方法は、Silverlight 2 ではやや古い方法であることに注意してください。ただし、Silverlight 1.0 を対象としている場合や、少なくとも XAML およびスクリプト コードだけから成る Silverlight アプリケーションを提供する場合には、引き続き有効な方法です。マネージ コードを使用して表示コンテンツに関する決定を行うことのできる開発者は、マネージ コードの代わりに JavaScript を使用した場合、ユーザー インターフェイスにはどのような変更を加えればよいのか、容易に思い付かないでしょう。
Silverlight 2 では、JavaScript コードを使用してマネージ コードを呼び出すことができます。この方法により、重要な処理を行うマネージ コードを使用した HTML ベースのページが存在するシナリオに対処できます。
JavaScript コードでは、スクリプト可能として事前に登録された、Silverlight 2 でホストされている任意のマネージ オブジェクトにスクリプトでアクセスできます。
guidHelper = new GuidHelper();
HtmlPage.RegisterScriptableObject("GuidTools", guidHelper);
RegisterScriptableObject メソッドは、登録されるオブジェクトの非公式の名前と可変インスタンスを受け取ります。最初の引数の文字列は、呼び出し元の JavaScript コードが登録されたオブジェクトを参照するために使用する名前です。
上記のコードに関連する次のコードは、GuidHelper サンプル クラスのメソッドを呼び出すために JavaScript から行う処理を示しています。
// Invoke a method on a scriptable managed object
var guid = $get("Silverlight1").content.GuidTools.Generate();
// Update the user interface with the results
$get("Random1").innerHTML = guid;
スクリプト可能なオブジェクトのパブリック名 (この場合は GuidTools) は、基になるオブジェクトに JavaScript からアクセスするためのメンバとして使用されます。疑似プロパティである GuidTools は、Silverlight プラグインの content プロパティによって公開されます。では、GuidHelper クラスを見てみましょう。
public class GuidHelper
{
[ScriptableMember]
public string Generate()
{
Guid g = Guid.NewGuid();
return g.ToString();
}
}
スクリプト可能なクラスのパブリック メソッドの ScriptableMember 属性は、メソッドを JavaScript から呼び出せることを示しています。クラスのすべてのパブリック メソッドがスクリプト可能な場合は、クラスで ScriptableType 属性を使用することで、スクリプト可能であることを示す属性をすべてのパブリック メソッドに自動的に拡張できます。
スクリプト可能として登録されたオブジェクトはマネージ コードを通じてインスタンス化され、既存のインスタンスは JavaScript に渡されます。すべてのマネージ型を JavaScript で直接インスタンス化できるわけではありません。
JavaScript からマネージ クラスのインスタンスを作成するには、まず JavaScript コードから作成できる型として、目的の型を登録する必要があります。
HtmlPage.RegisterCreateableType("StockPicker", typeof(Samples.Order));
最初の引数は、JavaScript 内から使用される型の別名を示します。Samples.StockPicker クラスのインスタンスの作成方法を次に示します。
var plugin = $get("Silverlight1");
var type = "Samples.StockPicker";
var inst = plugin.content.services.createObject(type);
Silverlight プラグインを取得し、content.services プロパティにアクセスしてから、createObject メソッドを呼び出します。createObject の引数は、インスタンス化するマネージ型の名前を示す文字列です。
メソッドのシグネチャに複雑なカスタム型のあるスクリプト可能な型は、その型のファクトリとしても機能します。例として、次のメソッドを持つ Customer マネージ型を考えてみましょう。
Order 型はどうなるでしょうか。作成可能として宣言する必要はあるでしょうか。いいえ、必ずしも宣言する必要はありません。RegisterCreateableType の呼び出しを省略した場合、スクリプト可能な型 (その型を必要とするスクリプト可能なメンバのある型) はファクトリとしてのみ使用できます。つまり、Customer がスクリプト可能として登録されており、Add メソッドがスクリプト可能である場合は、型を作成可能として宣言することなく JavaScript から Order をインスタンス化できます。次のコードは正常に動作します。
var customer = $get("Silverlight1").content.Customer;
var order = customer.createManagedObject("Order");
order.ID = 1;
o.OrderDate = new Date();
customer.Add(order);
マネージ型は、ブラウザ オブジェクトとしてラップされた JavaScript にマーシャリングされます。ラッパー ブラウザ オブジェクトには、JavaScript から呼び出す有効なメソッドの一覧が含まれます。この一覧には、スクリプト可能として宣言されたすべてのメソッドと createManagedObject が含まれます。メソッドに対する呼び出しは、マネージ コードの対応するメソッドを呼び出すことで解決されます。
クロスドメイン アクセスと Silverlight プラグイン
パブリック マネージ API を公開する Silverlight アプリケーションは、クロスドメイン アクセスの対象となります。クロスドメイン アクセスがセキュリティ上危険である場合や、単に望ましくない機能である場合は、対策を講じる必要があります。
Silverlight プラグインの含まれるページがフレーム内にホストされている場合、ホスト ページは Silverlight アプリケーションとは別のドメインに存在可能です。つまり、ホスト ページ (クロスドメイン) の JavaScript コードは、フレームを通じて Silverlight プラグインにアクセスし、任意のパブリック マネージ オブジェクトにスクリプトでアクセスできます。
Silverlight コンテンツへのクロスドメイン アクセスは、無効にすることも、完全に有効にすることもできます。また、スクリプトに制限することも可能です。既定では無効になっています。クロスドメイン アクセスを制御するには、図 6 に示すように、Silverlight アプリケーションのマニフェスト ファイルの Deployment ノードで ExternalCallersFromCrossDomain を使用します。このマニフェスト ファイルは、Silverlight プロジェクトをコンパイルしたときに Visual Studio 2008 によって生成されるファイルです。

図 6 Silverlight 2 アプリケーションのサンプル マニフェスト ファイル
<Deployment xmlns="http://schemas.microsoft.com/client/2007/deployment"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
EntryPointAssembly="BrowserFun"
EntryPointType="BrowserFun.App"
RuntimeVersion="2.0.30523.6"
ExternalCallersFromCrossDomain="FullAccess">
<Deployment.Parts>
<AssemblyPart x:Name="BrowserFun"
Source="BrowserFun.dll" />
<AssemblyPart x:Name="System.Windows.Controls.Extended"
Source="System.Windows.Controls.Extended.dll" />
</Deployment.Parts>
</Deployment>
1 つ以上の Silverlight プラグインが含まれているページをブラウザで読み込むとき、ブラウザ プロセスごとに 1 つの CLR インスタンスを取得することになります。ただし、Silverlight プラグインの実行中の各インスタンスは、独自の AppDomain を取得します。プラグイン間で何も共有していない場合も通信は可能ですが、その場合、コードを通じてちょっとしたテクニックを使う必要があります。
このテクニックでは、実質的に、ブラウザの相互運用層を中継手段として使用します。このとき、あるプラグインがマネージ インターフェイスを公開し、別のプラグインがそのインターフェイスに接続してメソッドを呼び出します。
そのため、次に示すように、いずれかのプラグインがいくつかのスクリプト可能なメンバを定義します。
public partial class Page : UserControl
{
public Page()
{
InitializeComponent();
HtmlPage.RegisterScriptableObject(
"Action", new ActionPageCommand());
}
}
ActionPageCommand クラスには、プラグインが外部呼び出し元に対して公開するすべてのメソッドが含まれます。その例を次に示します。
[ScriptableType]
public class ActionPageCommand
{
public int GetRandomNumber()
{
Random rnd = new Random();
return rnd.Next(0, 100);
}
}
プラグインのスクリプト可能なメンバは、ページの JavaScript コードに直接表示されます。図 7 に、公開されている Silverlight プラグイン インターフェイスの JavaScript ラッパーを示します。他のプラグインのインターフェイスに接続しているプラグインは、図の JavaScript ラッパーのインスタンスを作成できます。
ScriptObject so = HtmlPage.Window.CreateInstance("ActionBar");
object result = so.Invoke("invokeGetRandomNumber");

図 7 他の Silverlight プラグインへの公開
<script type="text/javascript">
var ActionBar = function()
{}
function invokeGetRandomNumber$Impl()
{
var plugin2Services = $get("Silverlight2").content;
var results = plugin2Services.Action.GetRandomNumber();
return results;
}
ActionBar.prototype =
{
invokeGetRandomNumber: invokeGetRandomNumber$Impl
}
</script>
もっと直接的な方法で Silverlight プラグインを別のプラグインから呼び出すことはできないでしょうか。もちろん可能です。次のコードを使用してください。
HtmlElement plugin = HtmlPage.Document.GetElementById("Silverlight2");
var content = (ScriptObject) plugin.GetProperty("content");
var action = (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 Esposito は、IDesign 社のアーキテクトであり、『Microsoft .NET: Architecting Applications for the Enterprise』(Microsoft Press、2008 年) の共著者です。Dino はイタリアに在住し、世界各国で開催される業界のイベントで頻繁に講演しています。ブログは
weblogs.asp.net/despos で読むことができます。