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.

  • Gravatar Community Blogs August 2nd, 2008 at 02:34
    <strong>Silverlight Cream for August 1, 2008 -- #340</strong>

    Denislav Savkov with a selection helper class, Mark Monster with SL Tag Cloud, Mike Taulty on asmx web
  • Gravatar unruledboy August 2nd, 2008 at 06:00
    hey, looks really great!
  • Gravatar fred August 20th, 2008 at 21:42
    Hi
    very nice and so sample !! thanks !!
    sorry but I have a problem with tagCloud_CloudItemClicked.
    The event is never called when I click in the left mouse ???
    any idea ????
    I am using vs2008+XP SL2 beta2
  • Gravatar Mark Monster August 23rd, 2008 at 12:00
    @fred:

    Did you add the code for listening to the event?

    tagCloud.CloudItemClicked += tagCloud_CloudItemClicked;
  • Gravatar Jan Michael February 3rd, 2009 at 10:40
    This is great. But I have a question, i want to apply paging (prev | next) to this tag cloud. how can I do this?

    The tag cloud item I should display must be not greater than the height of the grid contrainer of this TagCloud.
    Where should I apply this?
  • Gravatar Jan Michael February 3rd, 2009 at 12:13
    Basically I want to limit the cloud items I should display in this tagcloud.
  • Gravatar Mark Monster February 3rd, 2009 at 12:25
    Hi Jan,

    What you can do is limit the list of cloudItems. This can be done within the following part

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

    If you want to do things like paging, you can easily combine the Take and Skip methods.
  • Gravatar Jan Michael February 4th, 2009 at 07:28
    Thanks for your quick response!
    To explain further about my scenario in paging, the items I should display is based on the grid container of my tagcloud.

    ex: I have 10 tag items, because I have a predefine height of tag cloud, the visible tag item is 5. So then the others should be in the next page.
    Sorry for my lengthy question. I just barely need this. Thanks!
  • Gravatar Jan Michael February 5th, 2009 at 07:04
    Hi, any suggestion?
  • Gravatar Mark Monster February 5th, 2009 at 09:13
    Hi Jan,

    It is possible to measure the highest item per row. If the the max visible height is reached you can stop adding items. Maybe it's good to give each item an ascending number so you are able to easily select the next items that that should be visible on the next page.

    But in the end it all means measuring. I know this can be very difficult. I've had some trouble myself in creating a flow-panel that does the layout I want it to do. I hope this helps you in the right direction.
  • Gravatar Jan Michael February 7th, 2009 at 06:36
    Hi, thank you for your good suggestion. But seems, I'm having difficulty implementing it inside your wrap panel class.

    Thanks again!
  • Gravatar Mark Monster February 7th, 2009 at 21:52
    Hi Jan,

    I can understand the difficulty of implementing it in the Wrap Panel. I do not think about the Wrap Panel as an easy control. It's already half a year since I implemented it. I hope you will be able to find a solution that fits for your situation.
  • Gravatar nk July 17th, 2009 at 21:22
    Great post! SL newb here, I was trying to follow your tutorial / code to make on my own but am running into some issues. I'm not quite sure where all this code goes... I made a new SL project in VS2008 and added a new class CloudItem in which I only added the Name and Weight. Then in page.xaml.cs I put all that other code in. However this isn't working...I keep getting this error:

    1. cloudItems does not exist in current context

    Also, do I not need anything in page.xaml?
  • Gravatar donna September 22nd, 2009 at 07:22
    hi. nice tagcloud.:) i am currently working on a school project that requires me to do tag cloud and silverlight application. i happened to come across your page and found the tag cloud to be kind of interesting. therefore i decided to use it for my project. perhaps i can score better.:)

    i downloaded the sources from you. but when i opened up the solution, there seemed to be a warning.

    Unable to update auto-refresh reference 'system.web.silverlight.dll'. Cannot find assembly 'C:\Documents and Settings\Administrator\Desktop\Program Files\Microsoft SDKs\Silverlight\v2.0\Libraries\Server\System.Web.Silverlight.dll'.

    can i know where i can get this .dll file from??

    it would be great if you can help me. because i am not good with IT-related stuffs.
  • Gravatar Mark Monster September 22nd, 2009 at 07:34
    Hi Donna,

    This dll is no longer needed. It didn't much anyway, but basically it included some html that can show the silverlight application.

    Please read this article to understand how to include your Silverlight application using html: http://msdn.microsoft.com/en-us/library/cc189089%28VS.95%29.aspx
  • Gravatar donna September 23rd, 2009 at 08:22
    hi. its me again.

    i cant deploy it to my sharepoint site. any idea?
  • Gravatar donna October 1st, 2009 at 08:23
    hi

    i realised the reason why i cant deploy to the sharepoint site is because the source you gave is not a webpart project. it is a web application. am i right?

    however, when i transferred all your codes to the new webpart project that i created, i encountered many errors.

    why is this so?
  • Gravatar donna October 5th, 2009 at 04:49
    hey mark.

    i'm in need of help. i managed to build the application successfully. however, upon debugging, the tags are static. as shown above. it did not move like the one you have. can help me with that? do i have to add in any additional codes for it to work and move?? pls pls i need help.
  • Gravatar Mark Monster October 5th, 2009 at 07:41
    Hi Donna,

    The rotating 3-D Tagcloud is based on the articles of Peter Gerritsen.

    http://blogs.tamtam.nl/peterg/2009/02/13/CreatingA3DTagcloudInSilverlightPart1.aspx
    http://blogs.tamtam.nl/peterg/2009/02/19/CreatingA3DTagcloudInSilverlightPart2.aspx
  • Gravatar donna October 8th, 2009 at 09:22
    hi mark.

    so i just have to add in the codes from

    http://blogs.tamtam.nl/peterg/2009/02/13/CreatingA3DTagcloudInSilverlightPart1.aspx?
  • Gravatar waqas January 6th, 2010 at 09:22
    great job,keep it up,thanks
  • Gravatar Alberto September 29th, 2011 at 22:34
    and what about your current tag clouds with the tag incoming in the user direction? like the old fashon screen saver with a black sky and dotted stars incoming... is available the source code???....
  • Gravatar Will November 7th, 2011 at 08:11
    Hi
    thanks - really helpful. I've used this as a base for implementing my own control.

    Question for you: how would you recommend implementing this so that there was no space between the items? In other words, in the current version the height of each row is the height of the tallest item in the row. How would you implement it so that the spaces between all words were filled?

    cheers
    will.
Gravatar