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 }
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.



