This topic provides a comprehensive overview of data editing in UXGridView, discusses about data validation in depth, and explains how to handle the data creation, update and delete using MVVM pattern. For information about UXGridView and its features in general, see UXGridView Overview.
Enable Data Editing
Data editing in UXGridView involves three fundamental processes, Create, Update and Delete (CUD). You can enable each editing feature by setting the property CanUserAddRows, CanUserEditRows and CanUserDeleteRows respectively.
Example Title | ![]() |
---|---|
<Intersoft:UXGridView CanUserAddRows="True" CanUserDeleteRows="True" CanUserEditRows="True"/> |
A NewRow element will appear in the top of the UXGridView user interface when you set the CanUserAddRows property to true, such as shown in the following illustration.

In addition, you can also delete and edit the selected item by setting both the CanUserEditRows and CanUserDeleteRows properties to true.
Mouse and Keyboard Gesture
You can edit a row through a number of input devices and gestures such as using mouse or keyboard. The following list describes the configuration that you can customize to edit a record using mouse/keyboard.
Using Mouse Gesture
The mouse gesture is determined by the EditMouseGesture property. The following options are available to customize the edit mouse gesture.
- SingleClick
Directly begin edit on the selected cell. - SecondClick
Begin edit on the selected cell after the row is selected. - DoubleClick
Directly begin edit on the selected cell when double click fires.
Using Keyboard Gesture
The keyboard gesture is determined by several properties such as EditKeyGesture, EnterKeyAction and EditEnterKeyAction. Each property has specific action that you can customize. The following list details the action of each property.
- F2
Directly begin edit on the currently selected cell using F2 key. - AnyKeystroke
Directly begin edit on the currently selected cell from any key stroke.
- EnterEdit
Directly begin edit on the selected cell using Enter key. - MoveToNextRow
Move to next row (does not edit the cell).
- CommitAndMovetoNextRow
Commit the changes made in the edited row and move to the next row. - ExitEdit
Exit the editing of the selected cell. - MoveToNextEditableCell
Move to next editable cell. (Will move to next row’s cell when it reach the end of the row)
Beside these options, you can also use several common keystrokes during editing such as listed below.
- Tab / Shift + Tab
Move to the next/previous editable cell. - Shift + Enter
Commit the changes made in the edited row and stay in the current selection. - Delete
Delete the selected rows. - Escape
Cancel the current changes. If the editing mode is currently in the cell, it will cancel the changes made only to the particular cell. If the editing mode is in row level, pressing Escape key will cancel the changes made in the entire row.
Handling the CUD operation
To handle the CUD operation, UXGridView provides several command-related properties that you can bind to your ViewModel to execute a method depending on the actions. These command-related properties are listed as follows:
- PrepareNewRowCommand
Called when you begin edit at the NewRow element. Used to initialized the NewItem. - ValidateRowCommand
Validate the row before the row is committed. - InsertRowCommand
Called when a new row is committed. You can directly save the changes and/or refresh the UXGridView if necessary. - UpdateCellCommand
Called when the cell is committed. - UpdateRowCommand
Called when an existing row is committed. You can directly save the changes and/or refresh the UXGridView if necessary. - DeleteRowCommand
Called when a row is deleted. You can directly save the changes and / or refresh the UXGridView if necessary. - RejectRowCommand
Called when the changes in the row is cancelled. This command is used to reject the changes in the data entity if required (such as in DevForce). - SaveChangesCommand
Called when the save changes command is executed. You handle this command to save all the pending changes made in the UXGridView. - RejectChangesCommand
Called when the reject changes command is executed. You handle this command to reject all the pending changes made in the UXGridView.
You need to bind these command properties to your ViewModel and handle each of the action. Beside these command, UXGridView also provide HasChanges that you can use to determine whether there are existing changes in the data.
The following code example shows how to define and bind the editing commands from ViewModel to UXGridView.
View Model | ![]() |
---|---|
using System.Collections; using Intersoft.Client.Framework; using Intersoft.Client.Framework.Input; using Intersoft.Client.UI.Data; using UXGridView.Samples.ModelServices; namespace UXGridView.Samples.ViewModels { public class ServerEditingViewModel : ViewModelBase { public ServerEditingViewModel() : base() { this.Manager = new NorthwindDomainContext(); this.QueryDescriptor = new QueryDescriptor(); this.ExportCommand = new DelegateCommand(ExecuteExportCommand); this.DeleteRowCommand = new DelegateCommand(ExecuteDeleteRow); this.InsertRowCommand = new DelegateCommand(ExecuteInsertRow); this.PrepareNewRowCommand = new DelegateCommand(ExecutePrepareNewRow); this.UpdateCellCommand = new DelegateCommand(ExecuteUpdateCell); this.UpdateRowCommand = new DelegateCommand(ExecuteUpdateRow); this.RejectRowCommand = new DelegateCommand(ExecuteRejectRow); this.RejectChangesCommand = new DelegateCommand(ExecuteRejectChanges); this.SaveChangesCommand = new DelegateCommand(ExecuteSaveChanges); this.ValidateRowCommand = new DelegateCommand(ExecuteValidateRow); } #region Fields private bool _hasChanges; private bool _isBusy; private bool _isRefreshed; private object _newProduct; private IEnumerable _products; private QueryDescriptor _queryDescriptor; #endregion #region Properties private NorthwindDomainContext Manager { get; set; } public IEnumerable Products { get { return this._products; } set { if (this._products != value) { this._products = value; this.OnPropertyChanged("Products"); } } } public QueryDescriptor QueryDescriptor { get { return this._queryDescriptor; } set { if (this._queryDescriptor != value) { if (this._queryDescriptor != null) this._queryDescriptor.QueryChanged -= new System.EventHandler(OnQueryChanged); this._queryDescriptor = value; this._queryDescriptor.QueryChanged += new System.EventHandler(OnQueryChanged); this.OnPropertyChanged("QueryDescriptor"); } } } #endregion #region Selection and Editing Properties public object NewProduct { get { return this._newProduct; } set { if (this._newProduct != value) { this._newProduct = value; this.OnPropertyChanged("NewProduct"); } } } public bool IsBusy { get { return this._isBusy; } set { if (this._isBusy != value) { this._isBusy = value; this.OnPropertyChanged("IsBusy"); } } } public bool IsRefreshed { get { return this._isRefreshed; } set { if (this._isRefreshed != value) { this._isRefreshed = value; this.OnPropertyChanged("IsRefreshed"); } } } public bool HasChanges { get { return _hasChanges; } set { if (_hasChanges != value) { _hasChanges = value; OnPropertyChanged("HasChanges"); } } } #endregion #region Commands public DelegateCommand DeleteRowCommand { get; set; } public DelegateCommand InsertRowCommand { get; set; } public DelegateCommand PrepareNewRowCommand { get; set; } public DelegateCommand UpdateCellCommand { get; set; } public DelegateCommand UpdateRowCommand { get; set; } public DelegateCommand RejectRowCommand { get; set; } public DelegateCommand RejectChangesCommand { get; set; } public DelegateCommand SaveChangesCommand { get; set; } public DelegateCommand ValidateRowCommand { get; set; } #endregion #region Methods public void SaveChanges() { } public void ExecuteDeleteRow(object parameter) { } public void ExecuteInsertRow(object parameter) { } public void ExecutePrepareNewRow(object parameter) { } public void ExecuteUpdateCell(object parameter) { } public void ExecuteValidateRow(object parameter) { } public void ExecuteUpdateRow(object parameter) { } public void ExecuteRejectRow(object parameter) { } public void ExecuteRejectChanges(object parameter) { } public void ExecuteSaveChanges(object parameter) { } public virtual void LoadProducts() { if (Intersoft.Client.Framework.ISControl.IsInDesignModeStatic) return; var query = this.Manager.GetProductsQuery().OrderBy(p => p.ProductID).Parse(this.QueryDescriptor); query.IncludeTotalCount = true; this.Manager.Load( query, op => { if (op.IsComplete) { this.Products = new Intersoft.Client.Data.ComponentModel.PagedCollectionView(op.Entities); this.QueryDescriptor.PageDescriptor.TotalItemCount = op.TotalEntityCount; } else { MessageBox.Show(op.Error.ToString()); } }, true); } private void OnQueryChanged(object sender, System.EventArgs e) { this.LoadProducts(); } #endregion } } |
View | ![]() |
---|---|
<Intersoft:UXGridView AutoGenerateColumns="False" QueryOperation="Server" CanUserPage="True" PageSize="20" RowHeaderVisibility="Visible" IsBusy="{Binding IsBusy, Mode=TwoWay}" IsRefreshed="{Binding IsRefreshed, Mode=TwoWay}" ItemsSource="{Binding Products}" SortDescriptors="{Binding QueryDescriptor.SortDescriptors, Mode=TwoWay}" PageDescriptor="{Binding QueryDescriptor.PageDescriptor}" GroupFootersVisibility="Visible" GroupByBoxVisibility="Visible" CanUserAddRows="True" CanUserDeleteRows="True" CanUserEditRows="True" NewItem="{Binding NewProduct, Mode=TwoWay}" ValidateRowCommand="{Binding ValidateRowCommand}" InsertRowCommand="{Binding InsertRowCommand}" DeleteRowCommand="{Binding DeleteRowCommand}" PrepareNewRowCommand="{Binding PrepareNewRowCommand}" UpdateCellCommand="{Binding UpdateCellCommand}" UpdateRowCommand="{Binding UpdateRowCommand}" SaveChangesCommand="{Binding SaveChangesCommand}" RejectRowCommand="{Binding RejectRowCommand}" RejectChangesCommand="{Binding RejectChangesCommand}" HasChanges="{Binding HasChanges}"> <Intersoft:UXGridView.GroupDescriptors> <Intersoft:UXGridViewGroupDescriptor PropertyName="CategoryID"/> </Intersoft:UXGridView.GroupDescriptors> <Intersoft:UXGridView.Columns> <Intersoft:UXGridViewTextColumn Header="Category ID" Binding="{Binding CategoryID}"/> <Intersoft:UXGridViewTextColumn Header="Product ID" Binding="{Binding ProductID}" IsReadOnly="True" Aggregate="Count" FooterFormatString="Count = {0}"/> <Intersoft:UXGridViewTextColumn Header="Product Name" Binding="{Binding ProductName}"/> <Intersoft:UXGridViewTextColumn Header="Units In Stock" Binding="{Binding UnitsInStock}" Aggregate="Max" FooterFormatString="Max = {0}"/> <Intersoft:UXGridViewTextColumn Header="Unit Price" Binding="{Binding UnitPrice}" Aggregate="Avg" FooterFormatString="Avg = {0:n2}"/> <Intersoft:UXGridViewTextColumn Header="Units On Order" Binding="{Binding UnitsOnOrder}" Aggregate="Min" FooterFormatString="Min = {0}"/> <Intersoft:UXGridViewTextColumn Header="Quantity Per Unit" Binding="{Binding QuantityPerUnit}"/> </Intersoft:UXGridView.Columns> </Intersoft:UXGridView> |
After the commands are bound to the ViewModel, the actual CUD operations are completely delegated to you for further processing.
If you prefer to automatically update the records after each CUD operation, you can perform the server operation in the InsertRowCommand, UpdateRowCommand and DeleteRowCommand respectively, and perhaps followed with the RefreshCommand to refresh the data. However, if you prefer a batch update, you can notify the UXGridView by setting the HasChanges property to true, and later call the SaveChanges method to save all changes in one server call.
The batch update capability might not be available in all data providers such as WCF RIA. When server query is enabled in WCF RIA such as paging, sorting, and filtering; WCF RIA always override the existing changes with the fresh data from the database. This behavior is due to WCF RIA not supporting client-side caching. In this case, you may want to perform immediate update and/or refresh after each CUD operation.
Handling the CUD operation can be a tedious task in the RIA development due to the repetitive processes. ClientUI provides generic templates that you can use to quickly handle the CUD operation in efficient way. To learn more how to handle the CUD operation, see Walkthrough: Handle CUD Operation using UXGridView and WCF RIA and Walkthrough: Handle CUD Operation using UXGridView and DevForce.
The following examples show how to handle each data operation in MVVM pattern through the commands implementation.
Create Operation
To implement create operation using MVVM pattern, you handle the PrepareNewRow and InsertRow command in the ViewModel, shown in the example below.
CS | ![]() |
---|---|
public void ExecuteInsertRow(object parameter) { this.NewProduct = null; if (!this.IsBatchUpdate) this.SaveChanges(); else this.HasChanges = true; } public void ExecutePrepareNewRow(object parameter) { // It's possible to initialize the new row with default values // Example: // product.ProductName = "New Product"; this.NewProduct = this.EditableProductsSource.Create(); this.EditableProductsSource.Insert(this.NewProduct); } |
![]() |
The prepare new row is required to initializes the new item object. |
Update Operation
To implement the update operation using MVVM pattern, you handle the UpdateRow command in the ViewModel, shown in the example below.
CS | ![]() |
---|---|
public void ExecuteUpdateCell(object parameter) { object[] updateCellParameters = (object[])parameter; object product = updateCellParameters.GetValue(0); string property = updateCellParameters.GetValue(1).ToString(); // perform cell-level validation if required } public void ExecuteUpdateRow(object parameter) { if (!this.IsBatchUpdate) this.SaveChanges(); else this.HasChanges = true; } |
Delete Operation
To implement the delete operation using MVVM pattern, you handle the DeleteRow command in the ViewModel, shown in the example below.
CS | ![]() |
---|---|
public void ExecuteDeleteRow(object parameter) { this.EditableProductsSource.Delete(parameter as IList); if (!this.IsBatchUpdate) this.SaveChanges(); else this.HasChanges = true; } |
Row Validation and Cancellation
Before the changes are committed, you may want to perform data validation to the particular changes. You can implement the data validation by handling the ValidateRow command in the ViewModel. In addition, you can also add custom logic when the row changes is cancelled by handling the RejectRow command. Both scenarios are illustrated in the example below.
CS | ![]() |
---|---|
public void ExecuteValidateRow(object parameter) { Product product = (Product)parameter; product.ValidationErrors.Clear(); if (product.CategoryID < 1 || product.CategoryID > 8) product.ValidationErrors.Add(new ValidationResult("Specified CategoryID is not existed", new string[] { "CategoryID" })); if (product.UnitPrice < 0) product.ValidationErrors.Add(new ValidationResult("Unit Price can not be less than 0", new string[] { "UnitPrice" })); if (product.UnitsInStock < 0) product.ValidationErrors.Add(new ValidationResult("Units in Stock can not be less than 0", new string[] { "UnitsInStock" })); if (product.UnitsOnOrder < 0) product.ValidationErrors.Add(new ValidationResult("Units on Order can not be less than 0", new string[] { "UnitsOnOrder" })); } public void ExecuteRejectRow(object parameter) { if (parameter != null) this.EditableProductsSource.RejectChanges(parameter); this.NewProduct = null; } |
Batch Update
To implement batch update using MVVM pattern, you handle the RejectChanges and SaveChanges command such as shown in the following example.
CS | ![]() |
---|---|
public void ExecuteRejectChanges(object parameter) { this.EditableProductsSource.RejectChanges(); this.HasChanges = false; } public void ExecuteSaveChanges(object parameter) { // if users click on the save changes while the new row is being edited, // presume the new row isn't intended if (this.NewProduct != null) this.EditableProductsSource.RejectChanges(this.NewProduct); this.SaveChanges(); } |
For guided step-by-step instructions to implement data operation in UXGridView, see Walkthrough: Handle CUD Operation using UXGridView and WCF RIA and Walkthrough: Handle CUD Operation using UXGridView and DevForce.
IsReadOnly and IsReadOnly Binding
You can specify whether a column is editable through the IsReadOnly property in UXGridViewColumn. In more advanced scenarios, you may also want to disable editing on certain columns based on the value of other columns. For examples, if a product has been discontinued, editing in all other columns should be disabled.
To enforce the read only setting to a certain row in this scenario, you can use the IsReadOnlyBinding that is available in both UXGridView and UXGridViewBoundColumn level. The IsReadOnlyBinding at UXGridView level is used to evaluate whether the user can edit the row, while the IsReadOnlyBinding at UXGridViewBoundColumn level is used to evaluate whether the user can edit the cell.
The following code shows how to implement the IsReadOnlyBinding to achieve the above scenario.
XAML | ![]() |
---|---|
<Intersoft:UXGridView IsReadOnlyBinding="{Binding Discontinued}"> <Intersoft:UXGridView.Columns> <Intersoft:UXGridViewCheckBoxColumn Header="Discontinued" Binding="{Binding Discontinued}"/> <Intersoft:UXGridViewTextColumn Header="Category ID" Binding="{Binding CategoryID}" IsReadOnlyBinding="{Binding Discontinued}"/> <Intersoft:UXGridViewTextColumn Header="Product ID" Binding="{Binding ProductID}" IsReadOnly="True" Aggregate="Count" FooterFormatString="Count = {0}"/> <Intersoft:UXGridViewTextColumn Header="Product Name" Binding="{Binding ProductName}"/> </Intersoft:UXGridView.Columns> </Intersoft:UXGridView> |
Lost Focus Action
When you lost focus from an edited row, by default UXGridView will commit the current edit operation you can change this behavior by changing the LostFocusAction property and NewRowLostFocusAction property to CancelEdit.
This setting will force UXGridView to cancel the current edit operation when the focus is lost from the edited row. NewRowLostFocusAction property determines the action for new row object, while LostFocusAction determines the action for existing rows.
New Row Position
The rows of UXGridView are determined by the number of items specified in items source. If the number of items in items source is less than the available screen estate it will show blank white space.
Setting the EmptyRowsVisibility to Visible enables UXGridView to show empty rows in this blank white space area, to give a better look and feel.
When CanUserAddRows is enabled and EmptyRowsVisibility is set to Visible, you can change the new row position to bottom through NewRowPosition property.
![]() |
Note that some features are not compatible when CanUserAddRows is enabled and EmptyRowsVisibility is set to Visible such as paging and grouping. |
The following are several ways to enter edit mode when the new row position is set to Bottom:
- Press down key on last row.
- Press tab key on last column of the last row.
- Double click on any empty rows when EnterNewRowOnClick is set to False.
- Click on any on any empty rows when EnterNewRowOnClick is set to True.