Editing

SfDataGrid provides support for editing and it can be enabled or disabled by setting SfDataGrid.AllowEditing property.

<syncfusion:SfDataGrid x:Name="dataGrid"
                       AllowEditing="True"
                       AutoGenerateColumns="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" 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="OnTap"
                       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;

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 Employee : NotificationObject, IEditableObject
{
    private string _Name;
    private int _ContactID;
    private string _Title;
    private DateTime _BirthDate;
    private string _Gender;
    private double _SickLeaveHours;
    private double _Salary;
    
    protected Dictionary<string, object> BackUp()
    {
        var dict = new Dictionary<string, object>();
        var itemProperties = this.GetType().GetTypeInfo().DeclaredProperties;
  
        foreach (var pDescriptor in itemProperties)
        {
  
            if (pDescriptor.CanWrite)
                dict.Add(pDescriptor.Name, pDescriptor.GetValue(this));
        }
        return dict;
    }

    public string Name
    {
        get { return this._Name; }
        set
        {
            this._Name = value;
            this.RaisePropertyChanged("Name");
        }

    }

    public string Title
    {
        get { return this._Title; }
        set
        {
            this._Title = value;
            this.RaisePropertyChanged("Title");
        }
    }

    public int ContactID
    {
        get { return this._ContactID; }
        set
        {
            this._ContactID = value;
            this.RaisePropertyChanged("ContactID");
        }
    }

    public DateTime BirthDate
    {
        get { return this._BirthDate; }
        set
        {
            this._BirthDate = value;
            this.RaisePropertyChanged("BirthDate");
        }
    }

    public string Gender
    {
        get { return this._Gender; }
        set
        {
            this._Gender = value;
            this.RaisePropertyChanged("Gender");
        }
    }

    public double SickLeaveHours
    {
        get { return this._SickLeaveHours; }
        set
        {
            this._SickLeaveHours = value;
            this.RaisePropertyChanged("SickLeaveHours");
        }
    }

    public double Salary
    {
        get { return this._Salary; }
        set
        {
            this._Salary = value;
            this.RaisePropertyChanged("Salary");
        }
    }

    private int _EmployeeID;

    public int EmployeeID
    {
        get { return this._EmployeeID; }
        set
        {
            this._EmployeeID = value;
            this.RaisePropertyChanged("EmployeeID");
        }
    }

    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;
        }
        Debug.WriteLine("End Edit Called");
    }
}

public class NotificationObject : INotifyPropertyChanged
{
    public void RaisePropertyChanged(string propName)
    {
 
        if (this.PropertyChanged != null)
        this.PropertyChanged(this, new PropertyChangedEventArgs(propName));
    }

    public event PropertyChangedEventHandler PropertyChanged;

}

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;

void dataGrid_CurrentCellBeginEdit(object sender, Syncfusion.UI.Xaml.Grid.CurrentCellBeginEditEventArgs args)
{
}

CurrentCellEndEdit Event

CurrentCellEndEdit event occurs when the CurrentCell exits the edit mode. CurrentCellEndEditEventArgs has following members which provides information for CurrentCellEndEdit event.

this.dataGrid.CurrentCellEndEdit += dataGrid_CurrentCellEndEdit;

void dataGrid_CurrentCellEndEdit(object sender, Syncfusion.UI.Xaml.Grid.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;

void dataGrid_CurrentCellValueChanged(object sender, Syncfusion.UI.Xaml.Grid.CurrentCellValueChangedEventArgs args)
{
}

NOTE

GridComboBoxColumn and GridMultiColumnDropList, 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;

void dataGrid_CurrentCellDropDownSelectionChanged(object sender, CurrentCellDropDownSelectionChangedEventArgs args)
{
}

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;

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;        
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;

void dataGrid_CurrentCellBeginEdit(object sender, Syncfusion.UI.Xaml.Grid.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;
}

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.
  • ChangedButton-Get the MouseButton 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.
  • ChangedButton - Gets the MouseButton 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.
}

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 syncfusion:FocusManagerHelper.FocusedElement="True"
                       FontStyle="Italic"
                       FontWeight="SemiBold"
                       Padding="2,0"
                       Text="{Binding CustomerID}" />
        </DataTemplate>
    </syncfusion:GridTemplateColumn.CellTemplate>
</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="ProductId"  
                               syncfusion:FocusManagerHelper.WantsKeyInput= "True">
    <syncfusion:GridTemplateColumn.CellTemplate>
        <DataTemplate>
            <Grid>
                <TextBox x:Name="text" Text="{Binding ProductId}"/>
            </Grid>
        </DataTemplate>
    </syncfusion:GridTemplateColumn.CellTemplate>
</syncfusion:GridTemplateColumn>

NOTE

Enter and Tab keys are always handled by SfDataGrid only.

Providing mouse control to UIElement inside template

You can allow UIElement loaded inside template to handle mouse interaction in required cases by setting VisualContainer.WantsMouseInput attached property to GridColumn.

<syncfusion:SfDataGrid x:Name="dataGrid"
                       AutoGenerateColumns="False" 
                       ItemsSource="{Binding Orders}">
    <syncfusion:SfDataGrid.Columns>
        <syncfusion:GridTemplateColumn MappingName="ProductName">
            <syncfusion:GridTemplateColumn.CellTemplate>
                <DataTemplate>
                    <ComboBox ItemsSource="{Binding ComboItems, Source={StaticResource viewModel}}" 
                              syncfusion:VisualContainer.WantsMouseInput="True" />
                </DataTemplate>
            </syncfusion:GridTemplateColumn.CellTemplate>
        </syncfusion:GridTemplateColumn>
    </syncfusion:SfDataGrid.Columns>
</syncfusion:SfDataGrid>

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);
}
  1. 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>

<Window.Resources>
    <local:CellStyleSelector x:Key="cellStyleSelector" />
</Window.Resources>

<syncfusion:SfDataGrid x:Name="dataGrid"
                       CellStyleSelector="{StaticResource cellStyleSelector}"
                       AllowEditing="True"
                       ItemsSource="{Binding Path=OrdersDetails}"
                       ShowRowHeader="True">
</syncfusion:SfDataGrid>
public class CellStyleSelector : StyleSelector
{

    public override Style SelectStyle(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.SelectStyle(item, container);
    }
}

Allow editing when pressing minus key

SfDataGrid does not allow the cell to get into the edit mode while pressing the Minuskey or any special character. You can overcome this behavior by customizing the SfDataGrid class, and overriding its OnTextInput () method.

public class SfDataGridExt : SfDataGrid
{
    public SfDataGridExt()
    : base()
    {
    }
    
    protected override void OnTextInput(TextCompositionEventArgs e)
    {

        if (!SelectionController.CurrentCellManager.HasCurrentCell)
        {
            base.OnTextInput(e);
            return;
        }

        //Get the Current Row and Column index from the CurrentCellManager
        var rowColumnIndex = SelectionController.CurrentCellManager.CurrentRowColumnIndex;
        RowGenerator rowGenerator = this.RowGenerator;

        //Get the row from the Row index
        var dataRow = rowGenerator.Items.FirstOrDefault(item => item.RowIndex == rowColumnIndex.RowIndex);

        //Check whether the dataRow is null or not and the type as DataRow

        if (dataRow != null && dataRow is DataRow)
        {

            //Get the column from the VisibleColumn collection based on the column index
            var dataColumn = dataRow.VisibleColumns.FirstOrDefault(column => column.ColumnIndex == rowColumnIndex.ColumnIndex);

            //Convert the input text to char type 
            char text;
            char.TryParse(e.Text, out text);

            //Skip if the column is GridTemplateColumn and the column is not already in editing 

            //Allow Editing only pressed letters digits and Minus sign key 

            if (dataColumn != null && !(dataColumn.GridColumn is GridTemplateColumn) && !dataColumn.IsEditing && SelectionController.CurrentCellManager.BeginEdit() && (e.Text.Equals("-") || char.IsLetterOrDigit(text)))
                dataColumn.Renderer.PreviewTextInput(e);
        }
        base.OnTextInput(e);
    }
}