Creating a Silverlight TagCloud UserControl

I was just looking around the web to find a Silverlight Control that's has the features of a TagCloud. There are a lot of TagClouds in the world of Web 2.0, so probably almost everyone knows the way they work.

The looks of a Cloud Item

Just very simple a class that has a name, used for displaying and a weight, used for determining the font-size. You'll probably want it to have more properties but that can be added when needed I guess.

1 public class CloudItem 2 { 3 public string Name { get; set; } 4 public int Weight { get; set; } 5 }

Determining the style for a CloudItem based on the Weight

I've been Googling around to find a nice approach for determining the font-size for a Cloud-Item. I finally found a blog-post about Tag Clouds in ColdFusion that explains an approach that I also followed in this UserControl. It basically supports 5 different font-sizes: Smallest, Small, Medium, Large and Largest. The definition for which font-size is determined by using the minimum and maximum Weights. I did this in a very simple Linq query.

int minWeight = cloudItems.Min((cloudItem => cloudItem.Weight)); int maxWeight = cloudItems.Max((cloudItem => cloudItem.Weight));

And the following method determines which of the five styles should be used.

1 public string DetermineResourceForWeight(CloudItem cloudItem, int minWeight, int maxWeight) 2 { 3 int distribution = (maxWeight - minWeight)/3; 4 if (cloudItem.Weight == minWeight) 5 return "CloudTagStyleSmallest"; 6 if (cloudItem.Weight == maxWeight) 7 return "CloudTagStyleLargest"; 8 if (cloudItem.Weight > (minWeight + (distribution*2))) 9 return "CloudTagStyleLarge"; 10 if (cloudItem.Weight > (minWeight + (distribution))) 11 return "CloudTagStyleMedium"; 12 return "CloudTagStyleSmall"; 13 }

I added the styles to the App.xaml

1 <Application.Resources> 2 <Style x:Key="CloudTagStyleLargest" TargetType="TextBlock"> 3 <Setter Property="Margin" Value="3,3,3,3"/> 4 <Setter Property="FontSize" Value="30"/> 5 </Style> 6 <Style x:Key="CloudTagStyleLarge" TargetType="TextBlock"> 7 <Setter Property="Margin" Value="3,3,3,3"/> 8 <Setter Property="FontSize" Value="25"/> 9 </Style> 10 <Style x:Key="CloudTagStyleMedium" TargetType="TextBlock"> 11 <Setter Property="Margin" Value="3,3,3,3"/> 12 <Setter Property="FontSize" Value="20"/> 13 </Style> 14 <Style x:Key="CloudTagStyleSmall" TargetType="TextBlock"> 15 <Setter Property="Margin" Value="3,3,3,3"/> 16 <Setter Property="FontSize" Value="15"/> 17 </Style> 18 <Style x:Key="CloudTagStyleSmallest" TargetType="TextBlock"> 19 <Setter Property="Margin" Value="3,3,3,3"/> 20 <Setter Property="FontSize" Value="10"/> 21 </Style> 22 <Style x:Key="CloudTagStylePanel" TargetType="TagCloud:WrapPanel"> 23 <Setter Property="Background" Value="White"/> 24 </Style> 25 </Application.Resources>

Converting a CloudItem to a TextBlock

Now we have ourselves some code that covers the logic we have to convert every CloudItem into a TextBlock. The following function converts one CloudItem.

1 public TextBlock ConvertToTextBlock(CloudItem cloudItem, int minWeight, int maxWeight) 2 { 3 return new TextBlock 4 { 5 Text = cloudItem.Name, 6 Style = 7 Application.Current.Resources 8 [DetermineResourceForWeight(cloudItem, minWeight, maxWeight)] as Style, 9 Tag = cloudItem 10 }; 11 }

And this does the conversion for a collection of CloudItems.

IEnumerable<TextBlock> textBlocks = (from cloudItem in cloudItems select cloudItemService.ConvertToTextBlock(cloudItem, minWeight, maxWeight));

Dynamically adding TextBlocks to a Panel

We can add those items very easy to a Panel. We just have to remove all the items that are in it (you'll never know if there are any) and then just add the TextBlocks.

1 LayoutRoot.Children.Clear(); 2 3 foreach (TextBlock textBlock in textBlocks) 4 { 5 LayoutRoot.Children.Add(textBlock); 6 }

Wrapping the TextBlocks the right way

But yes, there needs to be some layout done. I was not sure if I needed to put time in doing a thing like layout on Panel like Wrapping or some kind. I did again some Googling and found a very nice solution that's works in this situation, so far. So I just used it!

Interaction with the TagCloud UserControl

It's important to know how you can interact with the TagCloud UserControl. You can change the collection of CloudItems in the following way.

1 var tagCloud = new TagCloud.TagCloud(); 2 tagCloud.CloudItems = new List<CloudItem> 3 { 4 new CloudItem {Name = ".NET", Weight = 6}, 5 new CloudItem {Name = "API", Weight = 2}, 6 new CloudItem {Name = "C#", Weight = 3}, 7 new CloudItem {Name = "Calendar", Weight = 1}, 8 new CloudItem {Name = "Mail", Weight = 2}, 9 new CloudItem {Name = "PIM", Weight = 4}, 10 new CloudItem {Name = "Reader", Weight = 1}, 11 new CloudItem {Name = "Silverlight", Weight = 5}, 12 new CloudItem {Name = "Task", Weight = 1} 13 };

And you can also listen to clicks on CloudItems by using this code. The CloudItemEventArgs contains the original CloudItem property. Even when you inherit from CloudItem to add properties these will be passed back through the CloudItemEventArgs.

1 tagCloud.CloudItemClicked += tagCloud_CloudItemClicked; 2 3 void tagCloud_CloudItemClicked(object sender, CloudItemEventArgs e) 4 { 5 throw new NotImplementedException(); 6 }

tagCloud Final preview and The sources

I think this control is quite done now. It can become more feature-rich but does the things I want it to do. You can find the sources here.

Please leave a message if you're using this control. It's free, but I'd like to know if the control get's used a lot.

Silverlight Networking - Using a proxy to overcome cross-domain-scripting troubles

This is already my fifth article on Silverlight Networking, with only the Credentials in mind. Please read my other articles first:

I ended my last post with the trouble we get by using Javascript for the networking. Javascript doesn't support calling outside the document's domain. I've read through some hacking around this, but the hacks tend to work for a small time because they make use of bugs/holes inside the browsers that will be closed someday. But there's a solution that will work for Javascript.

A proxy is the solution

It's the best solution available for Javascript I think and it works. I'm talking about using some kind of proxy that will fully forward the request and fully return the response. One of the things I think is important to forward is the set of HTTP Headers. I've looked at some proxy code in both .NET and PHP. Some of them just don't forward the Headers. This is important in scenario's where the authorization header is used.

Also I've found some solutions that are fully customized for a specific situation, but I would rather like a more generic solution that can handle any request and response not just the two requests I need now.

I've tested this proxy, written in PHP. It works for me because I've got a Webhost that supports PHP and no ASP.NET. I've looked around to find a possible solution written for ASP.NET. This solution looks promising but you'll have to change it a little bit to forward the Headers also.

The nicest thing about the proxies I've found, is their working. They all work with some kind of querystring parameter. I've changed build a new implementation of the IWebRequestExecutor a to support a proxy. It's a very small class that doesn't do much. It only changes the Uri in a JavascriptWebRequest. A very small addition I think with a lot of effect. You can use ProxyWebRequestExecutor instead of the JavascriptWebRequestExecutor as follows.

1 IWebRequestExecutor requestExecutor = 2 new ProxyWebRequestExecutor("http://.../proxy.php?url={0}"); 3 4 5 var request = new JavascriptWebRequest 6 { 7 Uri = "http://localhost:2851/Test.xml", 8 Method = "GET" 9 }; 10 11 requestExecutor.Execute(request, OnResponse);

This is the implementation of the ProxyWebRequestExecutor.

1 public class ProxyWebRequestExecutor : IWebRequestExecutor 2 { 3 private readonly IWebRequestExecutor innerWebRequestExecutor; 4 private readonly string proxyPattern; 5 6 public ProxyWebRequestExecutor(string proxyPattern) : 7 this(new JavascriptWebRequestExecutor(), proxyPattern) 8 { 9 } 10 11 public ProxyWebRequestExecutor(IWebRequestExecutor innerWebRequestExecutor, 12 string proxyPattern) 13 { 14 if (!proxyPattern.Contains("{0}")) 15 throw new ArgumentException( 16 "The proxyPattern should containt {0} which will be filled with the original request-url.", 17 "proxyPattern"); 18 19 this.innerWebRequestExecutor = innerWebRequestExecutor; 20 this.proxyPattern = proxyPattern; 21 } 22 23 #region IWebRequestExecutor Members 24 25 public void Execute(JavascriptWebRequest request, 26 EventHandler<JavascriptResponseEventArgs> functionToCallOnResponse) 27 { 28 if (!string.IsNullOrEmpty(proxyPattern)) 29 { 30 request.Uri = 31 string.Format(proxyPattern, HttpUtility.UrlEncode(request.Uri)); 32 } 33 innerWebRequestExecutor.Execute(request, functionToCallOnResponse); 34 } 35 36 public void Execute(JavascriptWebRequest request) 37 { 38 Execute(request, null); 39 } 40 41 #endregion 42 }

What's next?

So we now have something that can be used to bypass the limitations of the Silverlight Networking features. At least I have the code, and most of them is published through the different posts. I'm thinking about putting this functionality onto an Open Source site. Please leave a comment if you're interested.

Thinking about Silverlight and Mocking

There are quite some Mocking frameworks available. But sadly no one is ported to Silverlight, yet. I know about the porting of NUnit to Silverlight, but while working on Unit tests for the "Silverlight Networking through Javascript" library I found out there's no Mocking framework yet to support the Silverlight runtime.

I would like to see some support for Silverlight from either Moq or Rhino Mocks. That's because I like the way those Mocking frameworks work. I think they both make use of Castle's DynamicProxy, so that's another thing that first needs to become Silverlight enabled.

I'm sure more people thought about Mocking in a Silverlight runtime but didn't find a solution too. I'm interested if anyone started to port one of the above Mocking frameworks to Silverlight. I'm not even totally sure if it's possible but at least a part of Reflection.Emit exists.

What are anyone's thoughts about Mocking in Silverlight?

Silverlight Networking - Being Responsive to the WebRequest, a WebResponse

This is already my fourth article on Silverlight Networking, with only the Credentials in mind. Please read my other articles first:

In the last article I wrote about the WebRequest that gets translated from Silverlight to Javascript. In this article I will cover the converting of a Javascript WebResponse to a Silverlight WebResponse. Also the routing of the Response to the rightful listener is part of this article.

Building the Response

I created the following Silverlight class that can be used from Javascript. It's a class that represents a WebResponse and contains the Response variables. You can check for example the StatusCode property it should be "200" for a successful request.

1 [ScriptableType] 2 public class JavascriptWebResponse 3 { 4 public JavascriptWebResponse() 5 { 6 Headers = new Dictionary<string, string>(); 7 TimedOut = false; 8 Aborted = false; 9 } 10 11 [ScriptableMember] 12 public string Body { get; set; } 13 14 [ScriptableMember] 15 public string StatusCode { get; set; } 16 17 [ScriptableMember] 18 public string StatusText { get; set; } 19 20 [ScriptableMember] 21 public bool TimedOut { get; set; } 22 23 [ScriptableMember] 24 public bool Aborted { get; set; } 25 26 public IDictionary<string, string> Headers { get; set; } 27 28 [ScriptableMember] 29 public void SetHeaders(string headers) 30 { 31 string[] headersSplit = headers.Trim().Split('\n'); 32 foreach (string header in headersSplit) 33 { 34 string[] splitHeader = header.Split(':'); 35 Headers.Add(splitHeader[0].Trim(), splitHeader[1].Trim()); 36 } 37 } 38 }

And then I add an anonymous javascript function as a handler of the Completed event of the Request. This function should wrap the response in the JavascriptWebResponse object and call the ResponseComplete on the JavascriptBridge object which is responsible for distributing the response to a callback function.

1 request.add_completed( 2 (function(executor, eventArgs) 3 { 4 var silverlightPlugin = document.getElementById('silverlightControl'); 5 var webResponse = silverlightPlugin.content.services.createObject('JavascriptWebResponse'); 6 webResponse.Body = executor.get_responseData(); 7 webResponse.TimedOut = executor.get_timedOut(); 8 webResponse.Aborted = executor.get_aborted(); 9 webResponse.StatusCode = executor.get_statusCode(); 10 webResponse.StatusText = executor.get_statusText(); 11 webResponse.SetHeaders(executor.getAllResponseHeaders()); 12 silverlightPlugin.content.Bridge.ResponseComplete( 13 executor.get_webRequest().get_userContext(), 14 webResponse); 15 }) 16 ); 17

Associating the Response with a callback function

Because it is possible to run multiple requests at once that can differ to each other it's important to be able to identify every request. You can use the "userContext" on the javascript Sys.Net.WebRequest class to give it context.

request.set_userContext('fc7444e9-9f7c-4e79-9ed4-43f42ed38ba0');

And to read it back from the anonymous javascript function you can use:

executor.get_webRequest().get_userContext()

I make use of a Guid to generate an unique identification for every request. Besides this I have some code that accepts a function pointer and associates this function-pointer to the generated Guid.

1 private readonly Dictionary<string, EventHandler<JavascriptResponseEventArgs>> responseListeners = 2 new Dictionary<string, EventHandler<JavascriptResponseEventArgs>>(); 3 4 public string SubscribeToResponse(EventHandler<JavascriptResponseEventArgs> functionToCallOnResponse) 5 { 6 string listenerReference = Guid.NewGuid().ToString(); 7 lock (responseListeners) 8 { 9 responseListeners.Add(listenerReference, functionToCallOnResponse); 10 } 11 return listenerReference; 12 }

In Silverlight I have one method where all responses come in complete with the Guid. It looks up  a function-pointer and executes it.

1 [ScriptableMember] 2 public void ResponseComplete(string listenerReference, JavascriptWebResponse response) 3 { 4 EventHandler<JavascriptResponseEventArgs> functionToCallOnResponse = 5 responseListeners[listenerReference]; 6 if (functionToCallOnResponse != null) 7 functionToCallOnResponse(this, new JavascriptResponseEventArgs {WebResponse = response}); 8 //After the call the response is completed the function-pointer can be removed. 9 lock (responseListeners) 10 { 11 responseListeners.Remove(listenerReference); 12 } 13 } 14

Full usage of Request till Response

So we now have ourselves the ability to give a function-pointer to execute after the response is complete. The following example shows the full usage of request and response.

1 public void MakeRequest() 2 { 3 IWebRequestExecutor requestExecutor = new JavascriptWebRequestExecutor(); 4 5 var request = new JavascriptWebRequest 6 { 7 Uri = "http://localhost:2851/Test.xml", 8 Method = "GET" 9 }; 10 11 requestExecutor.Execute(request, OnResponse); 12 } 13 14 public void OnResponse(object sender, JavascriptResponseEventArgs e) 15 { 16 bool aborted = e.WebResponse.Aborted; 17 string body = e.WebResponse.Body; 18 IDictionary<string, string> headers = e.WebResponse.Headers; 19 string statusCode = e.WebResponse.StatusCode; 20 string statusText = e.WebResponse.StatusText; 21 bool timedOut = e.WebResponse.TimedOut; 22 }

What's next?

So we have ourselves everything from request to response, what's next? I already did some refactoring during the preparations of this article, and probably will do some more. Also I want to write some unit-tests to really test this framework before thinking about bringing it out as open-source. Before getting that far, there is still one bridge to cross. We have the trouble of cross-domain-scripting! Because Javascript won't let us execute requests outside of the current domain. I will cover a solution for this in my next article.

Silverlight Networking - Getting credentials to work, a new WebRequest

This is already my third article on Silverlight Networking, with only the Credentials in mind. Please read my other articles first:

The workaround

It took me some time to get the work-around ready enough for this article. I must say we finally have the possibility to make a WebRequest with Credentials. Although we have another problem to solve, it's crossdomain scripting! This article explains how you can make use of the Javascript abilities to make a WebRequest.

Using the ASP.NET Ajax Javascript libraries

I know it's not very difficult to build some code around XMLHttpRequest, but there's a very nice wrapper build by the people behind ASP.NET Ajax. This wrapper is the Sys.Net.WebRequest class. This would make a call as simple as the following code:

1 // This function adds and removes the 2 // Web request completed event handler. 3 function WebRequestCompleted() 4 { 5 // Instantiate the WebRequest. 6 var wRequest = new Sys.Net.WebRequest(); 7 8 // Set the request Url. 9 wRequest.set_url(getPage); 10 11 // Set the web request completed event handler, 12 // for processing return data. 13 wRequest.add_completed(OnWebRequestCompleted); 14 alert("Added Web request completed handler"); 15 16 // Remove the web request completed event handler. 17 // Comment the following two lines if you want to 18 // use the handler. 19 wRequest.remove_completed(OnWebRequestCompleted); 20 alert("Removed handler; the Web request return is not processed."); 21 22 // Execute the request. 23 wRequest.invoke(); 24 }

To use this library you first have to download it and reference the MicrosoftAjax.js as follows:

<script type="text/javascript" src="JS/MicrosoftAjax.js"></script>

Calling dynamic Javascript code from Silverlight

It is possible to call Javascript code that already exists by using the HtmlPage.Window.Invoke() method. But as explained next I want to built up the Javascript dynamically.

To call a string containing Javascript you will have to make use of the HtmlPage.Window.Eval() method.

HtmlPage.Window.Eval("(function(){ alert('Hello world! Using dynamic Javascript');})();");

Just for my own sake I build a small wrapper around the Eval function, that just delegates to HtmlPage.Window.

1 public static class JavascriptBridge 2 { 3 private static HtmlWindow window = HtmlPage.Window; 4 5 public static object Eval(string code) 6 { 7 return window.Eval(code); 8 } 9 }

Setting up a JavascriptWebRequest in C#

My idea was to build a C# class that makes use of Javascript to do the actual WebRequest but looks quite similar to the WebRequest object. I came up with the following class which probably speaks for itself.

1 public class JavascriptWebRequest 2 { 3 public string Uri { get; set; } 4 public string Method { get; set; } 5 public Dictionary<string, string> Headers { get; set; } 6 public string Body { get; set; } 7 8 public JavascriptWebRequest() 9 { 10 Headers = new Dictionary<string, string>(); 11 } 12 13 public void Execute() 14 { 15 JavascriptWebRequestExecutor.Execute(this); 16 } 17 }

I for myself really like this class. It allows me to write code like this:

1 var request = new JavascriptWebRequest 2 { 3 Uri = "http://localhost:2851/Test.xml", 4 Headers = 5 { 6 {"Authorization", string.Format("Basic {0}", new NetworkCredential("username","password").ToString().EncodeTo64())} 7 }, 8 Method = "GET" 9 }; 10 request.Execute();

Where's all the magic?

When you look at the JavascriptWebRequest Execute method you see some magic happens behind the scenes. What happens is the JavascriptWebRequestExecutor does some translation of the JavascriptWebRequest properties to a Sys.Net.Webrequest. The request above will translate to the following Javascript.

1 // Instantiate the WebRequest object. 2 var request = new Sys.Net.WebRequest(); 3 4 // Set the request Url. 5 request.set_url('http://localhost:2851/Test.xml'); 6 7 // Set the request verb. 8 request.set_httpVerb('GET'); 9 10 // Set the headers. 11 12 request.get_headers()['Authorization']='Basic dQBzAGUAcgBuAGEAbQBlADoAcABhAHMAcwB3AG8AcgBkAA=='; 13 14 // Set the body 15 request.set_body('');

I posted the sourcecode of the full JavascriptWebRequestExecutor below. It's just a start with lot's of uses of string.Format. I think it's not that readable at this moment, maybe I'll refactor it in the future to some more readable code for the templates.

1 public class JavascriptWebRequestExecutor 2 { 3 public static void Execute(JavascriptWebRequest request) 4 { 5 string variableName = "request"; 6 string javascriptToExecute = string.Format(requestFunctionTemplate, ToJavascriptVariableDeclarion(request, variableName), variableName); 7 JavascriptBridge.Eval(javascriptToExecute); 8 } 9 10 private static string ToJavascriptVariableDeclarion(JavascriptWebRequest request, string variableName) 11 { 12 //Set up the headers 13 IList<string> headers = new List<string>(); 14 foreach (var header in request.Headers) 15 headers.Add(string.Format(headerTemplate, variableName, header.Key, header.Value)); 16 string headersPresentation = string.Join(Environment.NewLine, headers.ToArray()); 17 //Return the complete variable presentation of the request. 18 return string.Format(requestVariableTemplate, variableName, request.Uri, request.Method, headersPresentation, request.Body); 19 } 20 21 22 private const string requestFunctionTemplate = 23 @" 24 (function() 25 {{ 26 {0} 27 {1}.add_completed((function(executor, eventArgs){{ alert(executor.get_responseData()); }})); 28 {1}.invoke(); 29 }})(); 30 "; 31 32 private const string requestVariableTemplate = 33 @" 34 // Instantiate the WebRequest object. 35 var {0} = new Sys.Net.WebRequest(); 36 37 // Set the request Url. 38 {0}.set_url('{1}'); 39 40 // Set the request verb. 41 {0}.set_httpVerb('{2}'); 42 43 // Set the headers. 44 {3} 45 // Set the body 46 {0}.set_body('{4}'); 47 "; 48 49 private const string headerTemplate = 50 @" 51 {0}.get_headers()['{1}']='{2}'; 52 "; 53 }

Conclusions

So we now have a possibility to make a webrequest through javascript. Although this code works as it seems there still is some work to be done. First, we need some way to get the result back, and not only that we also need to get the result back to the rightful caller of the webrequest. More on this in the next post. Besides this, I already mentioned to trouble with crossdomain scripting, this also needs to be covered. If anyone has a good idea on this, leave some comments on my blog.