Programming HTML with C#

In my last post, I promised to provide a more detailed technical explanation of how you can use the .NET capabilities of Silverlight with HTML, allowing full access to the HTML DOM from managed code as well as providing a means for client-side JavaScript to call into a .NET library.

All the magic necessary to accomplish this is contained in a new .NET namespace introduced with Silverlight 1.1, called System.Windows.Browser. Here you'll find a number of classes that enable you to manipulate the DOM, in particular HtmlPage (representing the parent browser); HtmlDocument (the root element of the DOM) and HtmlElement (for manipulating the individual elements within a page). Using these methods, it's possible to create an HTML page where all the underlying code logic is written in a language like C# without having to write so much as an event handler in JavaScript.

Let's walk through the process. I'm going to assume that you've got a machine already set up with Visual Studio 2008 Beta 1 (previously codenamed "Orcas") and the Silverlight project template add-on.

  1. Firstly, create a new Silverlight Project. Visual Studio creates a test HTML page with built-in code to instantiate the Silverlight runtime, along with a XAML page and matching C# class.

  2. Within the TestPage.html, you're going to add the following HTML code just underneath the <body> element:

     <form action="">
       <h1>Date Parsing</h1>
       <p>Input: <input type="text" id="input" />
          <input type="button" id="parseDate" value="Parse >>" />
          <input type="text" id="output" size="50" />
       </p>
       <h1>Browser Information</h1>
       <textarea id="browserCaps" cols="100" rows="6"></textarea>
       <h1>Adding Style to the Page</h1>
       <input type="button" id="addStyle" value="Style Me!" />
    </form>
    

    This creates a few elements we're going to use to highlight various elements of Silverlight / HTML DOM interaction. You can strip out the onload event in the default HTML, by the way - we won't need that.

  3. We're not going to use XAML to display any content, so in the Page.xaml file, set both the Width and Height for the Canvas object to 0.

  4. Let's write some C# code, which we're going to do in the Page.xaml.cs file. Firstly, let's declare a class variable HtmlDocument, which is going to store a handy reference to the currently loaded document:

     HtmlDocument hdoc;
    

    Let's also add a declaration for the System.Windows.Browser namespace:

     using System.Windows.Browser;
    

  5. In the Page_Loaded event, we're going to hook up the HTML buttons to some event handlers. This is the equivalent of adding an onClick attribute in HTML, but we're going to do it programmatically with C#:

     hdoc = HtmlPage.Document;
    hdoc.GetElementByID("parseDate").AttachEvent("onclick",
       new EventHandler<HtmlEventArgs>(this.OnParseDateButtonClick));
    hdoc.GetElementByID("addStyle").AttachEvent("onclick",
       new EventHandler<HtmlEventArgs>(this.OnAddStyleButtonClick));
    

  6. We're also going to use the Page_Loaded event to get some information about the currently loaded browser and initialize two textboxes:

     string browserCaps =
       "Name: " + HtmlPage.BrowserInformation.Name + "\n" +
       "Platform: " + HtmlPage.BrowserInformation.Platform + "\n" +
       "User Agent: " + HtmlPage.BrowserInformation.UserAgent + "\n" +
       "Cookies Enabled: " + HtmlPage.BrowserInformation.CookiesEnabled;
    
    hdoc.GetElementByID("input").SetProperty("value", DateTime.Now.ToString("g"));
    hdoc.GetElementByID("browserCaps").SetProperty("value", browserCaps);
    

    Note that HtmlPage contains a bunch of useful static properties that provide access to things like the browser info, cookies, the HTTP query string, as well as the URL of the page currently loaded. You can also call a static Navigate() method on the same class to have the browser load a new URL. You'll see that we're also using the HtmlPage.Document object to find named elements in the HTML page and set properties on them using managed code.

  7. As you can see from the code we added in step 5, we're going to need to create two event handlers. The first one is going to grab an unstructured date/time string from an input field and parse it into a well-formed RFC 1123 string. If it fails, it will display an error in the output textbox, otherwise it will display the parsed string. Note that the args parameter is of type HtmlEventArgs, which contains many useful properties that can be used for handling mouse and keyboard input.

     public void OnParseDateButtonClick(object sender, HtmlEventArgs args)
    {
       string output;
       try
       {
          string input = hdoc.GetElementByID("input").GetProperty<string>("value");
          DateTime inputAsDate = DateTime.Parse(input);
          output = inputAsDate.ToString("R"); // RFC 1123 string
       }
       catch (FormatException e)
       {
          output = "Error: " + e.Message;
       }
    
       hdoc.GetElementByID("output").SetProperty("value", output);
    }
    

  8. Lastly, we'll add a method that shows basic CSS functionality available to you. When you click on the last button on the page, it changes the color of each heading to dark blue using CSS. Here, we're iterating through a collection of <h1> elements, generated by calling the GetElementsByTagName() method, which should be very familiar to you if you've done client-side JavaScript programming before.

     public void OnAddStyleButtonClick(object sender, HtmlEventArgs args)
    {
       HtmlElementCollection hec = hdoc.GetElementsByTagName("h1");
       foreach (HtmlElement h1 in hec)
       {
          h1.SetStyleAttribute("color", "#000066");
       }
    }
    

  9. We're done. Save all the files, build the project and hit F5 to run the HTML page. You should be able to type in an unstructured date to the input field (e.g. "3/1/4 5pm") and when you hit parse, have it converted to a well-formed string (something like "Mon, 01 Mar 2004 17:00:00 GMT", dependent on your current time zone and thread culture). The browser information section should show the browser, platform, version and whether cookies are enabled, and when you click the "Style Me!" button, it should change the headings to blue.

If you've got the Silverlight 1.1 Alpha on your machine, you can browse straight to the finished page to see it in action. I've also made the source code available for the above walkthrough, in case you aren't able to complete the steps above for whatever reason.

I've not talked in this post about calling .NET code from JavaScript, because I wanted to focus on a JavaScript-free scenario. However, you'll find a great quickstart guide to this latter topic online at the silverlight.net community site.

Have fun with this!