This time I have question about a feature I was investigating, and wanted to know if there was a known easy way to handle this:
I have a bound data grid using the telerik radcontrols, and have thus far been greatly impressed with how effectively they work. However, presently we have been looking to implement slightly more complex headers than what seems readily available by the controls as is.
In other words, I would like to provide a large over-header, under which several smaller headers are contained, which in turn are keyed to respective columns. The ultimate goal will likely have two over-headers each with four normal headers and columns within them. The over-headers require nothing special, but we would like to maintain the Telerik features with the normal headers (Ie: column selection, filtering, etc.).
Now, thus far I have managed to partially accomplish this through the use of header and cell templates conained within styles. I create a column, set the template for the column, and it creates the general look we are searching for.
This approach is far from perfect, however. Using these templates, the columns have lost their ability to select by column, as well as the option for filtering. The data also seems to resize poorly in connection to the headers, making it very easy for them to become misaligned.
I figure this has to do with the fact that I have effectively added one column to my grid, and created multiple columns and headers within this single column. It's easy to see why this would not work as well as one might hope. If it is possible, I would like some guidence on a better way to go about creating over-headers that would provide me with full use of the telerik and silverlight features. Code examples can be provided, should they be desired.
Thank everyone for their time,
Philip Coffey
31 Answers, 1 is accepted
We are planning to add this as an internal feature of the RadGridView for future releases.
However there is a way to accomplish the task , even with the current version.
I have prepared a small sample for you. Here we have a small user control to act as a column group header.
In the RowLoaded event we inject this control into the header. What it needs to know is the first, the last column of the group and the group caption ( these passed in the constructor of the user control) .
The user control itself handles row resizing and aligns itself over the right columns.
With this 'hacky' approach we achieve the main goal of maintaining the presence of the full features of RadGridView columns. Please not that it will be useful to disable the columns reordering in this case as it may lead to mixing column groups and unpredictable layout.
The sample illustrates only one column group but you are free to add as many ColumnGroup controls as you need.
Let me know of you need further assistance adapting this to your project.
All the best,
Pavel Pavlov
the Telerik team
Instantly find answers to your questions on the new Telerik Support Portal.
Check out the tips for optimizing your support resource searches.
I am still having one small issue, however. When I imported the control it worked perfectly, except for the fact that the over-header is shifted a few pixels too far to the right. This seems to be a static offset, as the distance seems to be the same regardless of how big the columns are. Is there a simple way to correct for this?
Thanks again for your invaluable help!
Philip Coffey
Again, thank you for your help. This will be a useful tool to use until full support for over-headers is implemented.
Philip Coffey
Please take the updated version of the user control from the attached project. I have added a few lines of code to take care of the horizontal scroll .
Kind regards,
Pavel Pavlov
the Telerik team
Instantly find answers to your questions on the new Telerik Support Portal.
Check out the tips for optimizing your support resource searches.
I made it compile with Q3 and added some improvements
- groups are created automatically when the columns have UniqueName in the form of Multi[GroupName]ColumnName. (Would be nice if the columns had a Tag property!!!)
- headers are replaced so that the text doesn't go under the group control when resized (doesn't work with virtualization)
But there is still the problem where when scrolling, the group control shows over the frozen columns or RowIndicator column. Any ideas how to bypass this?
Also, when do you expect multi headers to be natively supported by the grid?
Page.xaml
<UserControl x:Class="ColumnGroups.Page" |
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation" |
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" |
Width="400" Height="300" |
xmlns:telerik="clr-namespace:Telerik.Windows.Controls;assembly=Telerik.Windows.Controls.GridView" |
> |
<Grid x:Name="LayoutRoot" Background="White"> |
<telerik:RadGridView x:Name="RadGridView1" AutoGenerateColumns="False" |
EnableColumnVirtualization="false"> |
<telerik:RadGridView.Columns> |
<telerik:GridViewDataColumn UniqueName="Age" DataMemberBinding="{Binding Age}" Header="Age"/> |
<telerik:GridViewDataColumn UniqueName="Multi[Name]FirstName" DataMemberBinding="{Binding FirstName}" Header="FirstName"/> |
<telerik:GridViewDataColumn UniqueName="Multi[Name]LastName" DataMemberBinding="{Binding LastName}" Header="LastName"/> |
<telerik:GridViewDataColumn UniqueName="Multi[Name1]FirstName" DataMemberBinding="{Binding FirstName}" Header="FirstName"/> |
<telerik:GridViewDataColumn UniqueName="Multi[Name1]LastName" DataMemberBinding="{Binding LastName}" Header="LastName"/> |
<telerik:GridViewDataColumn UniqueName="Multi[Name2]FirstName" DataMemberBinding="{Binding FirstName}" Header="FirstName"/> |
<telerik:GridViewDataColumn UniqueName="Multi[Name2]LastName" DataMemberBinding="{Binding LastName}" Header="LastName"/> |
<telerik:GridViewDataColumn UniqueName="Multi[Name3]FirstName" DataMemberBinding="{Binding FirstName}" Header="FirstName"/> |
<telerik:GridViewDataColumn UniqueName="Multi[Name3]LastName" DataMemberBinding="{Binding LastName}" Header="LastName"/> |
</telerik:RadGridView.Columns> |
</telerik:RadGridView> |
</Grid> |
</UserControl> |
Page.xaml.cs
using System; |
using System.Collections.Generic; |
using System.Linq; |
using System.Net; |
using System.Windows; |
using System.Windows.Controls; |
using System.Windows.Documents; |
using System.Windows.Input; |
using System.Windows.Media; |
using System.Windows.Media.Animation; |
using System.Windows.Shapes; |
using Telerik.Windows.Controls.GridView; |
using Telerik.Windows.Controls; |
namespace ColumnGroups |
{ |
public partial class Page : UserControl |
{ |
public Page() |
{ |
InitializeComponent(); |
this.RadGridView1.ItemsSource = Person.GetSampleListOfPersons(); |
this.RadGridView1.RowLoaded += new EventHandler<Telerik.Windows.Controls.GridView.RowLoadedEventArgs>(RadGridView1_RowLoaded); |
} |
void RadGridView1_RowLoaded(object sender, Telerik.Windows.Controls.GridView.RowLoadedEventArgs e) |
{ |
if (e.Row is GridViewHeaderRow) |
{ |
int groupIndex = -1; |
int startColumn = -1; |
string groupName = ""; |
for (int i = 0; i < RadGridView1.Columns.Count; i++) |
{ |
GridViewColumn col = RadGridView1.Columns[i]; |
if (col.UniqueName == null || !col.UniqueName.StartsWith("Multi[")) |
continue; |
if (groupName == "") |
{ |
groupName = col.UniqueName.Substring(6, col.UniqueName.IndexOf("]") - 6); |
startColumn = i; |
} |
int nextGroupIndex = -1; |
string nextGroupName = ""; |
if (i + 1 < RadGridView1.Columns.Count) |
{ |
GridViewColumn nextCol = RadGridView1.Columns[i + 1]; |
if (nextCol.UniqueName.StartsWith("Multi[")) |
nextGroupName = nextCol.UniqueName.Substring(6, nextCol.UniqueName.IndexOf("]") - 6); |
} |
if (groupName != nextGroupName) |
{ |
int endColumn = i; |
ColumnGroup colGroup = new ColumnGroup((GridViewHeaderRow)e.Row, RadGridView1.Columns[startColumn], RadGridView1.Columns[endColumn], groupName); |
groupName = ""; |
startColumn = -1; |
} |
} |
} |
} |
} |
public class Person |
{ |
public Person(int age, string firstName, string lastName) |
{ |
this.Age = age; |
this.FirstName = firstName; |
this.LastName = lastName; |
} |
public int Age { get; set; } |
public string FirstName { get; set; } |
public string LastName { get; set; } |
public static List<Person> GetSampleListOfPersons() |
{ |
var persons = new List<Person> |
{ |
new Person(30, "John", "Smith"), |
new Person(29, "Jane", "Smith"), |
new Person(30, "Peter", "Smith") |
}; |
return persons; |
} |
} |
} |
ColumnGroup.xaml
<UserControl x:Class="ColumnGroups.ColumnGroup" |
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation" |
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" |
> |
<Grid x:Name="LayoutRoot" Height="20"> |
<Grid.Background> |
<LinearGradientBrush EndPoint="0.5,1" StartPoint="0.5,0"> |
<GradientStop Color="#FFaaaaaa" Offset="0"/> |
<GradientStop Color="#FFbbbbbb" Offset="0.37"/> |
<GradientStop Color="#FFeeeeee" Offset="0.861"/> |
</LinearGradientBrush> |
</Grid.Background> |
<Grid.RowDefinitions> |
<RowDefinition Height="*" /> |
<RowDefinition Height="2" /> |
</Grid.RowDefinitions> |
<Grid.ColumnDefinitions> |
<ColumnDefinition Width="*" /> |
</Grid.ColumnDefinitions> |
<TextBlock x:Name="CaptionTextBlock" HorizontalAlignment="Center" Text="{Binding }" VerticalAlignment="Center" /> |
<Rectangle Width="Auto" Fill="Black" Grid.Row="1" /> |
</Grid> |
</UserControl> |
ColumnGroup.xaml.cs
using System; |
using System.Collections.Generic; |
using System.Linq; |
using System.Net; |
using System.Windows; |
using System.Windows.Controls; |
using System.Windows.Controls.Primitives; |
using System.Windows.Documents; |
using System.Windows.Input; |
using System.Windows.Media; |
using System.Windows.Media.Animation; |
using System.Windows.Shapes; |
using Telerik.Windows.Controls; |
using Telerik.Windows.Controls.GridView; |
namespace ColumnGroups |
{ |
public partial class ColumnGroup : UserControl |
{ |
public ColumnGroup(GridViewHeaderRow row, GridViewColumn firstColumn, GridViewColumn lastColumn, string caption) |
{ |
InitializeComponent(); |
this.FirstColumn = firstColumn; |
this.LastColumn = lastColumn; |
this.Caption = caption; |
this.LastColumn = lastColumn; |
this.Caption = caption; |
this.HorizontalAlignment = HorizontalAlignment.Left; |
this.VerticalAlignment = VerticalAlignment.Top; |
this.Loaded += new RoutedEventHandler(ColumnGroup_Loaded); |
this.Indent = 0; |
this.SetValue(Grid.ColumnProperty, firstColumn.DisplayIndex + 1); |
this.SetValue(Grid.ColumnSpanProperty, lastColumn.DisplayIndex - firstColumn.DisplayIndex + 1); |
row.ChildrenOfType<Grid>()[0].Children.Add(this); |
} |
public static readonly DependencyProperty CaptionProperty = |
DependencyProperty.Register("Caption", typeof(string), typeof(ColumnGroup), new PropertyMetadata(String.Empty, CaptionChanged)); |
public string Caption |
{ |
get |
{ |
return (string)this.GetValue(CaptionProperty); |
} |
set |
{ |
this.SetValue(CaptionProperty, value); |
} |
} |
public double Indent { get; set; } |
private RadGridView parentGrid; |
void ColumnGroup_Loaded(object sender, RoutedEventArgs e) |
{ |
//Note: Comment this and it will work with ColumnVirtualization |
var headerCells = ((UIElement)Parent).ChildrenOfType<GridViewHeaderCell>().ToList(); |
for (int i = FirstColumn.DisplayIndex; i <= LastColumn.DisplayIndex; i++) |
{ |
var headerCell = headerCells.Where(h => h.TabIndex == i).FirstOrDefault(); |
var contentPresenter = headerCell.ChildrenOfType<AlignmentContentPresenter>().FirstOrDefault(); |
contentPresenter.VerticalAlignment = VerticalAlignment.Top; |
var content = contentPresenter.Content; |
contentPresenter.Content = null; |
//MessageBox.Show(content.ToString()); |
StackPanel panel = new StackPanel { Orientation = Orientation.Vertical }; |
//TODO: should get the height of LayoutRoot |
panel.Children.Add(new ContentPresenter() { Height = 20 }); |
panel.Children.Add(new AlignmentContentPresenter() { TextWrapping = TextWrapping.Wrap, Content = content }); |
contentPresenter.Content = panel; |
} |
this.parentGrid = this.ParentOfType<RadGridView>(); |
var scrollViewer = this.parentGrid.ChildrenOfType<GridViewScrollViewer>().FirstOrDefault(); |
scrollViewer.ScrollChanged += new ScrollChangedEventHandler(scrollViewer_ScrollChanged); |
foreach (GridViewColumn column in this.parentGrid.Columns) |
{ |
column.PropertyChanged += new System.ComponentModel.PropertyChangedEventHandler(column_PropertyChanged); |
} |
AlignToColumns(); |
} |
double horizontalScrollOffset; |
void scrollViewer_ScrollChanged(object sender, ScrollChangedEventArgs e) |
{ |
//this.horizontalScrollOffset = e.HorizontalOffset; |
this.AlignToColumns(); |
} |
static void CaptionChanged(DependencyObject sender, DependencyPropertyChangedEventArgs args) |
{ |
((ColumnGroup)sender).CaptionTextBlock.Text = (string)args.NewValue; |
} |
void column_PropertyChanged(object sender, System.ComponentModel.PropertyChangedEventArgs e) |
{ |
if (e.PropertyName == "ActualWidth") |
this.AlignToColumns(); |
} |
private void AlignToColumns() |
{ |
double startPosition = 0; |
double endPosition = 0; |
double offset = 0; |
foreach (GridViewColumn column in this.parentGrid.Columns) |
{ |
if (column == this.FirstColumn) |
{ |
startPosition = offset; |
} |
offset += column.ActualWidth; |
if (column == this.LastColumn) |
endPosition = offset; |
} |
this.Margin = new Thickness(startPosition + Indent - horizontalScrollOffset, 0, 0, 0); |
this.Width = endPosition - startPosition - 2; |
} |
public GridViewColumn FirstColumn { get; set; } |
public GridViewColumn LastColumn { get; set; } |
} |
} |
Since there are a few requests about column groupings with the new version ,
I am going to prepare a substitute solution with the latest version and post it online.
I will drop you a line as soon as it is uploaded.
Meanwhile we are already researching how to provide it as an integrated feature for RadGridView.
The most possible release time would be Q1 2010.
Regards,
Pavel Pavlov
the Telerik team
Instantly find answers to your questions on the new Telerik Support Portal.
Watch a video on how to optimize your support resource searches and check out more tips on the blogs.
Hi all,
A quick update - I have uploaded a sample illustrating how to achive this with the latest version of RadGridView.
Please have a look here
All the best,
the Telerik team
Instantly find answers to your questions on the new Telerik Support Portal.
Watch a video on how to optimize your support resource searches and check out more tips on the blogs.
You may place custom content in the footer row , replacing the original one via the RadGridView.FooterRowStyle. Within the Style you can set a template to the footer row and add anything you need as UI elements.
Greetings,
Pavel Pavlov
the Telerik team
Instantly find answers to your questions on the new Telerik Support Portal.
Watch a video on how to optimize your support resource searches and check out more tips on the blogs.
*** Update ***
The sample from Pavel does work on Q1 2010.
Thanks.
Since this is not an internal logic of RadGridView , you will need to implement this manually.
Within the RadGridView.Exporting event you have access to the stream being exported. There you can plug in your modifications to add any additional elements you wish to appear in the exported document.
I am afraid I can not provide an example since this would be a code highly dependant on your custom scenario.
Kind regards,
Pavel Pavlov
the Telerik team
Do you want to have your say when we set our development plans? Do you want to know when a feature you care about is added or when a bug fixed? Explore the Telerik Public Issue Tracking system and vote to affect the priority of the items.
On the side note, I ran into a problem that after exporting when I looked at the excel, the content in the viewing area came properly but all the columns that are out of viewing area were so small in column width. Is it possible to expand them by default?
Thanks again.
The exported data is in ExcelML format , it is XML based and tables are similar to the HTML tables .
You may find some code manipulating EXcelML in this online article.
Best wishes,
Pavel Pavlov
the Telerik team
Do you want to have your say when we set our development plans? Do you want to know when a feature you care about is added or when a bug fixed? Explore the Telerik Public Issue Tracking system and vote to affect the priority of the items.
How about the second part of the question I have in the previous one?
Also are you planning to include the common headers part of the grid itself and also excel export would export the data without us having to write extra code? If so, which release will have this feature?
Thanks,.
On the second question - we get a lot of requests for it and we are already working on integrating this behavior as a feature.
However the task is heavy and not straightforward so it is a bit early to commit to a specific release.
Kind regards,
Pavel Pavlov
the Telerik team
Do you want to have your say when we set our development plans? Do you want to know when a feature you care about is added or when a bug fixed? Explore the Telerik Public Issue Tracking system and vote to affect the priority of the items.
"On the side note, I ran into a problem that after exporting when I looked at the excel, the content in the viewing area came properly but all the columns that are out of viewing area were so small in column width. Is it possible to expand them by default?"
Now that I am looking at the XML I see all the column width set to 20.
Thanks.
Thanks for being active in this thread.
A very small question: Is this feature natively supported in 2010 Q1 or Q2? If not, any idea when is it expected to?
Regards,
Kinjal.
We will do our best to add this for Q3 2010 (November) - currently this is not built-in.
Kind regards,Vlad
the Telerik team
Is there an update on this, will it be available in November (Q3)?
Unfortunately we will unable to deliver this as built-in feature in Q3 2010.
All the best,Vlad
the Telerik team
In my case columns visibility under CommonHeader is dynamic. But when a part of columns are hidden, common header size remains the same. And I also have undesirable offset between actual column border and common header border. I use controls ver. 2010.3.1110.1040.
Any directions?
Thanks,
Juliana
I believe some additional code is required in the common header control as originally it was not intended to cover such case.
I assume you are using the approach from my blog .
However I am not able to give you a descent solution , without seeing your particular implementation including the way you hide columns.
All the best,
Pavel Pavlov
the Telerik team
I use the same implementation as is in your sample.
I hide columns by setting IsVisible column property. My solution was to set column width property to 0 as well because grid columns width in SecondaryHeader is bound to gridviewcolumn width. But it starts working only after setting MinWidth gridview column property to 0 as well cause by default it's higher.
And issue with offset was solved by binding first grid column visibility in SecondaryHeader to gridview RowIndicatorVisibility.
Thanks,
Juliana
Glad to hear you have found a way to solve the issue.
All the best,
Pavel Pavlov
the Telerik team
I have same problem. I want to show a common header to 4 columns. and I am using below code. As I am using latest telerik dlls (2012)
it is telling me that indexing is not allowed. compile time error.
Please help
void RadGridView1_RowLoaded(object sender, Telerik.Windows.Controls.GridView.RowLoadedEventArgs e)
{
if (e.Row is GridViewHeaderRow)
{
ColumnGroup colGroup = new ColumnGroup(this.RadGridView1.Columns[1],this.RadGridView1.Columns[2], "Column group 1");
e.Row.ChildrenOfType<Grid>()[0].Height = 50;
e.Row.ChildrenOfType<Grid>()[0].Children.Add(colGroup);
}
}
Try to write something like this e.Row.ChildrenOfType<Grid>().First().Height = 50; Don't forget to add using System.Linq; namespace.
e.Row.ChildrenOfType<
Grid>().First().Children.Add(colGroup);
But it is not binding to my grid. Please help!.
<
telerik:RadGridView Grid.Row="2" Name="grd1" GridLinesVisibility="Vertical" MaxHeight="700" Width="700"
EnableColumnVirtualization="True" EnableRowVirtualization="True" HorizontalAlignment="Stretch" VerticalAlignment="Stretch"
RowIndicatorVisibility="Collapsed" ShowGroupPanel="False" ShowGroupFooters="True"
CanUserFreezeColumns="False" CanUserResizeColumns="False"
ShowColumnFooters="True" CanUserReorderColumns="False"
AutoGenerateColumns="False" IsReadOnly="True"
ItemsSource="{Binding Details,Mode=TwoWay}"
Visibility="{Binding GridVisibility_AvgDtls}">
<i:Interaction.Triggers>
<i:EventTrigger EventName="RowLoaded">
<cmd:EventToCommand Command="{Binding DetailsGridRowLoadedEvent}" PassEventArgsToCommand="True"/>
</i:EventTrigger>
</i:Interaction.Triggers>
<telerik:RadGridView.Columns>
<telerik:GridViewDataColumn Header="Code" DataMemberBinding="{Binding code}" HeaderCellStyle="{StaticResource HeaderCellStyleHeight50}">
<telerik:GridViewDataColumn.AggregateFunctions>
<telerik:CountFunction Caption="Total: "/>
</telerik:GridViewDataColumn.AggregateFunctions>
</telerik:GridViewDataColumn>
<telerik:GridViewDataColumn Header="Parent" DataMemberBinding="{Binding parentid}" Background="Wheat"/>
<telerik:GridViewDataColumn Header="ID" DataMemberBinding="{Binding id}" Background="LightBlue" />
<telerik:GridViewDataColumn Header="Min" HeaderCellStyle="{StaticResource headerCellStyle1}" DataMemberBinding="{Binding Min,Converter={StaticResource decimalValueConverter}}" TextAlignment="Right"/>
<telerik:GridViewDataColumn Header="Max" HeaderCellStyle="{StaticResource headerCellStyle2}" DataMemberBinding="{Binding Max,Converter={StaticResource decimalValueConverter}}" TextAlignment="Right" />
<telerik:GridViewDataColumn Header="Avg" HeaderCellStyle="{StaticResource headerCellStyle2}" DataMemberBinding="{Binding Avg,Converter={StaticResource decimalValueConverter}}" TextAlignment="Right" />
<telerik:GridViewDataColumn Header="Last" HeaderCellStyle="{StaticResource headerCellStyle3}" DataMemberBinding="{Binding Last,Converter={StaticResource decimalValueConverter}}" TextAlignment="Right" />
 
<telerik:GridViewDataColumn Header="Data" DataMemberBinding="{Binding Data}" TextAlignment="Left" />
<telerik:GridViewDataColumn Header="source" DataMemberBinding="{Binding source}" TextAlignment="Left" />
</telerik:RadGridView.Columns>
</telerik:RadGridView>