i was able to do so, altering the code from
http://www.telerik.com/community/forums/winforms/gridview/adding-controls-to-the-group-header.aspx
just needed to inherit from GridGroupContentCellElement class.
but, the checkbox is appearing on the right most side and also i cannot attach to its check state toggle event.
Thank you in advance for your help
the code for the inerited class is
public class CustomGroupHeaderCell : GridGroupContentCellElement { public event EventHandler MeCheckChanged; private RadCheckBoxElement checkbox; public CustomGroupHeaderCell(GridViewColumn column, GridRowElement row) : base(column, row) { } protected override void CreateChildElements() { base.CreateChildElements(); checkbox = new RadCheckBoxElement(); checkbox.MinSize = new System.Drawing.Size(5, 5); checkbox.ToggleStateChanged += new StateChangedEventHandler(checkbox_ToggleStateChanged); Children.Insert(0,checkbox); ApplyThemeToElement(checkbox, "Bruder"); } private void checkbox_ToggleStateChanged(object sender, StateChangedEventArgs args) { if (MeCheckChanged != null) MeCheckChanged(this, null); } protected override System.Drawing.SizeF ArrangeOverride(System.Drawing.SizeF finalSize) { SizeF size = base.ArrangeOverride(finalSize); RectangleF rect = GetClientRectangle(finalSize); if (this.checkbox != null) this.checkbox.Arrange(new RectangleF(rect.Right - this.checkbox.DesiredSize.Width - 5, rect.Top + (rect.Height - this.checkbox.DesiredSize.Height) / 2, this.checkbox.DesiredSize.Width, this.checkbox.DesiredSize.Height)); return size; } private void ApplyThemeToElement(RadItem item, string themeName) { try { if (item.ElementTree == null) return; DefaultStyleBuilder builder = ThemeResolutionService.GetStyleSheetBuilder( (RadControl)item.ElementTree.Control, item.GetThemeEffectiveType().FullName, string.Empty, themeName) as DefaultStyleBuilder; if (builder != null) item.Style = builder.Style; } catch (Exception ex) { } } } }16 Answers, 1 is accepted
The ArrangeOverride method of your custom cell defines the location of the checkbox in the cell. The implementation you have aligns it right. You can position it in the center of the cell using the following implementation of ArrangeOverride:
protected override System.Drawing.SizeF ArrangeOverride(System.Drawing.SizeF finalSize){ SizeF size = base.ArrangeOverride(finalSize); RectangleF rect = GetClientRectangle(finalSize); if (this.checkbox != null) { this.checkbox.Arrange(new RectangleF(rect.Width / 2 - this.checkbox.DesiredSize.Width / 2, rect.Top + (rect.Height - this.checkbox.DesiredSize.Height) / 2, this.checkbox.DesiredSize.Width, this.checkbox.DesiredSize.Height)); } return size;}You can attach to your custom cell events after initializing the cell in the CreateCell event of RadGridView:
private void radGridView1_CreateCell(object sender, GridViewCreateCellEventArgs e){ if (e.CellType == typeof(GridGroupContentCellElement)) { CustomGroupHeaderCell customCell = new CustomGroupHeaderCell(e.Column, e.Row); customCell.MeCheckChanged += new EventHandler(customCell_MeCheckChanged); e.CellElement = customCell; }}private void customCell_MeCheckChanged(object sender, EventArgs e){}I hope it helps you to accomplish your scenario.
Best regards,
Alexander
the Telerik team
I am having a problem implementing this solution (Attached snapshot)
First, thanks for sharing the useful information, i tweaked the code a little bit to fit in my requirements. Everything is going well, when i check the checkBox in groupRow its checking all the rows in the group (fine), but when i scroll my gridview the checkBox in that specific groupRow losses its checked state and the check comes to some other groupRow (Randomly), Moreover it actually doesn't trigger "checkbox_ToggleStateChanged" with this.
Blow is the code, please guide if i am doing something wrong?
Class:
Imports Telerik.WinControls.UIImports Telerik.WinControlsPublic Class CustomGroupHeaderCell Inherits GridGroupContentCellElement Public Event MeCheckChanged As EventHandler Public Event MeKeyUp As EventHandler Private checkbox As RadCheckBoxElement Private textbox As RadTextBoxElement Public Sub New(ByVal column As GridViewColumn, ByVal row As GridRowElement) MyBase.New(column, row) End Sub Protected Overrides Sub CreateChildElements() MyBase.CreateChildElements() checkbox = New RadCheckBoxElement() checkbox.MinSize = New System.Drawing.Size(5, 5) 'checkbox.Text = "Group)" AddHandler checkbox.ToggleStateChanged, AddressOf checkbox_ToggleStateChanged Children.Insert(0, checkbox) ApplyThemeToElement(checkbox, "Bruder") textbox = New RadTextBoxElement() AddHandler textbox.KeyUp, AddressOf textbox_keypress Children.Insert(0, textbox) End Sub Private Sub textbox_keypress(ByVal sender As Object, ByVal args As KeyEventArgs) If args.KeyCode = Keys.Enter Then pvGroupRowHeaderTextBoxValue = textbox.Text RaiseEvent MeKeyUp(Me, New KeyEventArgs(Keys.Enter)) End If End Sub Private Sub checkbox_ToggleStateChanged(ByVal sender As Object, ByVal args As StateChangedEventArgs) RaiseEvent MeCheckChanged(Me, New StateChangedEventArgs(checkbox.ToggleState)) End Sub Protected Overloads Overrides Function ArrangeOverride(ByVal finalSize As SizeF) As SizeF Dim size As SizeF = MyBase.ArrangeOverride(finalSize) Dim rect As RectangleF = GetClientRectangle(finalSize) If Me.checkbox IsNot Nothing Then Me.checkbox.Arrange(New RectangleF((finalSize.Width - Me.checkbox.DesiredSize.Width) / 1.18, (rect.Height - 20) / 2, 20, 20)) End If If Me.textbox IsNot Nothing Then Me.textbox.Arrange(New RectangleF((finalSize.Width - Me.textbox.DesiredSize.Width) / 1.1, (rect.Height - 20) / 2, 40, 20)) End If Return size End Function Private Sub ApplyThemeToElement(ByVal item As RadItem, ByVal themeName As String) Try If item.ElementTree Is Nothing Then Return End If Dim builder As DefaultStyleBuilder = TryCast(ThemeResolutionService.GetStyleSheetBuilder(DirectCast(item.ElementTree.Control, RadControl), item.GetThemeEffectiveType().FullName, String.Empty, themeName), DefaultStyleBuilder) If builder IsNot Nothing Then item.Style = builder.Style End If Catch ex As Exception End Try End SubEnd ClassForm:
Private Sub radGridView1_CreateCell(ByVal sender As Object, ByVal e As GridViewCreateCellEventArgs) Handles grdData.CreateCell If e.CellType = GetType(GridGroupContentCellElement) Then Dim customCell As New CustomGroupHeaderCell(e.Column, e.Row) AddHandler customCell.MeCheckChanged, AddressOf customCell_MeCheckChanged AddHandler customCell.MeKeyUp, AddressOf customCell_MeKeyUp e.CellElement = customCell End IfEnd SubPrivate Sub customCell_MeKeyUp(ByVal sender As Object, ByVal e As KeyEventArgs) If TypeOf grdData.CurrentRow Is GridViewGroupRowInfo Then Dim groupRow As GridViewGroupRowInfo = DirectCast(grdData.CurrentRow, GridViewGroupRowInfo) For i As Short = 0 To groupRow.Group.ItemCount - 1 groupRow.Group.Item(i).Cells("Percent").Value = pvGroupRowHeaderTextBoxValue 'Public Var to hold textbox value Next i End If pvGroupRowHeaderTextBoxValue = NothingEnd SubPrivate Sub customCell_MeCheckChanged(ByVal sender As Object, ByVal e As StateChangedEventArgs) If TypeOf grdData.CurrentRow Is GridViewGroupRowInfo Then Dim groupRow As GridViewGroupRowInfo = DirectCast(grdData.CurrentRow, GridViewGroupRowInfo) For i As Short = 0 To groupRow.Group.ItemCount - 1 groupRow.Group.Item(i).Cells("Select").Value = e.ToggleState Next i End IfEnd SubThank you for writing back.
The RadGridView control uses UI virtualization - its row and cell elements are created only for its visible data rows and columns and then they are reused while scrolling, sorting, grouping and so.on. In your case, the custom group header cells are reused in other group rows while scrolling.
You can assure the correct checkbox state of the reused cells using the following approach:
1. Mark for each group row if the cell element, associated with it, has checked state. To achieve this, you can use the ToggleStateChanged event handler of the checkbox in your custom cell:
Private Sub checkbox_ToggleStateChanged(ByVal sender As Object, ByVal args As StateChangedEventArgs) RaiseEvent MeCheckChanged(Me, New StateChangedEventArgs(checkbox.ToggleState)) Me.RowInfo.Tag = If(Me.checkbox.ToggleState = Enumerations.ToggleState.On, True, False)End Sub2. Override the SetContentCore method in your custom cell to update the checkbox state of the reused cells:
Protected Overrides Sub SetContentCore(ByVal value As Object) MyBase.SetContentCore(value) Me.checkbox.ToggleState = If(Me.RowInfo.Tag, Enumerations.ToggleState.On, Enumerations.ToggleState.Off)End SubI hope it helps.
Best regards,
Alexander
the Telerik team
Register for the Q2 2011 What's New Webinar Week. Mark your calendar for the week starting July 18th and book your seat for a walk through of all the exciting stuff we will ship with the new release!
How can i access this custom group header cell from the grid valuechanged event.
I want to change checkbox state, according to items in its group
Thank you
You can access the custom cell element from the ValueChanged event of RadGridView using the GetRowElement method of GridTableElement. Further, get the header row element and iterate through its cells to find the custom cell element:
private void radGridView1_ValueChanged(object sender, EventArgs e){ GridViewGroupRowInfo groupRow = this.radGridView1.CurrentRow.Parent as GridViewGroupRowInfo; if (groupRow == null) { return; } GridGroupHeaderRowElement groupRowElement = this.radGridView1.TableElement.GetRowElement(groupRow) as GridGroupHeaderRowElement; if (groupRowElement == null) { return; } CustomGroupHeaderCell customCell = null; foreach (GridCellElement cell in groupRowElement.VisualCells) { customCell = cell as CustomGroupHeaderCell; if (customCell != null) { break; } } if (customCell != null) { // ... }}Please note that due to the virtualized user interface of RadGridView, the above code snippet will work only if the group row is visible. In the opposite case its row element will not be created and you should use the group data row. As an idea for suitable approach, you could review my answer to the Aquariens's question.
Best regards,
Alexander
the Telerik team
Explore the entire Telerik portfolio by downloading the Ultimate Collection trial package. Get now >>
Could you please let us know what steps you have taken in order to follow the approach? I am enclosing a sample project which demonstrates that this scenario works as expected.
Regards,
Svett
the Telerik team
Thanks for the solution above. I do have one question though.
I have used the following code to allow checkboxes within a group header. I need to implement a "Select All" option which will check or uncheck each of the checkboxes. How can I achieve this?
public class CustomGroupCellHeader : GridGroupContentCellElement{ public event EventHandler CheckChanged; public RadCheckBoxElement CheckBox { get; set; } public GridViewColumn Column { get; set; } public GridRowElement Row { get; set; } public Guid OperatorId { get; set; } public string OperatorName { get; set; } public CustomGroupCellHeader(GridViewColumn column, GridRowElement row) : base(column, row) { this.Column = column; this.Row = row; } protected override void CreateChildElements() { base.CreateChildElements(); this.CheckBox = new RadCheckBoxElement(); this.CheckBox.MinSize = new Size(10, 10); this.CheckBox.ToggleStateChanged += new StateChangedEventHandler(_checkbox_ToggleStateChanged); this.Children.Insert(0, this.CheckBox); } protected override System.Drawing.SizeF ArrangeOverride(System.Drawing.SizeF finalSize) { SizeF size = base.ArrangeOverride(finalSize); RectangleF rect = GetClientRectangle(finalSize); if (this.CheckBox != null) { this.CheckBox.Arrange(new RectangleF(rect.X, rect.Y + 5, 10, 10)); } return size; } private void _checkbox_ToggleStateChanged(object sender, StateChangedEventArgs args) { if (this.CheckChanged != null) { this.CheckChanged(this, null); } }}You can change the values of the child rows of particular group row by performing the following code snippet in the ToggleStateChanged event handler of the check box element:
private void checkbox_ToggleStateChanged(object sender, StateChangedEventArgs args){ if (MeCheckChanged != null) MeCheckChanged(this, null); bool value = this.checkbox.ToggleState == ToggleState.On; this.RowInfo.Tag = value; ToggleRows(this.RowInfo, value);}private void ToggleRows(GridViewRowInfo gridViewRowInfo, bool value){ foreach (GridViewRowInfo rowInfo in gridViewRowInfo.ChildRows) { if (rowInfo is GridViewGroupRowInfo) { ToggleRows(rowInfo, value); } else { rowInfo.Cells["BooleanColumn"].Value = value; } }}I hope this helps.
Kind regards,
Svett
the Telerik team
I've followed the zipped example. What i'd like to accomplish is on a button click, loop through all the header rows, see if they're checked, and perform a task.
I'm simply trying to loop the header rows and read their state from a button click event handler.
I'm doing this by looping though my header rows like so:
GridViewGroupRowInfo gi = rgvMain.Groups[i].GroupRow
bool test = (bool)gi.Tag;
My problem is that the tag value is randomly null at times.
One other thing is, i'm using a datatable as the data source of the grid. The application can run in different modes, depending on the mode selected, the application will clear and add rows to the data table.
I believe I've got this working. The data table was screwing things up. It seems I have to rebind the datatable to the grid after I make changes to the rows of that datatable.
I didn't seem to have to do that up unitl I added the Custom checkbox group row.
Okay, I think i'm on to something. It appears my issue isn't the underlying datatable or the binding.
In a post above Alexander wrote:
The RadGridView control uses UI virtualization - its row and cell elements are created only for its visible data rows and columns and then they are reused while scrolling, sorting, grouping and so.on. In your case, the custom group header cells are reused in other group rows while scrolling.
I have 10 groups with somewhere near 20 data rows per group. On the initial display I want the first group expanded and the rest collapsed. Some of the collapsed groups don't fit in the grid window and get clipped.
When I try to iterate the header row using
GridViewGroupRowInfo gi = rgvMain.Groups[i].GroupRow
bool test = (bool)gi.Tag;
The Tag values are null for the group header rows that are off screen/clipped. If I initially display all groups collapsed so that they're displayed on the screen, everything works fine.
Is there a trick to get around this?
​
Thank you for writing.
By default, GridViewGroupRowInfo.Tag property is set to null. If you toggle the checkbox in the group row, you will update the GridViewGroupRowInfo.Tag property value to true. However, for the rest of the group rows that are never checked, this default null value remains. That is why you may obtain a row with Tag = null. Indeed, RadGridView uses virtualization, but only for its visual elements. GridViewGroupRowInfo is a data object and it is not reused. Hence, it is not supposed to obtain different Tag value for a group when it is in the visible grid area or not.
Please find attached a sample project. When the button is clicked, the Tag value is printed in the Output window. If you are still experiencing any further difficulties, it would be greatly appreciated if you explain in details what changes should I perform in order to replicate the issue locally. Alternatively, you can open a support ticket and provide a sample project demonstrating the undesired behavior.
I hope this information helps. Should you have further questions I would be glad to help.
Dess
Telerik
I've attached an example project that further explains the issue I'm running into. Rename the jpg. to .zip
To reproduce, run the app, click start. Then click change mode, then click start.
Thank you for responding, I really appreciate it.
Thank you for writing back.
The provided sample project is greatly appreciated. You should clear the RadGridView.GroupDescriptors collection when clearing the DataTable.Rows collection. Thus, when grouping the grid, the SetContentCore method for the custom GridGroupContentCellElement will be triggered for each row:
private void radButton1_Click(object sender, EventArgs e){ _mainTable.Rows.Clear(); this.rgvMain.GroupDescriptors.Clear(); for (int i = 0; i < _testCount; i++) { for (int j = 0; j < 8; j++) { DataRow dr = _mainTable.NewRow(); dr.ItemArray = new object[] { true, "Other Test " + j.ToString(), i }; _mainTable.Rows.Add(dr); } } rgvMain.GroupDescriptors.Add(new Telerik.WinControls.Data.GroupDescriptor("TagIndex")); rgvMain.MasterTemplate.GroupComparer = new GroupComparer();}I hope this information helps. If you have any additional questions, please let me know.
Regards,
Dess
Telerik
