A missing feature in the Silverlight 3 DataGrid is the ability to add a footer row. For example, if you were trying to create a timesheet application using Silverlight, you’d want to be able to show the total number of hours for each day as shown in the image below.
The solution that I came up with is slightly hack-y but definitely works. Rather than relying on the DataGrid to provide a footer, create your own UserControl that snaps a footer row on to the bottom of the grid using a StackPanel.
<StackPanel Orientation=”Vertical”>
<data:DataGrid x:Name=”m_grid”
DataContext=”{Binding Mode=OneWay}”
ItemsSource=”{Binding Weeks, Mode=TwoWay}”
HeadersVisibility=”Column” GridLinesVisibility=”All”
AutoGenerateColumns=”False” LayoutUpdated=”m_grid_LayoutUpdated” >
<data:DataGrid.Columns>
<data:DataGridTextColumn Binding=”{Binding Task.Value, Mode=TwoWay}” Header=”Project Name”/>
<data:DataGridTextColumn Binding=”{Binding Monday.Value, Mode=TwoWay}” Header=”Monday”/>
<data:DataGridTextColumn Binding=”{Binding Tuesday.Value, Mode=TwoWay}” Header=”Tuesday” />
<data:DataGridTextColumn Binding=”{Binding Wednesday.Value, Mode=TwoWay}” Header=”Wednesday” />
<data:DataGridTextColumn Binding=”{Binding Thursday.Value, Mode=TwoWay}” Header=”Thursday” />
<data:DataGridTextColumn Binding=”{Binding Friday.Value, Mode=TwoWay}” Header=”Friday” />
<data:DataGridTextColumn Binding=”{Binding Saturday.Value, Mode=TwoWay}” Header=”Saturday” />
<data:DataGridTextColumn Binding=”{Binding Sunday.Value, Mode=TwoWay}” Header=”Sunday” />
<data:DataGridTextColumn Binding=”{Binding TotalHours, Mode=TwoWay}” Header=”Total Hours” />
</data:DataGrid.Columns>
</data:DataGrid>
<Grid HorizontalAlignment=”Stretch” VerticalAlignment=”Top” x:Name=”m_gridFooter”>
<Grid.ColumnDefinitions>
<ColumnDefinition />
<ColumnDefinition/>
<ColumnDefinition/>
<ColumnDefinition/>
<ColumnDefinition/>
<ColumnDefinition/>
<ColumnDefinition/>
<ColumnDefinition/>
<ColumnDefinition/>
</Grid.ColumnDefinitions>
<TextBlock VerticalAlignment=”Bottom” Grid.Column=”0″ HorizontalAlignment=”Left”
Text=”Totals” Padding=”5,0,0,0″ FontWeight=”Bold”/>
<TextBlock VerticalAlignment=”Bottom” Grid.Column=”1″ HorizontalAlignment=”Left”
Text=”{Binding MondayTotal, Mode=OneWay}” Padding=”5,0,0,0″ FontWeight=”Bold”/>
<TextBlock VerticalAlignment=”Bottom” Grid.Column=”2″ HorizontalAlignment=”Left”
Text=”{Binding TuesdayTotal, Mode=OneWay}” Padding=”5,0,0,0″ FontWeight=”Bold”/>
<TextBlock VerticalAlignment=”Bottom” Grid.Column=”3″ HorizontalAlignment=”Left”
Text=”{Binding WednesdayTotal, Mode=OneWay}” Padding=”5,0,0,0″ FontWeight=”Bold”/>
<TextBlock VerticalAlignment=”Bottom” Grid.Column=”4″ HorizontalAlignment=”Left”
Text=”{Binding ThursdayTotal, Mode=OneWay}” Padding=”5,0,0,0″ FontWeight=”Bold”/>
<TextBlock VerticalAlignment=”Bottom” Grid.Column=”5″ HorizontalAlignment=”Left”
Text=”{Binding FridayTotal, Mode=OneWay}” Padding=”5,0,0,0″ FontWeight=”Bold”/>
<TextBlock VerticalAlignment=”Bottom” Grid.Column=”6″ HorizontalAlignment=”Left”
Text=”{Binding SaturdayTotal, Mode=OneWay}” Padding=”5,0,0,0″ FontWeight=”Bold”/>
<TextBlock VerticalAlignment=”Bottom” Grid.Column=”7″ HorizontalAlignment=”Left”
Text=”{Binding SundayTotal, Mode=OneWay}” Padding=”5,0,0,0″ FontWeight=”Bold”/>
<TextBlock VerticalAlignment=”Bottom” Grid.Column=”8″ HorizontalAlignment=”Left”
Text=”{Binding GrandTotal, Mode=OneWay}” Padding=”5,0,0,0″ FontWeight=”Bold”/>
</Grid>
</StackPanel>
Ok. So what do you do when the DataGrid’s columns get re-sized? Basically, how do you keep the footer cells aligned and sized to match the columns in the DataGrid? The answer is pretty easy. Once you have the DataGrid and the supporting footer controls added to the StackPanel, you can create an event handler that attaches to the DataGrid’s LayoutUpdated event so that you can keep the footer columns. Whenever the LayoutUpdated event fires, iterate over the columns in the DataGrid and set the corresponding footer cell control width to the same value.
private void SyncColumnWidths(DataGrid source, Grid target)
{
if (source == null || target == null)
{
return;
}if (target.ColumnDefinitions.Count == source.Columns.Count)
{
for (int i = 0; i < target.ColumnDefinitions.Count; i++)
{
target.ColumnDefinitions[i].Width = new GridLength(source.Columns[i].ActualWidth);
}
}
}
Click here to view a running online sample.
Click here to download the source code.
-Ben
Leave a Reply