Sunday, April 3, 2011

Doing the dynamic thing (Part II)

After the basic intro given in the first part of this series, it is time for something more advanced. It won’t be rocket science, yet, but you will get more insight into what dynamic coding can do for you.
As mentioned in the previous post, dynamic programming can help you a lot when working with COM objects. I will give an example of this for JavaScript objects in a small Silverlight application. In the next post I will extend this simple example and I will explain interaction with Excel. But first, let’s talk about JavaScript.
Interacting with JavaScript might be something you need in real life. For instance if you have a Silverlight app that is hosted on a page that relies on JQuery a lot. JQuery can provide you with objects your SilverLight application does not know about. How can you interact with these objects if your managed code doesn’t know their type? The answer is to utilize dynamic.
To illustrate this, suppose we have a service running that can give us data about persons. A Person being just a DTO class with a FirstName and LastName property. Plain and simple.

[ServiceContract]
public interface IDataService
{
    [OperationContract]
    List<Person> GetData();
}
 
[DataContract]
public class Person
{
    [DataMember]
    public string FirstName { getset; }
    [DataMember]
    public string LastName { getset; }
}

I also have a HTML page that includes a SuperPerson object. A SuperPerson being the same as a Person, but with one extra property, 'somethingextra'.
function SuperPerson(firstname, lastname, somethingextra)
{
    this.firstname = firstname;
    this.lastname = lastname;
    this.somethingextra = somethingextra; 
}
There is also a JavaScript function that can create one of these SuperPerson objects with the extra data.
function GetSuperPerson(firstname, lastname, somethingextra) {
    var person = new SuperPerson(firstname, lastname, somethingextra);
    return person; 
}
To illustrate how to work with such a JavaScript object, I’ve made a small Silverlight application that receives a list of person objects from the WCF service described above. The Silverlight application will display this list of persons in a DataGrid control. 
public MainPage()
{
    InitializeComponent();
    DataService.DataServiceClient client = new DataService.DataServiceClient();
    client.GetDataCompleted += new EventHandler<DataService.GetDataCompletedEventArgs>(client_GetDataCompleted);
    client.GetDataAsync();
}
 
void client_GetDataCompleted(object sender, DataService.GetDataCompletedEventArgs e)
{
    dataGrid1.ItemsSource = e.Result;
}
This is the basic set up I will use in this example. The Silverlight application will be hosted on an HTML page that has the above mentioned JavaScript code present. 
In this example, to keep it simple, I will build a HTML table. It will be the Silverlight code that builds this table, not the JavaScript code. For this, first thing will be to get hold of a specific div tag on the HTML page in which we will build our table. Silverlight lets you interact with the hosting page through the HtmlPage class. This class gives you a Document property that behaves pretty much the same as the document object in JavaScript, which makes it very easy to use.

HtmlElement theDiv = HtmlPage.Document.GetElementById("dynamicDiv");
HtmlElement table = HtmlPage.Document.CreateElement("table");
Next, we will iterate over all Person objects in the DataGrid.

foreach (DataService.Person p in dataGrid1.ItemsSource)
{
For each of these Person objects we will execute the GetSuperPerson function, present in the JavaScript of the hosting page. Since the Silverlight application has no knowledge of the SuperPerson type, we will use the capabilities of .NET 4.0.

    dynamic superperson = HtmlPage.Window.Invoke("GetSuperPerson", p.FirstName, p.LastName, System.DateTime.Now.Millisecond);
With the dynamic keyword in place our compiler does not complain about the SuperPerson type it does not know about. And since we know a SuperPerson has a firstname, lastname and somethingextra, we can interact with it though these operations with no problem.

    HtmlElement row = HtmlPage.Document.CreateElement("tr");
    dynamic td = HtmlPage.Document.CreateElement("td");
    td.innerHTML = superperson.firstname;
    row.AppendChild(td);
    td = HtmlPage.Document.CreateElement("td");
    td.innerHTML = superperson.lastname;
    row.AppendChild(td);
    td = HtmlPage.Document.CreateElement("td");
    td.innerHTML = superperson.somethingextra;
    row.AppendChild(td);
    table.AppendChild(row);
}
 
theDiv.AppendChild(table);
Two things are notable about the code above. First of all, I use firstname and lastname, not FirstName and LastName (mind the casing). This is because we are interacting with the JavaScript object, not with the original Person object. I purposely used other casing in the JavaScript code to make my point on this clear.
A second thing you may have noticed is a second use of the dynamic keyword. When I create a new td element, I do not use the HtmlElement type (which is valid for a td tag), but instead I use the dynamic keyword. I do this because I know for sure it is possible to call the innerHtml operation on a td tag. The HtmlElement, however, does not have this operation present (you can check this in your intellisense). There is also no specific type for a td element that does have this operation present. With dynamic there is no problem at all to call innerHtml, not at compile time and not at run time.
There you have it, two good reasons to use dynamic when you're interacting with JavaScript. You can get around unknown objects in managed code and you can call operations managed code has no clue of.
Be ready for the next two parts, first on dynamic and Excel and next about ExpandoObjects (this is where the fun begins!).

1 comment: