Skip Ribbon Commands
Skip to main content
Navigate Up
Sign In
 

 Categories

 
Alan Le's Vertigo Blog > Posts > Building a WPF Tag Cloud
June 05
Building a WPF Tag Cloud

I just did some recent exploration in creating a tag cloud with WPF. In particular, I wanted to be able to create a tag cloud of the lastnames for a collection of people. I came up with a solution that I'm happy with and would like to share.

You can see a screenshot of the prototype below.  The left Listbox is bounded to the collection of people and the right list is the tag cloud.  The size of the tags is based on the number of people with that lastname. Names can be added manually or updated in the top section. There is a Churn button to auto populate the people collection.

Code

Surprisingly I was able to build the tag cloud with very little code.  Instead of generating the tag cloud on the fly each time or cached like most web-based solution, I wanted the tag cloud to be dynamic as new items are added and changed.  I also didn't want to roll my own class that keep tracks of the lastnames. So I relied heavily on the built-in functionality and power of WPF and the .NET 3.0 Framework to accomplish this.

Tags and ListCollectionView

To get the list of tags, I created a ListCollectionView on the people collection and grouped it by added a GroupDescription on the "Lastname" property. The PeopleTagCloud ListBox is bounded to the groups for that ListCollectionView.

// ListCollectionView is used for sorting and grouping

lcv = new ListCollectionView(people);

 

...

 

// Group the list by lastname, the tag cloud is based on the group Name and ItemCount

lcv.GroupDescriptions.Add(new PropertyGroupDescription("Lastname"));

PeopleTagCloud.ItemsSource = lcv.Groups;

Displaying the Tags

The PeopleTagCloud is just a Listbox that has a style to make it layout the items like a tag cloud. The trick is to replace the ItemsHost which originally is a StackPanel with a WrapPanel. Lee Brimelow wrote about this awhile back.

<Style x:Key="TagsListBox" TargetType="{x:Type ListBox}">

    …

<Setter Property="Template">

<Setter.Value>

<ControlTemplate TargetType="{x:Type ListBox}">

<Grid>

<Border x:Name="Border" BorderBrush="{TemplateBinding BorderBrush}" BorderThickness="{TemplateBinding BorderThickness}"/>

<ScrollViewer Margin="1" Style="{DynamicResource SimpleScrollViewer}" Focusable="false" Background="{TemplateBinding Background}">

 

<!-- Replaced the default StackPanel ItemsHost with a WrapPanel to get the TagCloud layout-->

<WrapPanel Margin="2" IsItemsHost="true"/>

 

</ScrollViewer>

</Grid>

</ControlTemplate>

</Setter.Value>

</Setter>

</Style>

Recall that in the previous section the PeopleTagCloud is bounded to the groups of lastnames. With that binding, I can simply create a DataTemplate to display the individual tags.

<DataTemplate x:Key="TagCloudTemplate">

<TextBlock Padding="0,0,10,0"

FontSize="{Binding Path=ItemCount, Converter={StaticResource CountToFontSizeConverter}}"

Text="{Binding Path=Name, Mode=Default}"/>

</DataTemplate>

The text is bounded to the Name property and the FontSize is bounded to the ItemCount.

CountToFontSizeConverter

To get the ItemCount to be a FontSize, I needed to create a value converter. The Convert method itself isn't that great but works. I'm sure someone can devise a better algorithm for the sizes.

class CountToFontSizeConverter : IValueConverter

{

#region IValueConverter Members

 

public object Convert(object value, Type targetType, object parameter, System.Globalization.CultureInfo culture)

{

const int minFontSize = 6;

const int maxFontSize = 38;

const int increment = 3;

 

int count = (int)value;

 

return ((minFontSize + count + increment) < maxFontSize) ? (minFontSize + count + increment) : maxFontSize;

}

ObservableCollection and INotifyPropertyChanged

How does the PeopleTagCloud know that it's being updated? That's the beauty of inheriting from the ObservableCollection class. No extra code necessary in the presentation layer.

class People : ObservableCollection<Person> { }

To get the PeopleTagCloud to update when the lastname is modified is a bit trickier. I presumed that I can simply implement INotifyPropertyChanged.

class Person : INotifyPropertyChanged

This typically just works as you can see the effect on the PeopleListBox when the names are changed. However, the ListViewCollection groups and ItemCount were not updated. I needed to programmatically call the Refresh() method when the lastname is changed.

private void TextBox_TextChanged(object sender, TextChangedEventArgs e)

{

// Tell the ListCollectionView to update itself so that the groups and itemcount are updated

if (lcv != null)

lcv.Refresh();

}

 

Because of this, there is slight performance on updating the tag cloud when the names are modified. A possible solution might be to refresh after losing focus instead of TextChanged to lessen the cost of refreshing the groups. Let me know if I'm missing something here.

Auto-Populating the Data

I just re-used the churning code by Kevin Moore in his bag of tricks. It uses a background thread to add the people. The names are from the list of common names on names.mongabay.com.

Source

You can download the source here.

Comments

Re: Building a WPF Tag Cloud

Hello, I would like to get in touch with Alan Le in regards to trying out various products . . . more more by email. I can be reached at claire(at)vso-software.fr. (sorry I don't see a personal contact form) Thanks,
Claire at 8/3/2007 5:08 AM

qwyptsmv

woxofytv http://pczhtnzs.com nrczuwwd grrkjesl uymdhmak [URL=http://rfnnchta.com]pvhgnjmb[/URL]
at 8/8/2008 6:43 PM

simvastatin

Inspiration does exist, but it must find you working.
at 8/10/2008 7:31 PM

generic zyrtec

At a dinner party one should eat wisely but not too well, and talk well but not too wisely.
at 8/10/2008 10:58 PM

order vicodin

It's wonderful what we can do if we're always doing.
at 8/11/2008 2:58 AM

lorazepam

They are ill discoverers that think there is no land, when they can see nothing but sea.
at 8/11/2008 6:45 AM

generic paxil

Life is consciousness.
at 8/11/2008 12:26 PM

clopidogrel

You never know till you try to reach them how accessible men are; but you must approach each man by the right door.
at 8/11/2008 4:07 PM

chock

It is not enough to succeed. Others must fail.
at 8/11/2008 8:01 PM

buy cialis online

At a dinner party one should eat wisely but not too well, and talk well but not too wisely.
at 8/11/2008 11:15 PM
1 - 10 Next

Add Comment

Items on this list require content approval. Your submission will not appear in public views until approved by someone with proper rights. More information on content approval.

Full Name *


Your Blog


Title


Body *

CommentUrl


Attachments