Calling Silverlight Methods using JavaScript
I’m going to create a fictional business application that uses JavaScript to call a Silverlight application to search for Australian post codes. The benefit of calling Silverlight code from JavaScript is that the calls are made asynchronously, so there is no page refresh for the end user.
To begin with open Visual Studio 2008 and choose File > New > Project > Silverlight > Silverlight Application. Navigate to the Silverlight application and open the Page.xaml file. Copy the following xaml into the file:
<UserControl x:Class="SilverlightApplication1.Page"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
Width="0" Height="0">
<Grid x:Name="LayoutRoot" Background="White">
</Grid>
</UserControl>
In the code above, notice that the width and height of the UserControl is set to zero and the Grid is empty. This is because the user will not see any Silverlight control’s, it will all be plain HTML and JavaScript. The trick to making your Silverlight code accessible from JavaScript is via the HtmlPage.RegisterScriptableObject method. This method registers managed objects for scriptable access by JavaScript code. Copy the following code into the Page constructor:
C#
public Page()
{
InitializeComponent();
HtmlPage.RegisterScriptableObject("SilverlightPostCode", this);
}
VB.NET
Public Sub New()
InitializeComponent()
HtmlPage.RegisterScriptableObject("SilverlightPostCode", Me)
End Sub
The first parameter, SilverlightPostCode, will be referenced in JavaScript to access the Page class. The second parameter is used to declare which object you want accessible, and this or Me means the current object.
The work in the Silverlight project is done for now. Switch back to the web application and add a new class. Name it AddressInfo and add the following code:
C#
public class AddressInfo
{
public string Suburb { get; set; }
public string State { get; set; }
public string Postcode { get; set; }
}
VB.NET
Public Class AddressInfo
Private privateSuburb As String
Public Property Suburb() As String
Get
Return privateSuburb
End Get
Set(ByVal value As String)
privateSuburb = value
End Set
End Property
Private privateState As String
Public Property State() As String
Get
Return privateState
End Get
Set(ByVal value As String)
privateState = value
End Set
End Property
Private privatePostcode As String
Public Property Postcode() As String
Get
Return privatePostcode
End Get
Set(ByVal value As String)
privatePostcode = value
End Set
End Property
End Class
This class will hold the data that relates to the post code search. The next step is to add a web reference to the following URL:
This is a free service for querying Australian post codes. Now that we have our source of data, create a Silverlight-enabled WCF Service to retrieve the data. Call it PostCodeService:
Add the following code to the WCF service:
C#
[ServiceContract(Namespace = "")]
[AspNetCompatibilityRequirements(RequirementsMode = AspNetCompatibilityRequirementsMode.Allowed)]
public class PostCodeService
{
[OperationContract]
public List<AddressInfo> DoWork(string code)
{
List<AddressInfo> addresses = new List<AddressInfo>();
PostcodeService postCode = new PostcodeService();
DataSet ds = postCode.GetPostcodeAndSuburbForAustralia(string.Empty, string.Empty, code);
if (ds.Tables[0] != null)
{
foreach (DataRow item in ds.Tables[0].Rows)
{
addresses.Add(new AddressInfo()
{
Suburb = item[0] as string,
Postcode = item[1] as string,
State = item[2] as string
});
}
}
return addresses;
}
}
VB.NET
<ServiceContract(Namespace := ""), AspNetCompatibilityRequirements(RequirementsMode := AspNetCompatibilityRequirementsMode.Allowed)> _
Public Class PostCodeService
<OperationContract> _
Public Function DoWork(ByVal code As String) As List(Of AddressInfo)
Dim addresses As New List(Of AddressInfo)()
Dim postCode As New PostcodeService()
Dim ds As DataSet = postCode.GetPostcodeAndSuburbForAustralia(String.Empty, String.Empty, code)
If ds.Tables(0) IsNot Nothing Then
For Each item As DataRow In ds.Tables(0).Rows
addresses.Add(New AddressInfo() With {.Suburb = TryCast(item(0), String), .Postcode = TryCast(item(1), String), .State = TryCast(item(2), String)})
Next item
End If
Return addresses
End Function
End Class
In the code above, the return type is a generic List<AddressInfo> collection. I always try to use strongly typed objects whenever I code, and the data returned from the web service is a DataSet, so if there is data found relating to the post code, I am using a foreachloop to enumerate through each record and adding it to an AddressInfo object. This collection is what will be consumed by the Silverlight application and displayed to the end user.
Next let’s add the HTML to the page so that we can see our UI. Open the SilverlightApplication1TestPage.aspx and add the following code:
<h1>Search Australian Postcodes</h1>
<input type="button" onclick="loadPicturesSilverlight();" value="Search" />
<input type="text" name="txtPostCode" maxlength="4" />
<div id="sampleDiv">
</div>
<div>
<asp:Silverlight ID="Xaml1" runat="server" Source="~/ClientBin/SilverlightApplication1.xap"
MinimumVersion="2.0.31005.0" Width="100%" />
</div>
The button in the code above will execute a JavaScript function called loadPicturesSilverlight. We will create that last. The text box will allow the user to type in a post code. A div named sampleDiv will hold the results from the search. Very simple stuff indeed!
The web application is done for now. Switch back to the Silverlight application and add a new Service Reference to the WCF service we just created:
Open the Page.xaml.cs or Page.xaml.vb file and add the following code:
C#
[ScriptableMember(ScriptAlias = "SearchPostCode")]
public void InternalNameOnly(string postCode)
{
PostCodeServiceReference.PostCodeServiceClient client = new PostCodeServiceReference.PostCodeServiceClient();
client.DoWorkCompleted += new EventHandler<SilverlightApplication1.PostCodeServiceReference.DoWorkCompletedEventArgs>(client_DoWorkCompleted);
client.DoWorkAsync(postCode);
}
VB.NET
<ScriptableMember(ScriptAlias := "SearchPostCode")> _
Public Sub InternalNameOnly(ByVal postCode As String)
Dim client As New PostCodeServiceReference.PostCodeServiceClient()
AddHandler client.DoWorkCompleted, AddressOf client_DoWorkCompleted
client.DoWorkAsync(postCode)
End Sub
The code in the method above does a normal asynchronous call to the WCF service. The code above is what we will execute when the user clicks the HTML button in the web application. The magic behind this is decorating the method with the ScriptableMember attribute. The ScriptableMember attribute indicates that this method is accessible by JavaScript callers. The ScriptAlias property is optional and it means that this method will be referred to in JavaScript as SearchPostCode, NOT InternalNameOnly. This is the way to hide method names from JavaScript. Next add the following code for the EventHandler:
C#
void client_DoWorkCompleted(object sender, SilverlightApplication1.PostCodeServiceReference.DoWorkCompletedEventArgs e)
{
HtmlElement parent = HtmlPage.Document.GetElementById("sampleDiv");
parent.SetAttribute("innerHTML", string.Empty);
ObservableCollection<PostCodeServiceReference.AddressInfo> addresses = e.Result;
if (addresses.Count > 0)
{
HtmlElement ul = HtmlPage.Document.CreateElement("ul");
foreach (PostCodeServiceReference.AddressInfo item in addresses)
{
HtmlElement li = HtmlPage.Document.CreateElement("li");
li.SetAttribute("innerHTML", string.Format("{0} ({1})", item.Suburb, item.State));
ul.AppendChild(li);
}
parent.AppendChild(ul);
}
else
{
HtmlElement p = HtmlPage.Document.CreateElement("p");
p.SetAttribute("innerHTML", "No records found");
parent.AppendChild(p);
}
}
VB.NET
Private Sub client_DoWorkCompleted(ByVal sender As Object, ByVal e As SilverlightApplication1.PostCodeServiceReference.DoWorkCompletedEventArgs)
Dim parent As HtmlElement = HtmlPage.Document.GetElementById("sampleDiv")
parent.SetAttribute("innerHTML", String.Empty)
Dim addresses As ObservableCollection(Of PostCodeServiceReference.AddressInfo) = e.Result
If addresses.Count > 0 Then
Dim ul As HtmlElement = HtmlPage.Document.CreateElement("ul")
For Each item As PostCodeServiceReference.AddressInfo In addresses
Dim li As HtmlElement = HtmlPage.Document.CreateElement("li")
li.SetAttribute("innerHTML", String.Format("{0} ({1})", item.Suburb, item.State))
ul.AppendChild(li)
Next item
parent.AppendChild(ul)
Else
Dim p As HtmlElement = HtmlPage.Document.CreateElement("p")
p.SetAttribute("innerHTML", "No records found")
parent.AppendChild(p)
End If
End Sub
The above code will run once the WCF service has finished executing. If results are found, the code will dynamically create one HTML unordered list (UL) and enumerate through each AddressInfo item in the collection and create a list item (LI). Once it is finished it will add the HTML to the sampleDiv. However, if no data is found it will dynamically create a paragraph and display No records foundto the user.
The last step is to switch back to the web application and add a new JavaScript file to the project. Add the following code to the file:
function loadPicturesSilverlight() {
// Get the silverlight server control
var slPlugin = document.getElementById("Xaml1");
var content = slPlugin.Content;
var postCode = document.getElementById("txtPostCode").value;
if (postCode.match(/^\d{4}$/))
{
// Use the SilverlightPostCode that was exposed by the HtmlPage.RegisterScriptableObject method
// Call SearchPostCode as this is the ScriptAlias for the method.
content.SilverlightPostCode.SearchPostCode(postCode);
}
else {
alert("Enter a 4 digit postcode");
}
}
In the code above, the important variable is content because it gets a reference to the Silverlight Content. From there it parses the text from the text box to ensure the input from the user is four (4) numbers, as Australian post codes must meet that requirement. Once the data is correct, the code can now call Silverlight methods directly via the following line:
content.SilverlightPostCode.SearchPostCode(postCode);
If you remember earlier in the article, we setup the SilverlightPostCode code in the HtmlPage.RegisterScriptableObject method in the Page.xaml file. That is the code that enables managed objects to be accessible by JavaScript callers. If you run the code and search for an Australian post code, for example 3000, the code will be initiated from JavaScript but Silverlight will create the resulting HTML. Pretty cool if you ask me!
The source code of this article can be downloaded from here
This article has been editorially reviewed by Suprotim Agarwal.
C# and .NET have been around for a very long time, but their constant growth means there’s always more to learn.
We at DotNetCurry are very excited to announce The Absolutely Awesome Book on C# and .NET. This is a 500 pages concise technical eBook available in PDF, ePub (iPad), and Mobi (Kindle).
Organized around concepts, this Book aims to provide a concise, yet solid foundation in C# and .NET, covering C# 6.0, C# 7.0 and .NET Core, with chapters on the latest .NET Core 3.0, .NET Standard and C# 8.0 (final release) too. Use these concepts to deepen your existing knowledge of C# and .NET, to have a solid grasp of the latest in C# and .NET OR to crack your next .NET Interview.
Click here to Explore the Table of Contents or Download Sample Chapters!
Was this article worth reading? Share it with fellow developers too. Thanks!
Malcolm Sheridan is a Microsoft awarded MVP in ASP.NET, a Telerik Insider and a regular presenter at conferences and user groups throughout Australia and New Zealand. Being an ASP.NET guy, his focus is on web technologies and has been for the past 10 years. He loves working with ASP.NET MVC these days and also loves getting his hands dirty with jQuery and JavaScript. He also writes technical articles on ASP.NET for SitePoint and other various websites. Follow him on twitter @
malcolmsheridan