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.UI
Imports
Telerik.WinControls
Public
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
Sub
End
Class
Form:
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
If
End
Sub
Private
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 =
Nothing
End
Sub
Private
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
If
End
Sub
Thank 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
Sub
2. 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
Sub
I 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