RSS Feed


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.

 
Posted by Alan Le | 40 Comments | Trackback Url | Bookmark with:        
Tags:

Links to this Post

Comments

Friday, 3 Aug 2007 05:08 by Re: Building a WPF Tag Cloud
<div class=ExternalClass799430D0B9CE4FCA84CB8F8E98ABC7D5>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,</div>

Friday, 8 Aug 2008 06:43 by qwyptsmv
<div class=ExternalClass3B7F15B66FE64DE88CD5EF54DD2479E5> woxofytv http://pczhtnzs.com nrczuwwd grrkjesl <a href="http://czrhgrkf.com">uymdhmak</a> [URL=http://rfnnchta.com]pvhgnjmb[/URL] </div>

Sunday, 10 Aug 2008 07:31 by simvastatin
<div class=ExternalClass98D895877549486B924CC2FBC11426DA>Inspiration does exist, but it must find you working.</div>

Sunday, 10 Aug 2008 10:58 by generic zyrtec
<div class=ExternalClass53539A7AD569486A869E2F8283EF283A>At a dinner party one should eat wisely but not too well, and talk well but not too wisely.</div>

Monday, 11 Aug 2008 02:58 by order vicodin
<div class=ExternalClassEE4D5D5D5B2E40C994D925875FFB77C4>It's wonderful what we can do if we're always doing.</div>

Monday, 11 Aug 2008 06:45 by lorazepam
<div class=ExternalClassDCA18554E01A42ADB6C46A783478DEC5>They are ill discoverers that think there is no land, when they can see nothing but sea.</div>

Monday, 11 Aug 2008 12:26 by generic paxil
<div class=ExternalClass53AEAD230FCF4D6799561EA12FDC8CBB>Life is consciousness.</div>

Monday, 11 Aug 2008 04:07 by clopidogrel
<div class=ExternalClassCD3C95BF672947DEB814CB76FD1D2F53>You never know till you try to reach them how accessible men are; but you must approach each man by the right door.</div>

Monday, 11 Aug 2008 08:01 by chock
<div class=ExternalClass556363118ED2466BBFF6429BB219A92B>It is not enough to succeed. Others must fail.</div>

Monday, 11 Aug 2008 11:15 by buy cialis online
<div class=ExternalClassAA4FE021D66D4260980E955B3F0DD2E1>At a dinner party one should eat wisely but not too well, and talk well but not too wisely.</div>

Tuesday, 12 Aug 2008 03:12 by order fioricet
<div class=ExternalClass773BD8CCC4DD46B6A58D5826D57A7301>You don't have to cook fancy or complicated masterpieces - just good food from fresh ingredients.</div>

Tuesday, 12 Aug 2008 07:05 by purchase viagra
<div class=ExternalClass6F901DC111504DCCBFFF7345954CB40D>Nothing in the world can take the place of Persistence. Talent will not; nothing is more common than unsuccessful men with talent. Genius will not; unrewarded genius is almost a proverb. Education will not; the world is full of educated derelicts. Persistence and determination alone are omnipotent. The slogan 'Press On' has solved and always will solve the problems of the human race.</div>

Tuesday, 12 Aug 2008 10:26 by buy valium
<div class=ExternalClassFDF6F9EC5CE44283A496EE101F6F4F7F>Where we have strong emotions, we're liable to fool ourselves.</div>

Wednesday, 13 Aug 2008 07:02 by tpozixwv
<div class=ExternalClassB21F996E53914C888AF7D64BF79B5E54> <a href="http://dtoqdwmg.com">ssfrpejc</a> gmzzszog http://rbzdzebr.com fkxvgxoo kknupuiv [URL=http://zjexhwjd.com]djgsyxbw[/URL] </div>

Wednesday, 13 Aug 2008 08:05 by cheap vicodin
<div class=ExternalClassF32834FAC4E34B23885E0F3BC829BAC1>There is no stigma attached to recognizing a bad decision in time to install a better one.</div>

Wednesday, 13 Aug 2008 11:45 by omeprazole
<div class=ExternalClass0F7FBC105414458C80C053C204CAE607>Inspiration does exist, but it must find you working.</div>

Wednesday, 13 Aug 2008 03:27 by order phentermine online
<div class=ExternalClass89A31A98D17346CA8288C40AD8761D4B>I have very strong feelings about how you lead your life. You always look ahead, you never look back.</div>

Wednesday, 13 Aug 2008 06:54 by imovane itself alerter
<div class=ExternalClass6E55B04F9B4D4487844E3BD8519340B9>What's the earth With all its art, verse, music, worth - Compared with love, found, gained, and kept?</div>

Wednesday, 13 Aug 2008 10:12 by allegra
<div class=ExternalClassF10D1A5FAF704924AA42357A69DE1F66>Deeds, not words shall speak me.</div>

Thursday, 14 Aug 2008 01:52 by ultram online electroshock selecting
<div class=ExternalClassFB5D15322B6E46DC829DC59F78BD28F0>We are what we repeatedly do.</div>

Thursday, 14 Aug 2008 05:26 by prozac
<div class=ExternalClass241C1DD0E79F46CDBCF3CC052B623410>It really doesn't matter if the person who hurt you deserves to be forgiven. Forgiveness is a gift you give yourself. You have things to do and you want to move on.</div>

Thursday, 14 Aug 2008 09:24 by danazol
<div class=ExternalClassEE9A4F2DACD94D53A608D33289175010>Challenge is a dragon with a gift in its mouth?Tame the dragon and the gift is yours.</div>

Thursday, 14 Aug 2008 12:58 by cheap carisoprodol transferrin classification
<div class=ExternalClass368435DC00BA4AB1918C32F8499BB9A4>There are no wise few. Every aristocracy that has ever existed has behaved, in all essential points, exactly like a small mob.</div>

Thursday, 14 Aug 2008 04:33 by generic soma
<div class=ExternalClass25DFBDE2E113441A8124ECAA526D1B29>All programmers are playwrights and all computers are lousy actors.</div>

Thursday, 14 Aug 2008 08:04 by amoxycillin
<div class=ExternalClass46B7D1640172430BBA4C73F4E0E62251>Take a two-mile walk every morning before breakfast.</div>

Friday, 15 Aug 2008 12:25 by soma online
<div class=ExternalClass18CB5558BA0E4CAF95D635E244CB68F0>Facts are stubborn things; and whatever may be our wishes, our inclinations, or the dictates of our passion, they cannot alter the state of facts and evidence.</div>

Friday, 15 Aug 2008 03:40 by generic effexor
<div class=ExternalClass4DB69599C6324A829633299BE6C2B38F>Let us so live that when we come to die even the undertaker will be sorry.</div>

Friday, 15 Aug 2008 07:06 by viagra online
<div class=ExternalClass77B20F7827CC4A6E84624649D020C726>Normal is not something to aspire to, it's something to get away from.</div>

Friday, 15 Aug 2008 10:21 by propecia
<div class=ExternalClass957FA8DBBD1B4581A41ACCC702E17E7E>Tragedy is when I cut my finger. Comedy is when you walk into an open sewer and die.</div>

Friday, 15 Aug 2008 02:08 by clopidogrel
<div class=ExternalClass27FB643EE9364A1D8EA7CDA2E27553D1>When we are unhurried and wise, we perceive that only great and worthy things have any permanent and absolute existence, that petty fears and petty pleasures are but the shadow of the reality.</div>

Friday, 15 Aug 2008 05:45 by generic viagra online
<div class=ExternalClass239742B9F8284FD39D50BA5662B7E2B5>God creates men, but they choose each other.</div>

Friday, 15 Aug 2008 09:05 by order fioricet
<div class=ExternalClass5AB88783D7CA4809AD66D969084C876D>I like to play blackjack. I'm not addicted to gambling, I'm addicted to sitting in a semi-circle.</div>

Saturday, 16 Aug 2008 12:38 by buy ultram fiedlerite pastorale
<div class=ExternalClassEAF5F7162A0E49CABF750FC8A4238937>The crowd gives the leader new strength.</div>

Tuesday, 27 Jan 2009 06:18 by Generic Viagra
<div class=ExternalClass2836EFE9E5B24592B133BE6E0580A768>Good post, but have you thought about Building a WPF Tag Cloud before? </div>

Tuesday, 3 Feb 2009 06:36 by Order kamagra oral jelly (gel)
<div class=ExternalClassF9573E4BD05042BAA48C824D63E83A4D>At a dinner party one should eat wisely but not too well, so talk well but not too wisely.</div>

Monday, 1 Jun 2009 02:45 by phentermine 37.5
<div class=ExternalClass7A0CE077A2474AC29FACAB432566825A>Generally, phentermine appears to be relatively well tolerated.[5] It can produce side effects consistent with its catecholamine-releasing properties, e.g., tachycardia (increased heart rate) and elevated blood pressure, but the incidence and magnitude of these appear to be less than with the amphetamines. Because phentermine acts through sympathomimetic pathways, the drug may increase blood pressure and heart rate. It may also cause palpitations, restlessness, and insomnia. Additionally, phentermine has the potential to cause physical and psychological dependence. http://www.abcweightloss.net Phentermine without prescription</div>

Thursday, 25 Jun 2009 07:53 by Online pharma
<div class=ExternalClass651067796A204C2C8238180FF0C35027>Thanks for article! It's great.</div>

Tuesday, 14 Jul 2009 12:25 by AnhLDBK
<div class=ExternalClass714C85E967F34909B86E993EC3A7AEA0>That's great! Thanks for sharing!</div>

Wednesday, 29 Jul 2009 04:35 by generic viagra online
<div class=ExternalClass82EA328284C84692801B31F36C597583>thanks for sharing it. very nice post. nice comments.</div>

Sunday, 16 Aug 2009 01:02 by Why is my computer so slow
<div class=ExternalClassCCD8859560D648E6AF75C5E28BFADA5F>Nice post, lean something every day :) thanks!</div>

Name:
URL:
Email:
Comments:

CAPTCHA Image Validation