Angled Column Headers with Silverlight 2's DataGrid

At the software shop I work at screen real-estate is always at a premium.  The bread and butter of our business is data, and the more of it we can fit on a screen the happier our customers are.  Sometimes, to cram more data into our grids, we end up abbreviating column headers, wrapping them or possibly combining 2 or 3 data elements into a single cell.  Usually this works out O.K., but sometimes we still run out of room.  When this happens we either remove columns, or break the grid across separate pages. 

Honestly, usually none of this is a huge problem.  But there is that certain class of data that just doesn't fit well into an HTML TABLE - when the length of the data elements are substantially smaller than the data label's.  The grid ends up looking too sparse. 

I hadn't thought of this before, but football statistics fit into this category pretty well (I noticed this while leafing through Sports Illustrated Fantasy Football Preview).  The football season is short (only 16 games) and the per game statistics that are worth counting have relatively small values (i.e, carries, receptions, touchdowns, etc...).  Sports Illustrated angled the column headers to keep the grid from running off the page and I thought it looked pretty good.

So where is this going?  Well, I haven't had a chance to play with the new Silverlight 2 Beta 2 bits so I thought it might be interesting see what it would take to override Silverlight's DataGrid to render the column headers at a 45 degree angles.  I should warn you that I am *very* new to Silverlight, but I thought this was interesting enough to write up a quick post about. 

Live Demo | Download 



Binding Data to the Grid

I bound the DataGrid to a collection of WideReciever objects (anyone else excited about football being just around the corner?).  It all feels a lot like working with ASP.NET's GridView.  I just want the grid to render text, so I have bound the properties of the WideReciever objects to the DataGridTextColumn.  Then I set the column header text using the Header property and then used the ElementStyle attribute to set the padding on the text that is rendered.

Here is the XAML for the DataGrid.

   1: <data:DataGrid x:Name="grid" AutoGenerateColumns="False" Width="560" GridlinesVisibility="None" HeadersVisibility="Column" MinColumnWidth="40" IsReadOnly="True" CanUserResizeColumns="False">
   2:     <data:DataGrid.Columns>
   3:         <data:DataGridTemplateColumn Width="250">
   4:             <data:DataGridTemplateColumn.CellTemplate>
   5:                 <DataTemplate>
   6:                     <StackPanel Orientation="Horizontal" VerticalAlignment="Bottom">
   7:                         <TextBlock Loaded="TextBlock_Loaded" Padding="4, 5, 4, 2" FontWeight="Bold" />
   8:                         <TextBlock Text="{Binding Name}" Foreground="#990000" FontWeight="Bold" Padding="10, 5, 10, 2" />
   9:                         <TextBlock Text="{Binding Team}" FontStyle="Italic" Padding="0, 5, 5, 5" />
  10:                     </StackPanel>
  11:                 </DataTemplate>
  12:             </data:DataGridTemplateColumn.CellTemplate>
  13:         </data:DataGridTemplateColumn>
  14:         <data:DataGridTextColumn Header="Games" DisplayMemberBinding="{Binding GamesPlayed}" ElementStyle="{StaticResource textCell}" />
  15:         <data:DataGridTextColumn Header="Receptions" DisplayMemberBinding="{Binding Receptions}" ElementStyle="{StaticResource textCell}" />
  16:         <data:DataGridTextColumn Header="Rec. Yards" DisplayMemberBinding="{Binding Yards}" ElementStyle="{StaticResource textCell}" />
  17:         <data:DataGridTextColumn Header="Yds per Game" DisplayMemberBinding="{Binding YardsPerGame}" ElementStyle="{StaticResource textCell}" />
  18:         <data:DataGridTextColumn Header="100-YD Gms" DisplayMemberBinding="{Binding OneHundredYardGames}" ElementStyle="{StaticResource textCell}" />
  19:         <data:DataGridTextColumn Header="Total TDs" DisplayMemberBinding="{Binding Touchdowns}" ElementStyle="{StaticResource textCell}" />
  20:         <data:DataGridTextColumn Header="Bye Week" DisplayMemberBinding="{Binding ByeWeek}" ElementStyle="{StaticResource textCell}" />
  21:     </data:DataGrid.Columns>
  22: </data:DataGrid>


And here is the style setting I am using to set the padding and alignment of the cell text.

   1: <Style x:Key="textCell" TargetType="TextBlock">
   2:     <Setter Property="Padding" Value="6, 2, 6, 2" />
   3:     <Setter Property="HorizontalAlignment" Value="Center" />
   4: </Style>


Determining the DataItemIndex

I didn't find a way to bind the current rows index to the grid.  So to work around this I wired the Loaded event of a TextBlock to run a bit of code that looks up the bound WideReciever index and populates the TextBlock with this value.

   1: private void TextBlock_Loaded(object sender, RoutedEventArgs e)
   2: {
   3:     //  get a reference to the TextBlock
   4:     TextBlock textBlock = (TextBlock)sender;
   6:     //  get the data item index
   7:     int index = ((IList<WideReciever>)this.grid.ItemsSource).IndexOf((WideReciever)textBlock.DataContext);
   9:     //  set the index
  10:     textBlock.Text = index + 1 < 10 ? string.Format("{0}.  ", index + 1) : string.Format("{0}.", index + 1);
  11: }


Styling the Column Headers

Finally, to get the column headers to render at an angle, I overrode the Template for the DataGrid's DataGridColumnHeaders and used a plain old Canvas to position the header text how I wanted it.  To get the text to rotate I used the RotateTransform and set the angle property to -35.

   1: <Style TargetType="data:DataGridColumnHeader">
   2:     <Setter Property="Template">
   3:         <Setter.Value>
   4:             <ControlTemplate TargetType="data:DataGridColumnHeader">
   5:                 <Canvas x:Name="RootElement" Height="60" HorizontalAlignment="Stretch">
   6:                     <ContentPresenter FontSize="10" FontWeight="400" Canvas.Left="15" Canvas.Top="50" Content="{TemplateBinding Content}">
   7:                         <ContentPresenter.RenderTransform>
   8:                             <RotateTransform Angle="-35"/>
   9:                         </ContentPresenter.RenderTransform>
  10:                     </ContentPresenter>
  11:                 </Canvas>
  12:             </ControlTemplate>
  13:         </Setter.Value>
  14:     </Setter>
  15: </Style>


That's it.  Enjoy!


TrackBack URL for this entry:

Listed below are links to weblogs that reference Angled Column Headers with Silverlight 2's DataGrid:

» Links from Evonet Consulting
Links [Read More]

» Post: 163 from Mirrored Blogs
Post: Approved at: Aug-12-2008 Angled Column Headers with SL 2's DataGrid Matt Berseth has a winner [Read More]

» Links from Evonet Consulting
Links [Read More]


overflow: auto ftw

Posted by: Jonx on August 11, 2008 03:54 AM

I avoid using silverlight wherever I can. For example here, I've already done something similar but used an image handler to generate an image with the 45 degree text in it ;)

Well your feeds was uptoday finally, I was able to receive the latest using Outlook.
Good post Matt, and again, my first to read about Silverlight 2.0

@Muhammad -
Hi Muhammad. My post last week wasn't showing up in my reader so I toyed around with my feed settings to see if I could fix the problem. Hopefully everything is back to normal now.

@Jonx -
That's actually why I thought this would make an interesting sample. Angling column headers is not something that is easy to do using HTML/CSS - that's why you had to build a custom image handler. I was curious how much flexibility Silverlight would provide here. This post is by no means a well polished solution, but it did a good job showing me what is possible with SL.

It never hurts to familiarize with new technologies. For commercial and slaves driven projects, I would always look "arround". For example :

Thanks, Dusan

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

  • Dusan wrote: It never hurts to familiarize with new technologies. For commercial and slaves driven projects, I wo...
  • Matt Berseth wrote: @Muhammad - Hi Muhammad. My <a href='
  • Muhammad Mosa wrote: Well your feeds was uptoday finally, I was able to receive the latest using Outlook. Good post Matt,...
  • Jonx wrote: I avoid using silverlight wherever I can. For example here, I've already done something similar but ...
  • Rush wrote: overflow: auto ftw...