In our app we have a page with SfDataGrid on it. The grid displays a list of stores and is also used for editing those stores. Store class that is a model for each row implements IEditableObject interface and has BeginEdit and EndEdit methods, the main use of these methods is to create a back up of a row before it is edited so that later you can cancell all changes to all the rows by falling back to the backup. The issue we encountered is that when you only use checkbox columns to edit a row the row doesn't enter into edit mode and BeginEdit, EndEdit methods are not invoked even though the changes to the model are applied anyway. (This doesn't occure if for example you first click on a DataGridTextColumn and then change checkboxes of the same row because in this case clicking on the DataGridTextColumn enters into edit mode and triggers BeginEditing so everything is handled as intended). So basically the problem is that clicking on a DataGridCheckboxColumn doesn't enter into edit mode.
The one workaround we found for that is to replace DataGridCheckboxColumn with DataGridTemplateColumn and use a regular checkbox with TapGestureRecognizer on it like this:
The reason we are using TapGestureRecognizer instead of CheckedChanged event is because when the DataGrid is created all checkboxes are unchecked and then the programm starts "manually" checking the once that should be changed thus triggering CheckedChanged event before the grid is even displayed.
Once the TapGesture is raised we handle the event by first reverting the value in the model to the opposite of what it was set to by the checkbox to make a snapshot of what the row looked like before the editing occured using BeginEdit method and then revert it back to save the changes.
I recreated a simpler version of this functionality in a separate project that I have attached here as well. Here is a brief description of the project, and a couple of steps you need to take to see what the problem is.
The model class Store is in the Store.cs file. The class countains a coulpe of fields, two events BeginEditingStore and StoreEdited. It also has BeginEdit method that calles OnBegginEditing method that in turn raises BeginEditingStore event. And similar EndEdit that calls OnBeginEdititng method that in turn raises the StoreEdited event.
The DataGrid is located on the MainPage.xaml, in its code behind there is a list of three stores, in the constructor AddStores method is called. It adds all stores to the list of rows after subscribing OnBeginEditing and OnStoreEdited methods from the code behind to BeginEditingStore and StoreEdited events of the Store object accordingly.
The ItemsSource property of the DataGrid is bound to Rows property that contains the list that is populated by AddStores in the constructor.
To see exactly what the porblem is, in the code behind (MainPage.xaml.cs) put a breakpoint in OnBeginEditing and OnStoreEdited methods and then lauch the project for Windows.
Once the app is running you will see two columns with checkboxes. The first one named "Active" is created using DataGridCheckboxColumn when you click on it neither OnBeginEditing nor OnStoreEdited will be invoked. The second column with checkboxes named "Main" is created using the workaround described before with a DataGridTemplateColumn that contains a regular checkbox with TapGestureRecognizer, as you can see by clicking on checkboxes from that column both OnBegginEditing and OnStoreEdited are hit. Another important thing to notice is that if you click on any of the text columns "Name" in this case the OnBegginEditing and OnStoreEdited will be invoked as well so this issue is specific to DataGridCheckboxColumns.
To sum up the problem, the core of the issue is that the DataGridCheckboxColumn does not automatically enter the row into edit mode upon interaction, which is necessary for the BeginEdit and EndEdit methods to be called.
Hi Vladimir,
Upon analyzing the attached sample, it is clear that this is the expected behavior of the DataGridCheckBox column. The DataGridCheckBox column does not support the Editable property. You can utilize the workaround you shared. If this post is helpful, please consider Accepting it as the solution so that other members can locate it more quickly.
Regards,
Nirmalkumar.
Expected behavior is not what we hoped to hear, as the behavior is not consistent among different type of edit controls within the grid and therefore could not be intended as is, but only a result of bad design or lack of interest for a better design.
Hi Andrei,
In SfDataGrid, BeginEdit and EndEdit events are called only the columns having different controls in UI and edit element. In CheckBox column, we have loaded the CheckBox as a UI control, so editing methods and events is not trigged for CheckBoxColumn. You can notify the editing operations of CheckBoxColumn by using DataGrid.CellValueChanged event.
this.dataGrid.CellValueChanged += DataGrid_CellValueChanged;
private void DataGrid_CellValueChanged(object? sender, DataGridCellValueChangedEventArgs e) { // progress your work here. }
|
Regards,
Sethupathy D.
The problem with this approach as I indicated in my report is that clicking on a checkbox column and changing it is the same event. With any other type of column clicking the column and editing it are two different events, because of this we can invoke BeginEdit when a column is clicked and take a snapshot of the data in the row before it is edited.
With checkbox columns however once it was clicked the data changes immediately, this doesn't leave us any space to take a snapshot of the row before it was edited. This means that using CellValueChanged is not going to help, because it will be raised after the row had already been changed.
Hi Vladimir,
Based on the provided information, it is clear that you need to take a snapshot of the DataRow before it is edited. We can achieve this by using a custom CheckBox renderer. I have shared both the code snippet and sample for your reference.
Code Snippet:
|
public partial class MainPage : ContentPage { public MainPage() { InitializeComponent(); this.dataGrid.CellRenderers.Remove("CheckBox"); dataGrid.CellRenderers.Add("CheckBox", new DataGridCheckBoxCellRendererExt()); } }
public class DataGridCheckBoxCellRendererExt : DataGridCheckBoxCellRenderer { protected override void OnInitializeDisplayView(DataColumnBase dataColumn, StackLayout? view) { base.OnInitializeDisplayView(dataColumn, view);
if (view != null && view.Children[0] is CheckBox checkBox) { checkBox.PropertyChanging += DataGridCheckBoxCellRendererExt_PropertyChanging; checkBox.PropertyChanged += DataGridCheckBoxCellRendererExt_PropertyChanged; } }
private void DataGridCheckBoxCellRendererExt_PropertyChanged(object? sender, System.ComponentModel.PropertyChangedEventArgs e) { if (e.PropertyName == "IsChecked") { Debug.WriteLine((sender as CheckBox)!.IsChecked); } }
private void DataGridCheckBoxCellRendererExt_PropertyChanging(object sender, PropertyChangingEventArgs e) { if (e.PropertyName == "IsChecked") { // You can take your Snap here. Debug.WriteLine((sender as CheckBox)!.IsChecked); } } } |
Regards,
Sethupathy Devarajan.
Hi Sethupathy,
Thank you for your suggestion. Using a custom renderer solves part of the problem. Now focusing on a checkbox column and changing it are two different events which allows us to manually trigger BeginEdit and EndEdit at the correct moment. However, a couple of issues still remain.
First of all before the page is displayed what seems to happen is that data grid is created with all checkbox columns unchecked and then, based on the underlying data, the program starts "manually" checking boxes that should be checked thus triggering DataGridCheckBoxCellRendererExt_PropertyChanging and populating our backup data with multiple snapshot of rows that the user didn't even interact with.
The second issue is that with this approach the checkbox column still stays disconnected from the state of the row, meaning that we should manually track if the row is already in edit mode when a checkbox is clicked. If we don't track the state what can happen is let's say a user first clicks on a text column, this enters the row into edit mode and BeginEdit is triggered taking a snapshot of the data, if after that a checkbox column of the same row is clicked and it is not being tracked weather or not the row is already in edit mode BeginEdit will be called again and incorrect snapshot will be taken, making it impossible to fully cancel changes later. The way we avoid having this problem now is by having IsInEditMode property on every Store and manually setting it when BeginEdit and EndEdit are called. This property is checked every time a checkbox is clicked which allows us to skip calling BeinEdit and EndEdit when the row is already in edit mode.
Hi Vladimir,
|
Query |
Response |
|
|
Based on the provided information, it is clear that DataGridCheckBoxCellRendererExt_PropertyChanging is triggered, populating your backup data with multiple snapshots of rows that the user didn't even interact with. You can restrict this with the IsFocused property of the checkbox.
Secondly, you had stated that the checkbox column still stays disconnected from the state of the row, causing manual tracking. You can get rid of manual tracking with the IsEditingItem property. We had shared the code snippet for your reference.
Code Snippet:
|
Regards,
Sethupathy Devarajan.