Editing in UWP DataGrid (SfDataGrid)
10 May 202124 minutes to read
SfDataGrid provides support for editing and it can be enabled or disabled by setting SfDataGrid.AllowEditing property.
<syncfusion:SfDataGrid x:Name="dataGrid"
AllowEditing="True"
ItemsSource="{Binding Orders}">
dataGrid.AllowEditing = true;
You can enable or disable editing for particular column by setting GridColumn.AllowEditing property.
<syncfusion:GridTextColumn AllowEditing="True"
HeaderText="Order ID"
MappingName="OrderID" />
dataGrid.Columns["OrderID"].AllowEditing = true;
NOTE
GridColumn.AllowEditing takes higher priority than SfDataGrid.AllowEditing.
NOTE
It is mandatory to set the NavigationMode to Cell to enable CurrentCell navigation and editing.
Entering into edit mode
You can enter into edit mode by pressing F2 key or clicking (touch also supported) the cell. You can allow users to edit the cell in single click (OnTap) or double click (OnDoubleTab) by setting by EditTrigger property.
<syncfusion:SfDataGrid x:Name="dataGrid"
AllowEditing="True"
EditTrigger="OnDoubleTap"
ItemsSource="{Binding Orders}" />
dataGrid.EditTrigger = EditTrigger.OnTap;
Cursor placement
When the cell enters into edit mode, cursor is placed based on EditorSelectionBehavior property.
SelectAll
– selects the text of edit element loaded inside cell.
MoveLast
– places the cursor at the last of edit element loaded inside cell.
<syncfusion:SfDataGrid x:Name="dataGrid"
AllowEditing="True"
EditorSelectionBehavior="SelectAll"
ItemsSource="{Binding Orders}">
dataGrid.EditorSelectionBehavior = EditorSelectionBehavior.SelectAll;
Retain editing on lost focus
The editing of current cell will be ended by default while the focus is moving from DataGrid to another control. You can set the LostFocusBehavior property to LostFocusBehavior.Default
if you want to retain the editing of the current cell even when focus is moved to another control.
<syncfusion:SfDataGrid x:Name="dataGrid"
AllowEditing="True"
LostFocusBehavior="Default"
ItemsSource="{Binding Orders}" />
dataGrid.LostFocusBehavior = LostFocusBehavior.Default;
Support for IEditableObject
SfDataGrid supports to commit and roll back the changes in row level when underlying data object implements IEditableObject interface.
The editing changes in a row will be committed only when user move to next row or pressing enter key in EndEdit
. Also when user press Esc key, then the changes made in a row will be reverted in CancelEdit
.
IEditableObject
has the following methods to capture editing,
BeginEdit – Gets called to begin edit on underlying data object when cell in a row get into edit mode.
CancelEdit – Gets called when user press the Esc key to discard the changes in a row since last BeginEdit
call.
EndEdit – Gets called when user move to the next row or press Enter key to commit changes in underlying data object since last BeginEdit
call.
In the below code snippet explains the simple implementation of IEditableObject
.
public class OrderInfo : INotifyPropertyChanged, IEditableObject
{
int orderID;
string customerId;
string country;
string customerName;
string shippingCity;
public int OrderID
{
get { return orderID; }
set { orderID = value; RaisePropertyChanged("OrderID"); }
}
public string CustomerName
{
get { return customerName; }
set { customerName = value; RaisePropertyChanged("CustomerName"); }
}
public string CustomerID
{
get { return customerId; }
set { customerId = value; RaisePropertyChanged("CustomerID"); }
}
public string Country
{
get { return country; }
set { country = value; RaisePropertyChanged("Country"); }
}
public string ShipCity
{
get { return shippingCity; }
set { shippingCity = value; RaisePropertyChanged("ShipCity"); }
}
public event PropertyChangedEventHandler PropertyChanged;
public void RaisePropertyChanged(string propName)
{
if (this.PropertyChanged != null)
this.PropertyChanged(this, new PropertyChangedEventArgs(propName));
}
private Dictionary<string, object> storedValues;
public void BeginEdit()
{
this.storedValues = this.BackUp();
}
public void CancelEdit()
{
if (this.storedValues == null)
return;
foreach (var item in this.storedValues)
{
var itemProperties = this.GetType().GetTypeInfo().DeclaredProperties;
var pDesc = itemProperties.FirstOrDefault(p => p.Name == item.Key);
if (pDesc != null)
pDesc.SetValue(this, item.Value);
}
}
public void EndEdit()
{
if (this.storedValues != null)
{
this.storedValues.Clear();
this.storedValues = null;
}
}
protected Dictionary<string, object> BackUp()
{
var dictionary = new Dictionary<string, object>();
var itemProperties = this.GetType().GetTypeInfo().DeclaredProperties;
foreach (var pDescriptor in itemProperties)
{
if (pDescriptor.CanWrite)
dictionary.Add(pDescriptor.Name, pDescriptor.GetValue(this));
}
return dictionary;
}
}
Events
SfDataGrid triggers the following events during editing.
CurrentCellBeginEdit Event
CurrentCellBeginEdit event occurs when the CurrentCell enter into edit mode. CurrentCellBeginEditEventArgs has following members which provides information for CurrentCellBeginEdit
event.
-
Cancel: When set to ‘true’, the event is canceled and the
CurrentCell
does not enter into the edit mode. -
RowColumnIndex: Gets the current row column index of the DataGrid.
-
Column: Gets the Grid Column of the SfDataGrid.
this.dataGrid.CurrentCellBeginEdit += DataGrid_CurrentCellBeginEdit;
private void DataGrid_CurrentCellBeginEdit(object sender, CurrentCellBeginEditEventArgs args)
{
}
CurrentCellEndEdit Event
CurrentCellEndEdit event occurs when the CurrentCell exits the edit mode. CurrentCellEndEditEventArgs has following members which provides information for CurrentCellEndEdit
event.
- RowColumnIndex: Gets the value for the current row column index.
this.dataGrid.CurrentCellEndEdit += DataGrid_CurrentCellEndEdit;
private void DataGrid_CurrentCellEndEdit(object sender, CurrentCellEndEditEventArgs args)
{
}
CurrentCellValueChanged Event
CurrentCellValueChanged event occurs whenever a value changes in GridColumn’s that supports editing.
CurrentCellValueChangedEventArgs has following members which provides information for CurrentCellValueChanged
event.
-
Column: Gets the Grid Column of the SfDataGrid.
-
RowColumnIndex: Gets the value of the current RowColumnIndex.
this.dataGrid.CurrentCellValueChanged += DataGrid_CurrentCellValueChanged;
private void DataGrid_CurrentCellValueChanged(object sender, CurrentCellValueChangedEventArgs args)
{
}
NOTE
GridComboBoxColumn and GridMultiColumnDropDownList, you have to use the CurrentCellDropDownSelectionChanged event.
CurrentCellDropDownSelectionChanged Event
CurrentCellDropDownSelectionChanged event occurs whenever the SelectedItem
of GridMultiColumnDropDownList
and GridComboBoxColumn
column changed.
CurrentCellDropDownSelectionChangedEventArgs has following members which provides information for CurrentCellDropDownSelectionChanged
event.
-
RowColumnIndex – Gets the RowColumnIndex of the corresponding item that were selected from the drop-down control.
-
SelectedIndex – Gets the index of the corresponding item that were selected from the drop-down control.
-
SelectedItem – Gets the data item that were selected from the drop-down control.
this.dataGrid.CurrentCellDropDownSelectionChanged += DataGrid_CurrentCellDropDownSelectionChanged;
private void DataGrid_CurrentCellDropDownSelectionChanged(object sender, CurrentCellDropDownSelectionChangedEventArgs args)
{
}
CellTapped Event
CellTapped event occurs when the user clicks or touches the Cell
in SfDataGrid with GridCellTappedEventArgs. CellTapped event does not occur for the non-selectable cells. The GridCellTappedEventArgs has following members which provides information for CellTapped
event.
-
Column - Gets the GridColumn of the tapped cell.
-
Record - Gets the data context of the tapped cell.
-
RowColumnIndex - Gets the RowColumnIndex of the tapped cell.
-
PointerDeviceType - Gets the device type that associated with the event.
-
OriginalSender - Gets the original reporting source that raised the event.
<Syncfusion:SfDataGrid x:Name="dataGrid"
CellTapped="datagrid_CellTapped"
ItemsSource="{Binding OrderInfoCollection }">
</Syncfusion:SfDataGrid>
this.dataGrid.CellTapped += Datagrid_CellTapped;
private void Datagrid_CellTapped(object sender, GridCellTappedEventArgs e)
{
//You can do your own logic here.
}
CellDoubleTapped Event
CellDoubleTapped event occurs when the user double clicks or double taps the GridCell
in SfDataGrid with GridCellDoubleTappedEventArgs. CellDoubleTapped event does not occur for non-selectable cells. GridCellDoubleTappedEventArgs has following members which provides information for CellDoubleTapped
event.
-
Column - Gets the GridColumn of the double tapped cell.
-
Record - Gets the data context of the double tapped cell.
-
RowColumnIndex - Gets the RowColumnIndex of the double tapped cell.
-
PointerDeviceType - Gets the device type that associated with the event.
-
OriginalSender - Gets the original reporting source that raised the event
<Syncfusion:SfDataGrid x:Name="dataGrid"
CellDoubleTapped="datagrid_CellDoubleTapped"
ItemsSource="{Binding OrderInfoCollection }">
</Syncfusion:SfDataGrid>
this.dataGrid.CellDoubleTapped += Datagrid_CellDoubleTapped;
private void Datagrid_CellDoubleTapped(object sender, GridCellDoubleTappedEventArgs e)
{
//you can do your own logic here.
}
Programmatically edit the cell
BeginEdit
SfDataGrid allows you to edit the cell programmatically by calling the BeginEdit method. Initially the CurrentCell need to set before calling the BeginEdit
method when the CurrentCell value is null.
//Add this namespace to access the RowColumnIndex structure type in SfDataGrid
using Syncfusion.UI.Xaml.ScrollAxis;
this.dataGrid.Loaded += DataGrid_Loaded;
private void DataGrid_Loaded(object sender, RoutedEventArgs e)
{
RowColumnIndex rowColumnIndex = new RowColumnIndex(3, 2);
this.dataGrid.MoveCurrentCell(rowColumnIndex);
this.dataGrid.SelectionController.CurrentCellManager.BeginEdit();
}
EndEdit
You can call the EndEdit method to programmatically end edit.
//Add this namespace to access the RowColumnIndex structure type in SfDataGrid
using Syncfusion.UI.Xaml.ScrollAxis;
this.dataGrid.Loaded += DataGrid_Loaded;
private void DataGrid_Loaded(object sender, RoutedEventArgs e)
{
RowColumnIndex rowColumnIndex = new RowColumnIndex(3, 2);
this.dataGrid.MoveCurrentCell(rowColumnIndex);
this.dataGrid.SelectionController.CurrentCellManager.EndEdit();
}
CancelEdit
You can use the CurrentCellBeginEdit event to cancel the editing operation for the corresponding cell.
//Add this namespace to access the RowColumnIndex structure type in SfDataGrid
using Syncfusion.UI.Xaml.ScrollAxis;
this.dataGrid.CurrentCellBeginEdit += DataGrid_CurrentCellBeginEdit;
private void DataGrid_CurrentCellBeginEdit(object sender, CurrentCellBeginEditEventArgs args)
{
var recordIndex = this.dataGrid.ResolveToRecordIndex(args.RowColumnIndex.RowIndex);
var columnIndex = this.dataGrid.ResolveToGridVisibleColumnIndex(args.RowColumnIndex.ColumnIndex);
var mappingName = this.dataGrid.Columns[columnIndex].MappingName;
var record = this.dataGrid.View.Records.GetItemAt(recordIndex);
var cellValue = this.dataGrid.View.GetPropertyAccessProvider().GetValue(record, mappingName);
if (args.RowColumnIndex == new RowColumnIndex(3, 2))
args.Cancel = true;
}
Mouse and Keyboard operations for UIElement inside Template
You can directly load edit element using GridTemplateColumn.CellTemplate
property. In this case, you can provide focus and control (keyboard and mouse) to the UIElement inside CellTemplate in the below ways,
Providing focus to the control inside the Template
You can focus to the particular UIElement loaded inside template when cell gets activated by setting FocusedManager.FocusedElement attached property.
<syncfusion:GridTemplateColumn HeaderText="Customer ID" MappingName="CustomerID">
<syncfusion:GridTemplateColumn.CellTemplate>
<DataTemplate>
<TextBlock FontStyle="Italic"
FontWeight="SemiBold"
Padding="2,0"
Text="{Binding CustomerID}" />
</DataTemplate>
</syncfusion:GridTemplateColumn.CellTemplate>
<syncfusion:GridTemplateColumn.EditTemplate>
<DataTemplate>
<TextBox FontStyle="Italic"
FontWeight="SemiBold"
Padding="2,0"
Text="{Binding CustomerID}"
syncfusion:FocusManagerHelper.FocusedElement="True" />
</DataTemplate>
</syncfusion:GridTemplateColumn.EditTemplate>
</syncfusion:GridTemplateColumn>
Providing keyboard control to UIElement inside CellTemplate
You can allow UIElement
loaded inside CellTemplate
to handle keyboard interaction by setting FocusManagerHelper.WantsKeyInput attached property to GridColumn
.
<syncfusion:GridTemplateColumn MappingName="CustomerID" syncfusion:FocusManagerHelper.WantsKeyInput="False">
<syncfusion:GridTemplateColumn.CellTemplate>
<DataTemplate>
<Grid>
<TextBox x:Name="text" Text="{Binding CustomerID}" />
</Grid>
</DataTemplate>
</syncfusion:GridTemplateColumn.CellTemplate>
</syncfusion:GridTemplateColumn>
NOTE
Enter and Tab keys are always handled by SfDataGrid only.
How to
Change the foreground of edited cells to keep track of changes
You can change the foreground color of edited cells through the CellStyleSelector
.
Please follow the below steps to highlight the edited cells.
1.Add new property EditedColumns
in data object to maintain edited column MappingName
.
2.Add the MappingName of the column to ` EditedColumns`, in CurrentCellValueChanged to keep track of edited columns in data object.
this.dataGrid.CurrentCellValueChanged += DataGrid_CurrentCellValueChanged;
private void DataGrid_CurrentCellValueChanged(object sender, CurrentCellValueChangedEventArgs args)
{
if (!(args.Record as OrderInfo).EditedColumns.Contains(args.Column.MappingName))
(args.Record as OrderInfo).EditedColumns.Add(args.Column.MappingName);
//updates the current row index
this.dataGrid.UpdateDataRow(args.RowColumnIndex.RowIndex);
}
3.Create a style of TargetType GridCell
and change the Foreground using CellStyleSelector based on ` EditedColumns` property in data object.
<Application.Resources>
<Style x:Key="cellStyle" TargetType="syncfusion:GridCell">
<Setter Property="Foreground" Value="DarkOrange" />
</Style>
</Application.Resources>
<Page.Resources>
<local:CellStyleSelector x:Key="cellStyleSelector" />
</Page.Resources>
<syncfusion:SfDataGrid x:Name="dataGrid"
AllowEditing="True"
CellStyleSelector="{StaticResource cellStyleSelector}"
ItemsSource="{Binding Orders}" />
public class CellStyleSelector:StyleSelector
{
protected override Style SelectStyleCore(object item, DependencyObject container)
{
var gridCell = container as GridCell;
if (gridCell.ColumnBase == null || gridCell.ColumnBase.GridColumn == null)
base.SelectStyle(item, container);
var record = item as OrderInfo;
if (record.EditedColumns.Contains(gridCell.ColumnBase.GridColumn.MappingName))
return App.Current.Resources["cellStyle"] as Style;
return base.SelectStyleCore(item, container);
}
}
Allow editing when pressing minus key
SfDataGrid does not allow the cell to get into the edit mode while pressing the - key or any special character. You can overcome this behavior by deriving a class from GridCellNumericRenderer
and override the ShouldGridTryToHandleKeyDown
method.
this.grid.CellRenderers.Remove("Numeric");
this.grid.CellRenderers.Add("Numeric", new GridCellNumericRendererExt());
public class GridCellNumericRendererExt : GridCellNumericRenderer
{
//ShouldGridTryToHandleKeyDown() is responsible for all key navigation associated with GridNumericColumn.
protected override bool ShouldGridTryToHandleKeyDown(Windows.UI.Xaml.Input.KeyRoutedEventArgs e)
{
//If the GridNumericColumn's cell is not already in Edit mode
if (!IsInEditing)
{
//Edit mode will be based on the ProcessPreviewTextInput method
ProcessPreviewTextInput(e);
if (!(CurrentCellRendererElement is SfNumericTextBox))
return true;
}
return base.ShouldGridTryToHandleKeyDown(e);
}
//ProcessPreviewTextInput() will be responsible for Edit mode behavior
private void ProcessPreviewTextInput(Windows.UI.Xaml.Input.KeyRoutedEventArgs e)
{
//Here you can customize the edit mode behavior whether it is based on letters or digits or any key
if ((!char.IsLetterOrDigit(e.Key.ToString(), 0) || !DataGrid.AllowEditing || DataGrid.NavigationMode != NavigationMode.Cell) || CheckControlKeyPressed() || (e.Key == VirtualKey.F2))
return;
//The Editing for current cell of GridNumericColumn is processed here.
if (DataGrid.SelectionController.CurrentCellManager.BeginEdit())
PreviewTextInput(e);
}
internal bool CheckControlKeyPressed()
{
return (Window.Current.CoreWindow.GetAsyncKeyState(VirtualKey.Control).HasFlag(CoreVirtualKeyStates.Down));
}
}