Child rows do not update visually when the parent row is expanded in a Hierarchical RadGridView

2 Answers 24 Views
GridView Styling
João
Top achievements
Rank 1
Iron
João asked on 28 Jul 2025, 12:00 PM

Hello.

I am developing a form that has a radgrid with hierarchy and in some instances, the parent row makes changes to the child rows and vice versa.
In my case, I have the functionalities below (Among others):

  1. Both the parent and child rows have a "Check" column.
  2. The relation between the 2 is done through an "Id" column which in my DataSource, is a Guid (The column in both templates is a textbox).
  3. Every time a row is checked (Parent or child), the background changes color.
  4. Every time I check a parent row, the corresponding child rows are also checked (Which then change color).
    Same happens if I uncheck the parent row which then unchecks the corresponding child rows and resets the background color.
    This behavior is managed in the CellValueChanged event of the RadGridView.
    Checking and unchecking the child rows according to the parent, is being done directly in the DataSource which in my case is a DataTable. But the same behavior happens if I edit the grid rows directly.
  5. In the parent template, the Check column has the property EnableHeaderCheckBox set to true.
  6. The color rule is managed by a ExpressionFormattingObject added to the ConditionalFormattingObjectList property of the Check column. I also tried to set up the color rules in the Formating events but the problem I will explain below persists. Still, the color is not the only problem.

My problem here is that everything works correctly if I check and uncheck the parent rows individually. Whether they expanded or not. Depending on the check action in the parent row, the child rows update visually both in their "Check" state and in the color rule.



However, if I check/uncheck all the parent columns from the header check box, the child rows do not update visually unless I close the parent row and expand it again or if I click on all rows/scroll down or up to force them to update.
Like in this print for example. The first parent row was expanded when I checked the header check box. It was suppose to check the child rows and update the color. They are checked internally in the datasource.



There are also some issues with the header check in the child rows. In this case, I do not have the EnableHeaderCheckBox = true in the GridViewTemplate but the problem is similar. Checking all the child rows of a certain parent row through the header check, updates the value on the "Chk" column but does not update it visually with the color.

There is also a problem where in some instances if I check all the parent rows through the header check box, their color does not update.
This happens regardless of the parent rows being expanded or not.



I have to click on each one or scroll or expand them to force the visual update.
The problem seems to be related to the actual visual update. I tried multiple things to for the update but none helped. Also tried forcing the rows to expand if they were collapsed not none helped.

This is all I tried.


On all the examples, I checked the data in the datasource and everything is updated correctly. It is just the visuals that are not updating automatically.

To help understand the problem, I attached a little example with a solution similar to what I have, where this issue can be replicated.
I am using Telerik Winforms V2024.4.1113.462.

Thank you in advance for any answer.
João Carvalho

2 Answers, 1 is accepted

Sort by
0
Dinko | Tech Support Engineer
Telerik team
answered on 31 Jul 2025, 09:35 AM

Hello João,

Thank you for the provided project.

I have examined the project, and the changes applied in the DataTable are not reflected. What you can do is to call the AcceptChanges() of the _dtChildData DataTable to force the updates. 

Additionally, you will need to extend the logic in the CellValueChanged. Currently, it checks for the current row, but if we select a child row and click on the header checkbox, the CellValueChangedChildRows will be called. You can consider subscribing to the HeaderCellToggleStateChanged event and moving some of the logic to that event handler. 

 private void CellValueChangedParentRows(GridViewCellEventArgs e)
 {
     DataRow selectedParentRow = GetSelectedRow(e.Row);
   
     //If the changed column is "Chk", checks or unchecks all child rows accordingly.
     if (selectedParentRow != null && e.Column.FieldName == "Chk")
     {
         foreach (DataRow childRow in _dtChildData.Select($"ParentId = '{selectedParentRow["ParentId"]}'"))
         {
             childRow["Chk"] = selectedParentRow["Chk"];
         }

         _dtChildData.AcceptChanges();
     }
 }

Regards,
Dinko | Tech Support Engineer
Progress Telerik

Love the Telerik and Kendo UI products and believe more people should try them? Invite a fellow developer to become a Progress customer and each of you can get a $50 Amazon gift voucher.

0
João
Top achievements
Rank 1
Iron
answered on 31 Jul 2025, 12:43 PM | edited on 31 Jul 2025, 12:44 PM
Hello Dinko.

Thank you for the answer.

About the HeaderCellToggleStateChange, I implemented something like this but it does not seem to help. The behavior is the same. The rows on both child and parent rows datatables are updated but the corresponding grid rows are not updated visually.
By adding this event, the CellValueChanged event seems to no longer be fired for each parent row. Is this a correct behavior?

private void radGridView1_HeaderCellToggleStateChanged(object sender, GridViewHeaderCellEventArgs e)
{
    bool check = e.State == ToggleState.On;

    // Checks/unchecks all parent rows.
    foreach (DataRow parentRow in _dtParentData.Rows)
    {
        parentRow["Chk"] = check;

        // Checks/unchecks the corresponding child rows in the grid.
        foreach (DataRow childRow in _dtChildData.Select($"ParentId = '{parentRow["ParentId"]}'"))
        {
            childRow["Chk"] = check;
        }
    }
}
About the CellValueChanged event (Without the implementation of the HeaderCellToggleStateChange), I checked the _dtChildData datatable at the end of the CellValueChangedParentRows function and the data is updated. The "Chk" column is true in this case.
Example




As for the AcceptChanges, I cannot use that. In my real application I have a logic that will then cycle the DataTable and check which rows were updated, deleted and added to then proceed with the corresponding logic in the database. If I use the AcceptChanges method, I lose that mechanism because the row state will be reset.

The idea is to then do something like this to process the data:
private bool ProcessData()
{
    // Handle added and modified data rows
    foreach (DataRow row in _dtChildData.Select("Chk = True"))
    {
        switch (row.RowState)
        {
            case DataRowState.Added:
                // Process new parent rows
                break;
            case DataRowState.Modified:
                // Process modified parent rows
                break;
        }
    }

    // Handle deleted data rows
    foreach (DataRow row in _dtChildData.AsEnumerable().Where(r => r.RowState == DataRowState.Deleted))
    {
        // Process deleted child rows
    }
}

In my case, I need to do that for the child and parent rows. I need to handle Added, Modified and Deleted rows.
Keep in mind that the color rule is being done automatically with the use of ExpressionFormattingObject.

The main problem is that the check and color rules are not being updated visually.
Is there a function that I can call to force the grid to update visually? Because like explained initially, if click on a row, it updates or if I scroll up and down enough for it to "leave render". Once the row appears on "render" again, it updates.

I reattached the updated project with the addition of the radGridView1_HeaderCellToggleStateChanged and the ProcessData function which in my real case will be called in a "Process" button or something similar.

I will wait for an answer.
Thank you.
Dinko | Tech Support Engineer
Telerik team
commented on 05 Aug 2025, 12:14 PM

I appreciate the additional project. 

The CellValueChanged will be triggered when the user changes the value of a cell. Changing the value in the data row will not trigger the event.

In general, in hierarchy mode, each expand child grid have its own TableElement. Updating the main TableElement will not update the child TableElements. What we can do is to get the child TableElements and force their refresh. Here is what I came up with:

private void radGridView1_HeaderCellToggleStateChanged(object sender, GridViewHeaderCellEventArgs e)
{
    bool check = e.State == ToggleState.On;

    // Checks/unchecks all parent rows.
    foreach (GridViewHierarchyRowInfo parentRow in this.radGridView1.Rows)
    {
        var dataRowItem = parentRow.DataBoundItem as DataRowView;
        dataRowItem.Row["Chk"] = check;


        foreach (GridViewHierarchyRowInfo hierarchyChildRow in parentRow.ChildRows)
        {
            var childDataRowItem = hierarchyChildRow.DataBoundItem as DataRowView;
            childDataRowItem.Row["Chk"] = check;
       
        }
        // Checks/unchecks the corresponding child rows in the grid.
        foreach (DataRow childRow in _dtChildData.Select($"ParentId = '{dataRowItem["ParentId"]}'"))
        {
            childRow["Chk"] = check;
        }

        foreach (object rowObject in this.radGridView1.TableElement.Children[0].Children[1].Children)
        {
            var rowElement = rowObject as GridDetailViewRowElement;
            if (rowElement != null)
            {
                var detailsRowInfo = rowElement.RowInfo as GridViewDetailsRowInfo;
                if (detailsRowInfo.Owner == parentRow)
                {
                    var tableElement = (rowElement.Children[1] as GridDetailViewCellElement).Children[0] as GridTableElement;

                    tableElement.Update(GridUINotifyAction.DataChanged);
                         
                }
            }
        }
    }
}
I understand that it is not pretty, but currently there isn't a better solution to get the child TableElement. Now clicking on the main header checkbox will select all rows and their child rows.


João
Top achievements
Rank 1
Iron
commented on 06 Aug 2025, 10:34 AM | edited

Hello Dinko.

Thank you again for your help.
After examining your answer, I adapted the test project we have been sharing but there are still a few issues that I will try to explain.

Adding that child table element refresh mechanism in the HeaderCellToggleStateChanged fixes part of the problem but introduces a new one.

  • The child rows are now checked/unchecked visually as soon as I check/uncheck the header toggle.
  • However, if I now uncheck any of the parent rows, the HeaderCellToggleStateChanged is fired because since 1 of the parent rows is no longer checked, the header toggle will not be checked as well.
    This causes all parent rows and corresponding child rows to be unchecked because the HeaderCellToggleStateChanged makes all rows follow the state of the toggle check. Is there a way to know in this event, if it was fired automatically by the grid internal behavior or if it was the user that clicked the header toggle? This would fix the problem I believe.

    I tried to remove the handler for the HeaderCellToggleStateChangedevent in the CellValueChanged but the HeaderCellToggleStateChanged is fired first automatically, which does not help.

    To help show this problem, I recorded a small video that is attached as video1.mkv inside the zip.


Also, there is still an issue regarding the color update of all the rows.
I tried to also refresh the main table element using the logic you provided but it does not seem to help. Unless I am doing it wrong.

I used radGridView1.TableElement.Update(GridUINotifyAction.DataChanged). Similar to what you did for the child elements.
The behavior is shown in the video2.

Maybe I am missing something?
Attached, there is the V3 of the test project that contains all the previous logic implemented.

Thank you in advance.

Dinko | Tech Support Engineer
Telerik team
commented on 07 Aug 2025, 10:51 AM

I was able to observe both behaviors. You can extend the custom logic by checking the element under the mouse. This way you can raise a flag when the user clicks on the header checkbox or checkmark in the cell:

this.radGridView1.MouseClick += RadGridView1_MouseClick;

private void RadGridView1_MouseClick(object sender, MouseEventArgs e)
{
    var clickedElement = this.radGridView1.GridViewElement.ElementTree.GetElementAtPoint(e.Location) as RadCheckBoxElement;
    if (clickedElement != null)
    {
        if (clickedElement.Parent is GridCheckBoxHeaderCellElement)
        {
            headCellCheckBoxClicked = true;
        }
    }
}

In the above code, I have subscribed to the MouseClick event of the RadGridView. In the event handler, we can get the element under the click position and raise a flag. Then we can check that flag in the radGridView1_HeaderCellToggleStateChanged event handler:

private void radGridView1_HeaderCellToggleStateChanged(object sender, GridViewHeaderCellEventArgs e)
{
    if (!headCellCheckBoxClicked)
    {                
        return;
    }
. . . . . . . .  // other code
headCellCheckBoxClicked = false;
}

For the ExpressionFormattingObject, it does not update properly with the custom code. Instead, I would suggest removing it and manually style the cells in the CellFormatting event that will be triggered for each data cell. This event is triggered very often, thus allowing us to customize the cell in different scenarios:

this.radGridView1.CellFormatting += RadGridView1_CellFormatting;

private void RadGridView1_CellFormatting(object sender, CellFormattingEventArgs e)
{
    if (e.CellElement != null && e.Row is GridViewHierarchyRowInfo)
    {
        var checkColumn = (e.Row.DataBoundItem as DataRowView).Row.ItemArray[1];
        if ((bool)checkColumn)
        {
            e.CellElement.DrawFill = true;
            e.CellElement.NumberOfColors = 1;
            e.CellElement.BackColor = Color.PeachPuff;
        }
        else
        {
            e.CellElement.ResetValue(LightVisualElement.DrawFillProperty, ValueResetFlags.Local);
            e.CellElement.ResetValue(LightVisualElement.NumberOfColorsProperty, ValueResetFlags.Local);
            e.CellElement.ResetValue(LightVisualElement.BackColorProperty, ValueResetFlags.Local);
        }
    }
    else
    {
        e.CellElement.ResetValue(LightVisualElement.DrawFillProperty, ValueResetFlags.Local);
        e.CellElement.ResetValue(LightVisualElement.NumberOfColorsProperty, ValueResetFlags.Local);
        e.CellElement.ResetValue(LightVisualElement.BackColorProperty, ValueResetFlags.Local);
    }
}

 

 

 

 

 

Tags
GridView Styling
Asked by
João
Top achievements
Rank 1
Iron
Answers by
Dinko | Tech Support Engineer
Telerik team
João
Top achievements
Rank 1
Iron
Share this question
or