Web-based User Interfaces for C# Applications

My main project is an Excel add-in, written in C# using ExcelDNA. This add-in includes some User Interfaces available from contextual menus or from the fluent ribbon. These UIs are built with C# using WinForms or WPF. The purpose of this blog is not to discuss about pros and cons of these technologies, but to explore an alternative: web-based clients (HTML5).

Why web-based clients?

Web-based client applications have become quite popular over the last few years, with help to HTML5 and some key technologies such as JQuery or AngularJS. Therefore, various frameworks, templates, styles have appeared and it has become easier to develop sophisticated client applications in a web browser than using heavy development frameworks like WPF. We might expect better performance too, but this would need a specific study.
Note: in the context of an Excel add-in, running the UI in a separate process will provide extra resources to the developer. Excel is quite greedy with the memory, and it is easy to make the 32bits version of Excel crash because of that.

Basic Architecture

The Excel add-in will host a server component to interact through web sockets with the client. It may or may not host the web server to respond to HTTP requests (in order to serve HTML documents and other related files – css, js, jpg, etc.); the web server might also be a separate application.
The web browser will receive and display HTML documents, and one or more JavaScript files will take care of communicating with the Excel add-in in order to maintain the real-time updates.

Web Based UI

 

Illustration

Let’s assume we have an Excel add-in built with ExcelDNA which provide a fabulous function to return the cry of a given animal. Something like this:

public static string MakeNoise(string animal)
{
switch (animal.ToLower())
{
case "duck":
return "Quack";
case "dog":
return "Woof";
case "lion":
return "Roar";
case "pig":
return "Oink";
default:
return "Undefined noise";
}
}

I will not detail here how to create such an add-in. Please refer to the ExcelDNA page for tutorials.

What I want to achieve is a client application which counts the number of cries for each animal and allows me to add some animal cries in the spreadsheet. This would be a kind of monitor of my add-in, available from a web browser.

I will not detail here how to create such an add-in. Please refer to the ExcelDNA page for tutorials.

What I want to achieve is a client application which counts the number of cries for each animal and allows me to add some animal cries in the spreadsheet. This would be a kind of monitor of my add-in, available from a web browser.

Serving the documents

Since the UI is based on HTML5, we need to build a web server to respond to http requests and send the content of the documents. The server can be part of the Excel add-in and might only respond to request from localhost.

The .Net framework, since v2.0, offers an HttpListener class to help us achieve this. Please refer to this page from the MSDN documentation. A nice example is available on this blog, although its WebServer class assumes that the content to be sent is always HTML format. It will not work with css, js, etc. Personally, I would change the signature of the “_responderMethod” delegate to this:
Func<HttpListenerRequest, Tuple<byte[], string>> responderMethod;

The first parameter of the Tuple is of type byte[] and is supposed to be the encoded content to send back to the client. The second parameter is a string representing the type of data. Some example of types:

  • “text/html”
  • “text/css”
  • “text/js”
  • “text/png”
  • “text/jpg”
  • “text/gif”

Encoding the content is quite straightforward. Let’s assume that you have a path to a file, then you might just call File.ReadAllBytes(thepath).

Finally, my web server will contain directories with files to read, encode and send back to the client. For instance if the client requests “myServerUrl/dir1/dir2/somefile.js”, then the server will look for the folder “dir1”, then for the sub-folder “dir2” and finally load the file “somefile.js” and return the encoded string.

Communication between the browser and the server

An efficient way that I found to make my web browser and my .Net server talk to each other is using web sockets. This protocol is natively supported by all the recent web browsers. In C#, Websocket-sharp is a convenient library to do this.

The web-based client will need to know the web socket port of the server in order to communicate with it. This port could be loaded by a simple http GET request and stored into the sessionStorage cache. Finally, I will create a common JavaScript file to simplify the communications in my HTML/JavaScript code:

function getURL(extension) {
if (window.sessionStorage.getItem("mywebsocketport") == null) {
var xmlHttp = new window.XMLHttpRequest();
xmlHttp.open("GET", "WebSocket", false);
xmlHttp.send(null);
window.sessionStorage.setItem("mywebsocketport", xmlHttp.responseText);
}

return "ws://localhost:" + window.sessionStorage.getItem("mywebsocketport") + "/" + extension;
}

function openWebSocket(extension, message, onmessage) {
var address = getURL(extension);

var ws = new window.WebSocket(address);

ws.onopen = function () {
ws.send(message);
};

ws.onmessage = onmessage;
}

This new function “openWebSocket” will be quite helpful. The “onmessage” parameter is an optional callback to be invoked when the server responds. The “message” is a string representing a serialized json message. The “extension” is a keyword for the server to understand how to read the message.

Let’s come back to our example. The client can send an empty message, with the extension “ListenToAnimals” and a callback function “updatePageContent” to update the HTML content in order to display the animals.
openWebSocket("ListenToAnimals", '', updatePageContent);

Then the server will receive the message, will populate the list of animals with their counts, and will send a response back to the client. The server will keep updating the client with new counts, as long as the client has not closed the connection.

Illustration:

    1. User opens the UI from a button in a custom ribbon in Excel. The add-in will then open the URL http://localhost:8080/MyexcelAddin/Index.html. The content of my HTML page is empty:

Web Based UI

    1. The client automatically subscribes to the animals as described above.
    2. User enters MakeNoise(“dog”) in cell A1 in Excel. The client receives the notification and updates

Web Based UI

Now let’s add a field in the client UI where we can populate a range address from Excel by just selecting it. We can find how to achieve this in C# here: http://localhost:8080/MyexcelAddin/Index.html. But instead of updating some .Net UI, we will send the data back to the client.

Illustration: let’s assume the client looks like this

Web Based UI

  1. User puts the cursor on the “RefEdit” text box next to “at”.
  2. User selects a range in Excel (for example “C5”)
  3. Then the range appears in the “RefEdit” text box.

Web Based UI

Finally the user can click on “Add animal” which will send a new message to the server in order to make it insert “=MakeNoise(“cat”)” into the cell C5.

Web Based UI

Therefore all kind of interactions are possible between the client and the server. The client can:

  • Request some information from the server
  • Send data to the server and modify its behaviour
  • Subscribe to some event and be updated in real-time.

Conclusion

I demonstrated how to build web-based UIs for Excel add-ins built in C#. This can be extended to any .Net application, since the application does not need to be on the same machine as the client. Since web sockets are a generic protocol, I assume that this concept could be extended to any other technology (Java, C++, Python, etc.) as long as you can make a web server which can also listen to web sockets.

What needs to be done now, perhaps in a next blog, is a performance comparison between a Web/C# and a C#/C# client/server architectures.

TECH-0073