First, set up the Windows Forms client. I'll use Form1, the hardest working form of them all. I simply drag the WebBrowser control onto the form. In its constructor, I set a few properties needed for having the embedded WebBrowser:
public partial class Form1 : Form {
public Form1() {
InitializeComponent();
webBrowser1.AllowWebBrowserDrop =
webBrowser1.IsWebBrowserContextMenuEnabled =
webBrowser1.WebBrowserShortcutsEnabled = false;
webBrowser1.ObjectForScripting = this;
webBrowser1.Navigate("http://<url of Silverlight application>");
}
I also added a property and a method in the Windows Form for the hosted Silverlight application to call:
public void ButtonClicked(string message) {
MessageBox.Show("ButtonClicked: " + message);
}
public string Test {
set {
MessageBox.Show("Test: " + value);
}
}
In the Silverlight application, use managed code like the following (e.g. in a click event handler, load event, etc.) to call the custom methods on the hosting form (note that you need a reference to System.Windows.Browser to get to the browser):
using System.Windows.Browser;
/* ... */
string msg = "Clicked at " + DateTime.Now.ToString();
HtmlWindow w = HtmlPage.Window;
ScriptObject o = (ScriptObject)w.GetProperty("external");
if (null != o)
{
try
{
// Set a property value on the host form
o.SetProperty("Test", msg);
// Call a method on the host form
o.Invoke("ButtonClicked", new object[] { msg });
}
catch { }
}
Note the catch block: I haven't yet discovered how to detect if the Silverlight application is being hosted in a WebBrowser from inside (if it is being run directly in a browser, the 'external' object exists but invoking custom properties and methods will fail).
Calling methods in the Silverlight application from the WebBrowser control is a bit more complex and involves a few steps:
1) Stage the Silverlight application and methods for scripting
In App.xaml, you must register each Silverlight page for scripting support. For example, if I have a page called Page, I need code like the following:
Page p = new Page();
this.RootVisual = p;
HtmlPage.RegisterScriptableObject("Page", p); // The identifier ("Page") here is arbitrary and refers to your key for identifying the object from script
Now that I've done this, I have to mark each Silverlight page class with the ScriptableType attribute:
[ScriptableType()]
public partial class Page : UserControl
{
/* ... */
Also, each public method I intend to call in the marked class from external script must be marked with the attribute:
[ScriptableMember()]
public void SetSearchText(string msg)
{
/* ... */
2) Set up scripting support in the web page hosting the Silverlight application
The WebBrowser control can call script methods within the page, but doesn't seem to be able to call the Silverlight control object directly (I made several attempts to do this but was unsuccessful). What I ended up doing that worked was to set up a script method corresponding to each method I want to call within the Silverlight application.
First, alter the tag on the Silverlight control so that it calls a script method of yours when it loads:
<div style="height:100%;">
<asp:Silverlight ID="Xaml1" runat="server" Source="~/ClientBin/MySilverlightApp.xap" Version="2.0" Width="100%" Height="100%" OnPluginLoaded="pluginLoaded" />
</div>
Now, add a script block in the HEAD tag with a method corresponding to the above to store a reference to the Silverlight control when it loads:
<head runat="server">
<title>Test Page For DiggSample</title>
<script type="text/javascript">
var scControl;
function pluginLoaded(sender) {
scControl = sender.get_element();
}
</script>
...
Next, add one script method for each method you marked exposed within the Silverlight application. Note the formatting (you must call it in the form: objectName.Content.<name you chose as the Silverlight script object identifier>.method(arguments)):
<script type="text/javascript">
function SetSearchText(msg) {
scControl.Content.Page.SetSearchText(msg);
}
</script>
Finally, from within the Windows Forms application, you can call the method you exposed via the WebBrowser control as follows:
private void btnSetSearchText_Click(object sender, EventArgs e)
{
webBrowser1.Document.InvokeScript("SetSearchText", new string[] { "foo" });
}
That's it--you should be able to have two-way communication between the Windows Forms application and the Silverlight application hosted via its WebBrowser control.