RowSelectionBrush and AlternatingRowStyle seem to be summing instead of RowSelectionBrush determining color

On SfDataGrid, I generally use AlternatingRowStyle to make it easier to select long rows. It looks like the AlternatingRowStyle color and the RowSelectionBrush colors are being added to form the background color of the SFDG row. I'd prefer the RowSelectionBrush to determine the color totally if the row(s) are selected. 

I've tried to find another way to totally override the AlternatingRowStyle color when a row is selected - you can, for instance, put a Border in the DataTemplate of SFDG and set the border's background attribute to bind to a color in the DataSource records, and have the records track the SelectedItems list. That's sort of nasty, though. So is setting the RowSelectionBrush opacity to 100%. 

I haven't been able to find a way to fire a Trigger to flip the Border between transparent and the selection color because there doesn't seem to be any sort of "IsSelected" I can use to distinguish between the two types of rows. 

Is there a crafty way of doing this that isn't so non-MVVM and hack-y?


16 Replies 1 reply marked as answer

SB Sweatha Bharathi Syncfusion Team June 9, 2025 02:09 PM UTC

Hi Thomas Brand,
Query
Comments
On SfDataGrid, I generally use AlternatingRowStyle to make it easier to select long rows. It looks like the AlternatingRowStyle color and the RowSelectionBrush colors are being added to form the background color of the SFDG row. I'd prefer the RowSelectionBrush to determine the color totally if the row(s) are selected. 
I've tried to find another way to totally override the AlternatingRowStyle color when a row is selected - you can, for instance, put a Border in the DataTemplate of SFDG and set the border's background attribute to bind to a color in the DataSource records, and have the records track the SelectedItems list. That's sort of nasty, though. So is setting the RowSelectionBrush opacity to 100%. 
We have reviewed your query and attempted to replicate the scenario based on the details you provided. However, we were unable to reproduce the reported issue. When applying both the RowSelectionBrush and the AlternatingRowStyle, the selection behavior works as expected—the two styles do not conflict, and the colors are applied correctly during selection.
We have attached the sample for your reference. Kindly review it. To assist you further, we request the following additional details about your scenario:
Customizations: Could you please clarify how you have applied the AlternatingRowStyle? Specifically, what colors have you assigned to the alternating row style and the RowSelectionBrush?
Modified Sample: Kindly modify the attached sample to reproduce the issue on your end. If possible, please include a video or image reference to illustrate the behavior. 
Providing these details will help us better understand the issue and offer a more accurate and effective solution.
I haven't been able to find a way to fire a Trigger to flip the Border between transparent and the selection color because there doesn't seem to be any sort of "IsSelected" I can use to distinguish between the two types of rows. 
Is there a crafty way of doing this that isn't so non-MVVM and hack-y?
You can distinguish between selected and non-selected rows and manually applying background colors to the selected rows using the SelectionChanged event.
Within this event, you can set the IsSelected property to true, which will trigger the converter. Based on the selection state, the appropriate background color can then be applied to the selected rows.
Code snippet to manually apply the background for selected row:
 public class RowBackgroundConverter : IValueConverter
 {
     public object Convert(object value, Type targetType, object parameter, CultureInfo culture)
     {
         if (value is bool isSelected && isSelected)
             return Brushes.LightGreen;
         else
             return DependencyProperty.UnsetValue;
     }
     public object ConvertBack(object value, Type targetType, object parameter, CultureInfo culture)
     {
         throw new NotImplementedException();
     }
 }     

 private void dataGrid_SelectionChanged(object sender, GridSelectionChangedEventArgs e)
 {
       foreach (var item in e.RemovedItems)
       {
              ((item as GridRowInfo).RowData as OrderInfo).IsSelected = false;
        }
        foreach (var item in e.AddedItems)
        {
              ((item as GridRowInfo).RowData as OrderInfo).IsSelected = true;
         }        
  }
 <syncfusion:SfDataGrid x:Name="dataGrid"     
                                           SelectionChanged="dataGrid_SelectionChanged">
     <syncfusion:SfDataGrid.RowStyle>
         <Style TargetType="syncfusion:VirtualizingCellsControl">
             <Setter Property="Background" Value="{Binding IsSelected, Converter={StaticResource rowBackgroundConverter}}" />
         </Style>
     </syncfusion:SfDataGrid.RowStyle>
 </syncfusion:SfDataGrid>

If this post is helpful, please consider Accepting it as the solution so that other members can locate it more quickly.
Regards,
Sweatha B
 

Attachment: sample_cad69807.zip


TB Thomas Brand June 9, 2025 07:49 PM UTC

Hi Sweatha: 

Thanks for getting back to me. I'll look at the .zip today and see how yours works. My program uses a relative LOT of SfDataGrids and they all do the AlternatingRowStyle + RowSelectionBrush color mixing thing. I have to say I was surprised that occurred, since it seemed like it would take time and really not net you anything to calculate a mix color. 

The second half of your reply is what I was referring to as "having the records track the SelectedItems list" in my first question. It WORKS, but it's sort of hacky, and I have to move my EF Core records to an intermediate record with a selection flag, or add a no-mapping selection flag property to the db set. I'd rather not do that. 

I spent a while trying to pass in SelectedItems from the SfDataGrid  as a converter parameter or by a multi-binding to a converter like the one you suggest using RelativeSource TemplatedParent to fish around in the SFDG's properties but never got it to work. 

Now, are you using full saturation on your example's RowSelectionBrush? THAT works, but it results in really garish colors. 

At any rate, I'll go look at the .zip today, and if I can't get anywhere I'll try to make you a demo program that demonstrates the mixing thing.



SB Sweatha Bharathi Syncfusion Team June 10, 2025 06:44 AM UTC

Hi Thomas Brand,


Thanks for the update, we will wait until we hear from you.


Regards,
Sweatha B


TB Thomas Brand June 10, 2025 07:32 PM UTC

Ok. You're doing things a bit differently than I have been doing in places. 

I generally use a style in the xaml for the alternating row, following other SF examples, instead of sticking it in the code-behind as a cs method. No problem, that also causes the same issue I'm seeing.

You also inserted a field into your data to track SelectedItems. I got it to work that way too when I was trying different fixes, although I used it to select a Border background. But I'm using EF Core DBSet records in most places to produce read-only SfDataGrid columns which then select an editing screen. I don't want to add a non-mapped field just for that or have to copy the dbset records out to a display record set that's exactly the same, but has "IsSelected" in it. I mean, that works, but it's hacky. 

I modified your sample to permit extended selection, simplified the alternaterowselector and disabled the converter, also modified the colors a bit so it's really plain. 

Run the program, and select a range of records. The selected items' rowselectionbrush doesn't override the alternatingrowstyle color, it mixes with it. I would have expected rowselectionbrush to take precedence.

I've tried several ways to pass a copy of SelectedItems in to a converter routine but so far I either can't get it or changing SelectedItems doesn't trigger a redraw. 


Attachment: modified_sample_51e8b11e.zip


SB Sweatha Bharathi Syncfusion Team June 11, 2025 02:28 PM UTC

Hi Thomas Brand ,
We have reviewed your query and understand that you want the RowSelectionBrush color to take precedence over the row style background color. By default, this works as expected when using solid colors for the RowSelectionBrush.
Upon further analysis of the sample you provided, we noticed that the RowSelectionBrush is defined using an RGB color with opacity. When opacity is applied to a RowSelectionBrush, it allows the background (e.g., the row’s background color) to show through. This is why the reported problem occurs.
To avoid this, in the AlternateRowStyleSelector, you can check if the data row is selected by using the SelectionBorderVisibility property. If it is selected, set the background to transparent.
Additionally, at runtime, to update the row background color, you can use the SelectionChanged event. Within the event, you can call the UpdateDataRow method with the corresponding row index for the added and removed data rows.
Code snippet to precedence the RowSelectionBrush color over the Row style:

 public class AlternateRowStyleSelector : StyleSelector
 {
     public override Style SelectStyle(object item, DependencyObject container)
     {
         // Applying alternating background for rows.

         var row = (item as DataRowBase).RowData;
         var data = row as OrderInfo;

         if ((container as VirtualizingCellsControl).SelectionBorderVisiblity == Visibility.Visible)
         {
             // Reset the Background color to Transparent
             return App.Current.Resources["rowStyle1"] as Style;
         }
         return App.Current.Resources["rowStyle2"] as Style;
     }
 }

 private void dataGrid_SelectionChanged(object sender, GridSelectionChangedEventArgs e)
 {
     // Update the style at run time when the data row is in selection and unselection state.
     foreach (var item in e.RemovedItems)
     {
         dataGrid.UpdateDataRow((item as GridRowInfo).RowIndex);
     }
     foreach (var item in e.AddedItems)
     {
         dataGrid.UpdateDataRow((item as GridRowInfo).RowIndex);
     }        
 }


Find the modified demo in the attachment and let us know if you have any concerns on this.

Regards,
Sweatha B

If this post is helpful, please consider Accepting it as the solution so that other members can locate it more quickly.

Attachment: sample_afb0d3a7.zip

Marked as answer

TB Thomas Brand June 11, 2025 05:58 PM UTC

Sweatha:

That works just fine. Wasn't aware of either SelectionBorderVisibility or UpdateDataRow, but those work just fine.


Thanks,

Thomas



SB Sweatha Bharathi Syncfusion Team June 12, 2025 05:53 AM UTC

Hi Thomas Brand ,

Glad that your issue is resolved!!. Please get back to us if you need any further assistance, we will be happy to help you.

Regards,
Sweatha B


TB Thomas Brand June 12, 2025 08:14 PM UTC

Sweatha:

As a sort of follow-on to the original question, if a program fired SelectedItems.Clear() for dataGrid, would you expect the SelectionChanged event to return the underlying data record types for e instead of GridSelectionChangedEventArgs? 


Thomas




SB Sweatha Bharathi Syncfusion Team June 13, 2025 02:13 PM UTC

Hi Thomas Brand ,

When `SelectedItems.Clear()` is called on a DataGrid, the SelectionChanged event will still return `GridSelectionChangedEventArgs` as the event argument type `e`. The event argument type is determined by the event signature and cannot change dynamically based on how the event is triggered.
However, you can access the underlying data record types through the properties of the `GridSelectionChangedEventArgs`:
  • e.RemovedItems – Contains the underlying data records that were removed from the selection (in this case, all previously selected items when Clear() is called)
  • e.AddedItems - Contains the underlying data records that were added to the selection (this would be empty when Clear() is called)
These collections contain the actual data objects bound to the grid rows, not GridSelectionChangedEventArgs objects. So, while the event argument `e` itself is always of type `GridSelectionChangedEventArgs`, the items within `e.RemovedItems` and `e.AddedItems` are your underlying data record types.

Regards,
Sweatha B

If this post is helpful, please consider Accepting it as the solution so that other members can locate it more quickly.


TB Thomas Brand June 13, 2025 06:26 PM UTC


Sweatha:

I misstated what was happening since I did it from memory. Let's try again - if I select a different record using the mouse, e.AddedItems and e.RemovedItems return a GridRowInfo object. 

If I fire SelectedItems.Clear(), I get underlying data records instead of GridRowInfo objects. This means the code you sent: 

*************************************************************

private void dataGrid_SelectionChanged(object sender, GridSelectionChangedEventArgs e)

{

    // Update the style at run time when the data row is in selection and unselection state.

    foreach (var item in e.RemovedItems)

    {

        dataGrid.UpdateDataRow((item as GridRowInfo).RowIndex);

    }

    foreach (var item in e.AddedItems)

    {

        dataGrid.UpdateDataRow((item as GridRowInfo).RowIndex);

    }

}

***************************************************************

will throw an exception when (item as GridRowInfo) returns a null, due to it being an underlying data record and not a GridRowInfo object. Returning different object types seemed unusual there, so I wanted to see if that was an intentional choice before proceeding to deal with it in some other way. If this can occur intentionally, I'll probably recode it to test each entry in the list to be a GridRowInfo or a data record, if it's a data record I can use IndexOf() to get a RowIndex. 

Thomas



MS Malini Selvarasu Syncfusion Team June 16, 2025 02:07 PM UTC

Hi Thomas Brand ,

Thank you for sharing the detailed information regarding the issue you are encountering. We are currently validating the issue and require some additional time for analysis. We will provide further updates by June 18, 2025.
We appreciate your patience and understanding in the meantime.
Regards,
Malini Selvarasu


MS Malini Selvarasu Syncfusion Team June 17, 2025 01:43 PM UTC

Hi Thomas Brand ,
 
We have checked and confirmed the reported issue "Incorrect Type of RemovedItems in SelectionChangedEventArgs when clearing selection programmatically" as a defect. We have logged a bug, and it will be fixed in our NuGet release scheduled on July 08, 2025.
 
You can track the status of this report through the following feedback link,
 
 
Note: The provided feedback link is private, and you need to log in to view this feedback.
 
We will let you know once it is released. We appreciate your patience until then.
 
Disclaimer: Inclusion of this solution in the weekly release may change due to other factors including but not limited to QA checks and works reprioritization.
 
Regards,
Malini Selvarasu

 



TB Thomas Brand June 17, 2025 03:50 PM UTC

Lovely! In the meantime I will use .IndexOf() to generate a RowIndex, but it would be a LOT more efficient if I didn't have to do a linear search. 


Thanks,

Thomas



SB Sweatha Bharathi Syncfusion Team June 18, 2025 06:38 AM UTC

Hi Thomas Brand ,
We will include the fix in the previously mentioned timeline. In the meantime, you can retrieve the row index using the IndexOf method as a temporary workaround.
We will inform you once it is released. We appreciate your patience until then.

Regards,
Sweatha B


SB Sweatha Bharathi Syncfusion Team July 8, 2025 02:08 PM UTC

Thomas Brand ,
The NuGet has been postponed to July 09, 2025. We will share the fix details once the release is officially published.
We appreciate your patience and understanding in the meantime.


RM Rabina Murugan Syncfusion Team July 9, 2025 01:05 PM UTC

Thomas Brand,

 

We would like to let you know that Essential Studio Weekly NuGet packages v30.1.39 has been published in nuget.org with the fix for the issue "Incorrect Type of RemovedItems in SelectionChangedEventArgs when clearing selection programmatically". Please let us know if you have any concerns in this.

 

Root Cause Details:

 

While clearing the selection programmatically, we mistakenly added GridRowInfo.RowData instead of GridRowInfo to the GridSelectionChangedEventArgs.RemovedItems collection. This incorrect assignment caused the issue.


Loader.
Up arrow icon