Editing in WPF TreeGrid (SfTreeGrid)
12 Oct 202314 minutes to read
SfTreeGrid provides support for editing and it can be enabled or disabled by setting SfTreeGrid.AllowEditing property.
<syncfusion:SfTreeGrid x:Name="treeGrid"
AllowEditing="True"
AutoGenerateColumns="True"
ItemsSource="{Binding EmployeeDetails}" />
this.treeGrid.AllowEditing = true;
You can enable or disable editing for particular column by setting TreeGridColumn.AllowEditing property.
<syncfusion:TreeGridTextColumn AllowEditing="True"
HeaderText="First Name"
MappingName="FirstName" />
this.treeGrid.Columns["FirstName"].AllowEditing = true;
NOTE
TreeGridColumn.AllowEditing
takes higher priority thanSfTreeGrid.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 <kbd>F2</kbd> 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 SfTreeGrid.EditTrigger property.
<syncfusion:SfTreeGrid Name="treeGrid"
EditTrigger="OnTap"
AllowEditing="True"
AutoExpandMode="RootNodesExpanded"
ChildPropertyName="ReportsTo"
ItemsSource="{Binding EmployeeInfo}"
ParentPropertyName="ID"/>
this.treeGrid.EditTrigger = EditTrigger.OnTap;
Cursor placement
When the cell enters into edit mode, cursor is placed based on SfTreeGrid.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:SfTreeGrid Name="treeGrid"
AllowEditing="True"
EditorSelectionBehavior="SelectAll"
AutoExpandMode="RootNodesExpanded"
ChildPropertyName="ReportsTo"
EditTrigger="OnTap"
ParentPropertyName="ID"
ItemsSource="{Binding EmployeeInfo}" />
this.treeGrid.EditorSelectionBehavior = EditorSelectionBehavior.SelectAll;
Support for IEditableObject
SfTreeGrid 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 <kbd> Esc </kbd> 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 <kbd>Esc</kbd> 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 EmployeeInfo : IEditableObject, INotifyPropertyChanged
{
int _id;
/// <summary>
/// Gets or sets the ID.
/// </summary>
/// <value>The ID.</value>
public int ID
{
get
{
return _id;
}
set
{
_id = value;
RaisePropertyChanged("ID");
}
}
string _firstName;
/// <summary>
/// Gets or sets the first name.
/// </summary>
/// <value>The first name.</value>
public string FirstName
{
get { return _firstName; }
set
{
_firstName = value;
RaisePropertyChanged("FirstName");
}
}
string _lastName;
/// <summary>
/// Gets or sets the last name.
/// </summary>
/// <value>The last name.</value>
public string LastName
{
get { return _lastName; }
set
{
_lastName = value;
RaisePropertyChanged("LastName");
}
}
private string _title;
/// <summary>
/// Gets or sets the title.
/// </summary>
/// <value>The title.</value>
public string Title
{
get
{
return _title;
}
set
{
_title = value;
RaisePropertyChanged("Title");
}
}
double? _salary;
/// <summary>
/// Gets or sets the salary.
/// </summary>
/// <value>The salary.</value>
public double? Salary
{
get
{
return _salary;
}
set
{
_salary = value;
RaisePropertyChanged("Salary");
}
}
int _reportsTo;
/// <summary>
/// Gets or sets the reports to.
/// </summary>
/// <value>The reports to.</value>
public int ReportsTo
{
get
{
return _reportsTo;
}
set
{
_reportsTo = value;
RaisePropertyChanged("ReportsTo");
}
}
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;
}
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;
}
}
public event PropertyChangedEventHandler PropertyChanged;
public void RaisePropertyChanged(string propertyName)
{
if (PropertyChanged != null)
PropertyChanged(this, new PropertyChangedEventArgs(propertyName));
}
}
Events
SfTreeGrid triggers the following events during editing.
CurrentCellBeginEdit Event
CurrentCellBeginEdit event occurs when the CurrentCell enter into edit mode. TreeCurrentCellBeginEditEventArgs has following members which provides information for CurrentCellBeginEdit
event.
-
Cancel : When set to
true
, the event is canceled and theCurrentCell
does not enter into the edit mode. -
RowColumnIndex : Gets the current row column index of the TreeGrid.
-
Column : Gets the Tree Grid Column of the SfTreeGrid.
this.treeGrid.CurrentCellBeginEdit += TreeGrid_CurrentCellBeginEdit;
void TreeGrid_CurrentCellBeginEdit(object sender, TreeGridCurrentCellBeginEditEventArgs 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.treeGrid.CurrentCellEndEdit += TreeGrid_CurrentCellEndEdit;
void TreeGrid_CurrentCellEndEdit(object sender, CurrentCellEndEditEventArgs args)
{
}
CurrentCellValueChanged Event
CurrentCellValueChanged event occurs whenever a value changes in TreeGridColumn that supports editing.
TreeGridCurrentCellValueChangedEventArgs has following members which provides information for CurrentCellValueChanged
event.
- Column : Gets the Grid Column of the SfTreeGrid.
- RowColumnIndex : Gets the value of the current RowColumnIndex.
this.treeGrid.CurrentCellValueChanged += TreeGrid_CurrentCellValueChanged;
void TreeGrid_CurrentCellValueChanged(object sender, TreeGridCurrentCellValueChangedEventArgs args)
{
}
NOTE
For TreeGridComboBoxColumn, you have to use the ‘CurrentCellDropDownSelectionChanged’ event.
CurrentCellDropDownSelectionChanged Event
CurrentCellDropDownSelectionChanged event occurs whenever the SelectedItem
of TreeGridComboBoxColumn
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.treeGrid.CurrentCellDropDownSelectionChanged += TreeGrid_CurrentCellDropDownSelectionChanged;
void TreeGrid_CurrentCellDropDownSelectionChanged(object sender, CurrentCellDropDownSelectionChangedEventArgs args)
{
}
Programmatically edit the cell
BeginEdit
SfTreeGrid allows you to edit the cell programmatically by calling the BeginEdit method. Initially theCurrentCell need to set before calling the BeginEdit
method when the CurrentCell value is null.
this.treeGrid.Loaded += TreeGrid_Loaded;
void TreeGrid_Loaded(object sender, RoutedEventArgs e)
{
RowColumnIndex rowColumnIndex = new RowColumnIndex(2, 3);
treeGrid.SelectionController.MoveCurrentCell(rowColumnIndex);
treeGrid.SelectionController.CurrentCellManager.BeginEdit();
}
EndEdit
You can call the EndEdit method to programmatically end edit.
this.treeGrid.Loaded += TreeGrid_Loaded;
void TreeGrid_Loaded(object sender, RoutedEventArgs e)
{
RowColumnIndex rowColumnIndex = new RowColumnIndex(2, 3);
treeGrid.SelectionController.MoveCurrentCell(rowColumnIndex);
treeGrid.SelectionController.CurrentCellManager.EndEdit();
}
CancelEdit
You can use the CurrentCellBeginEdit event to cancel the editing operation for the corresponding cell.
this.treeGrid.CurrentCellBeginEdit += TreeGrid_CurrentCellBeginEdit;
void TreeGrid_CurrentCellBeginEdit(object sender, TreeGridCurrentCellBeginEditEventArgs args)
{
var mappingName = treeGrid.Columns[args.RowColumnIndex.ColumnIndex].MappingName;
var node = treeGrid.View.GetNodeAt(args.RowColumnIndex.RowIndex);
if (args.RowColumnIndex == new RowColumnIndex(2, 2))
args.Cancel = true;
}
ReadOnly
You can prevent users from modifying the contents of a treegrid cell by setting the SfTreeGrid.IsReadOnly property, but the user can able to perform copy and selection operation.
<syncfusion:SfTreeGrid Name="treeGrid"
AllowEditing="True"
IsReadOnly="True"
AutoGenerateColumns="True"
ItemsSource="{Binding EmployeeDetails}"/>
this.treeGrid.IsReadOnly = true;
You can enable or disable editing for particular column by setting TreeGridColumn.IsReadOnly property.
<syncfusion:TreeGridTextColumn HeaderText="Order ID"
MappingName="OrderID"
IsReadOnly="True"/>
this.treeGrid.Columns["OrderID"].IsReadOnly = true;
NOTE
We should set the AllowEditing property to achieve the IsReadOnly behavior.
TreeGridColumn.IsReadOnly takes higher priority than SfTreeGrid.IsReadOnly.
Mouse and Keyboard operations for UIElement inside Template
You can directly load edit element using TreeGridTemplateColumn.CellTemplate
property. In this case, you can provide focus and control 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:SfTreeGrid Name="treeGrid"
AutoGenerateColumns="False"
ColumnSizer="Star"
AllowEditing="True"
AutoExpandMode="RootNodesExpanded"
ChildPropertyName="Children"
ItemsSource="{Binding EmployeeDetails}">
<syncfusion:SfTreeGrid.Columns>
<syncfusion:TreeGridTemplateColumn HeaderText="First Name" MappingName="FirstName" >
<syncfusion:TreeGridTemplateColumn.CellTemplate>
<DataTemplate>
<TextBlock Syncfusion:FocusManagerHelper.FocusedElement="True"
FontStyle="Italic"
FontWeight="SemiBold"
Padding="2,0"
Text="{Binding FirstName}" />
</DataTemplate>
</syncfusion:TreeGridTemplateColumn.CellTemplate>
</syncfusion:TreeGridTemplateColumn>
</syncfusion:SfTreeGrid.Columns>
</syncfusion:SfTreeGrid>
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 TreeGridColumn
.
<syncfusion:SfTreeGrid Name="treeGrid"
AutoGenerateColumns="False"
AllowEditing="True"
AutoExpandMode="RootNodesExpanded"
ChildPropertyName="Children"
ItemsSource="{Binding EmployeeDetails}">
<syncfusion:SfTreeGrid.Columns>
<syncfusion:TreeGridTemplateColumn HeaderText="First Name" MappingName="FirstName"
syncfusion:FocusManagerHelper.WantsKeyInput= "True">
<syncfusion:TreeGridTemplateColumn.CellTemplate>
<DataTemplate>
<Grid>
<TextBox x:Name="text" Text="{Binding FirstName}"/>
</Grid>
</DataTemplate>
</syncfusion:TreeGridTemplateColumn.CellTemplate>
</syncfusion:TreeGridTemplateColumn>
</syncfusion:SfTreeGrid.Columns>
</syncfusion:SfTreeGrid>
NOTE
Enter and Tab keys are always handled by SfTreeGrid 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 TreeGridColumn
.
<syncfusion:SfTreeGrid Name="treeGrid"
AutoGenerateColumns="False"
AllowEditing="True"
AutoExpandMode="RootNodesExpanded"
ChildPropertyName="Children"
ItemsSource="{Binding EmployeeDetails}">
<syncfusion:SfTreeGrid.Columns>
<syncfusion:TreeGridTemplateColumn HeaderText="City" MappingName="City" >
<syncfusion:TreeGridTemplateColumn.CellTemplate>
<DataTemplate>
<ComboBox ItemsSource="{Binding CityCollection, Source={StaticResource viewModel}}"
syncfusion:VisualContainer.WantsMouseInput="True"/>
</DataTemplate>
</syncfusion:TreeGridTemplateColumn.CellTemplate>
</syncfusion:TreeGridTemplateColumn>
</syncfusion:SfTreeGrid.Columns>
</syncfusion:SfTreeGrid>
NOTE
You can refer to our WPF TreeGrid feature tour page for its groundbreaking feature representations. You can also explore our WPF TreeGrid example to know how to render and configure the treegrid.