Tag Cloud Filters with ASP.NET 3.5's LinqDataSource and ListView Controls

This past week I finally set aside some time to take a look at Silverlight.  I started with ScottGu's digg demo application and continued on to silverlight.net to check out what some of the early adopters have already built.  I didn't get too far before I bumped into a control on the Silverlight showcase homepage that thought was pretty interesting.  The control is a datagrid of sorts that allows you to filter the contents of the grid using a tag cloud.  So I took a crack at creating a similar looking grid using ASP.NET 3.5 (the screen shot is below).

Notes: The icon images used in this sample are transparent PNG's, so the demo page looks a little goofy in IE6.

Live Demo (IE7, FF and Opera 9.26) | Download (.Net 3.5)

image

Update (3/4/2008):  Mark Grubner posted the following comment:

A question about this one though - could you comment on the performance issues/requirements in a "real" application? I am thinking about the fact that the xml document is parsed every time you click on one of the tags. What could/should be done about this?

Which of course is a fine point and a great question.  In the solution I posted below every time a tag is clicked, the XML file is re-parsed.  Clearly this is far from an ideal implementation.  So I refactored my original sample to avoid this continuous reprocessing by taking the following actions:

  • Created a class for holding the Icon object data.  The class looks like this ...

image

  • Added a session backed property to the page that returns an IEnumerable<Icon> collection.  The first time the property is invoked LINQ to XML is used to load the Icons.xml file into a collection of Icon objects.  Once the file is loaded it is placed into session.  Of course depending on your requirements you might be able to use the Cache or Application containers as well.  I chose Session for my fictitious example.

image

  • Replaced the LINQ to XML queries in the two LinqDataSource Selecting event handlers with LINQ to Objects queries that operate on the IEnumerable<Icon> collection of objects.

image

Did I miss anything?  I hope not.  Anyway, in case anyone is curious, I usually focus my examples on portability (that's why I chose XML over a database for most of my samples).  I know doing this looses some of the 'real worldness', but I figure the portability makes up for that (and would it be any better if I used Northwind?  How close do those tables resemble your production database?).

Either way thank you Mark for the comment.  I appreciate you pointing it out.  The download for the updated code can be found here

Rounded Corners

I used the sliding doors technique to create the rounded corners for this sample.  If this approach is new to you, I would recommend reading the following links

Tag Cloud LinqDataSource

The data for the tag cloud and icon grid is fed from an XML file.  The XML file contains a collection of icon xml elements, each with three attributes: name, imageUrl and tags.  These attributes store the metadata for each of the images being displayed.  The tags attribute is a comma separated list of categories the image belongs to.  Here is a portion of the xml file for a few of the icons displayed above.

image

To build the tag cloud, I need to query this XML file and return the unique set of tag names as well as the frequency of their occurrence.  Tags that occur more frequently will be displayed in larger text from within our tag cloud.  Of course with LINQ to XML, getting at this information is a no-brainer.  In the following 4 lines of code, I have extracted all of the icon XML elements, split the tags attribute by the comma separator and figured out how many times each unique tag value occurs.    

image 

Tag Cloud ListView

After creating the LINQ query for our data source, I went ahead and created a very simple ListView to render the tag cloud.  I want my cloud to render as a series of anchor's within a DIV container, so I have configured the ListView with this in mind (ASP.NET render's LinkButton's as HTML anchors). 

image

To control the relative size of the tags within the cloud, I have implemented a method called CreateCssClass that accepts the number of times the tag occurs and returns the corresponding CSS class.  For this example, I have 5 classes, t1 through t5.  Each of these classes has a different font-height applied.

image 

And here is the CreateCssClass method that translates the tag frequency into a CSS class ...

image 

Tag Cloud Filter Commands

Each of the LinkButtons in the tag cloud ListView have their CommandName and CommandArgument attributes set.  I am using the ListView's ItemCommand event to apply the tag filter to the grid.  I have the following event handler attached to this event that carries out this task.  When a tag from the tag cloud is clicked, this event handler adds a parameter to the LinqDataSource's WhereParameters collection.  When the Select for the grid's ListView fires, it will have access to the tag value that needs to be filtered by.  When the special 'all' link is clicked, the WhereParameters collection is cleared and all of the icons are displayed.

image

Icon LinqDataSource

Like the tag cloud, the Icon grid is also fed from the XML previously described.  Because the Icon grid needs to support filtering based on given tag value, the Selecting event handler for the LinqDataSource needs to check if a tag value has been provided.  If one has, this value needs to be used from the where clause. 

Below is the event handler for the Selecting event.  Notice how the WhereParameters collection is evaluated to see if a tag filter has been applied.  If no tag filter exists, the where clause is short-circuited and all icons are returned.  If the tags filter does exist, only icons for the tag value are displayed.

image

Icon Grid

The grid displaying the Icons is also rendered using a ListView and like the tag cloud this grid is also very simple.  It renders as a UL/LI structure.  The LayoutTemplate contains a UL and a placeholder LI and the ItemTemplate contains the markup for the LI that contains a nested IMG element that is populated with the ImageUrl attribute from the data source. 

image

Tie it all together and you get a fairly nice looking page.

That's it.  Enjoy!


TrackBack

TrackBack URL for this entry:
http://mattberseth.com/blog-mt/mt-tb.fcgi/107

Listed below are links to weblogs that reference Tag Cloud Filters with ASP.NET 3.5's LinqDataSource and ListView Controls:

» 【收藏】推荐系列:2008年第08期 总10期 from Jacky_Xu
总第10期推荐系列,总共有8篇文章,内容涉及ASP.NET3.5、Silverlight、ASP.NETMVCFramework、Spring.Net、Unity等。 1.TagCloud... [Read More]

» Collegamenti del 14 Marzo, ASP.NET, ASP.NET AJAX, ASP.NET MVC e .NET from ScottGu Italian
Collegamenti del 14 Marzo, ASP.NET, ASP.NET AJAX, ASP.NET MVC e .NET [Read More]

Comments


Posted by: Adnan Algrigri on March 3, 2008 12:00 AM

great, inspiring work

Posted by: neo on March 4, 2008 12:00 AM

I want to use linq to sql to read data from database. I changed your linq to xml into linq to sql.But it failed.I dont know why

Posted by: neo on March 4, 2008 12:00 AM

I want to use linq to sql to read data from database. I changed your linq to xml into linq to sql.But it failed.I dont know why

Posted by: Mark Grubner on March 4, 2008 12:00 AM

Hi Matt! Fantastic examples flying out regularly - please keep them coming.
A question about this one though - could you comment on the performance issues/requirements in a "real" application? I am thinking about the fact that the xml document is parsed every time you click on one of the tags. What could/should be done about this?

Posted by: Mark Grubner on March 6, 2008 12:00 AM

Hi Matt!

Thanks for the updated code - I really appreciate that you took the time to help me understand. I also want to thank you for using the portable approach with XML.

If I could make another request while you are in such a good mood?
While working with your example some of my code in the Eval blocks was getting unwieldy - would it be possible for you to give an example of how to build one or more of the ListViews in code-behind? Or is there some other way to handle this?

Posted by: Realist on March 14, 2008 12:00 AM

Great!

That looks really sweet Matt. Can you tell us where we can find the icon set you are using?

Posted by: Jason on March 14, 2008 12:00 AM

Do you know where can I find those nice looking icons?

Very Nice use of LINQ and css!

Very nice!

@Mike, @Jason

I found the icons here. Each set has their own license agreement, so you might want to check that out if you decide to use them.

Posted by: Carl Xu on April 17, 2008 12:00 AM

Nice~!

I love, love, love ALL of your demos and examples. Its so uncommon to find a developer with a good design sense. Please keep up the good work as I continue to try and hone my design skills. :) Great job.

Post a comment

(If you haven't left a comment here before, you may need to be approved by the site owner before your comment will appear. Until then, it won't appear on the entry. Thanks for waiting.)

Consulting Services

Yep - I also offer consulting services. And heck, I'll do just about anything. If you enjoy my blog just drop me an email describing the work you need done.

Recent Comments

  • Jennifer wrote: I love, love, love ALL of your demos and examples. Its so uncommon to find a developer with a good ...
  • Carl Xu wrote: Nice~! ...
  • Matt Berseth wrote: @Mike, @Jason I found the icons here. Each set has their own license agreement, so you might want ...
  • Orry Rotem wrote: Very nice! ...
  • Peter Kellner wrote: Very Nice use of LINQ and css! ...
  • Realist wrote: Great! ...
  • Mike Comstock wrote: That looks really sweet Matt. Can you tell us where we can find the icon set you are using? ...
  • Jason wrote: Do you know where can I find those nice looking icons? ...