By default, if the value of a cell does not pass the validation process, the cell will exit edit mode and the value of the cell will be returned to its original valid value.
Custom validation can be provided at either the row or the cell level depending on the kind of validation you want to provide. For example, for values entered into an InsertionRow, custom validation would be provided at the row level since "normally", the user would be allowed to enter all the values in a row before they are validated. In other cases, such as when modifying existing values in the grid, validation would be done at the cell level because "normally" you would allow the user to change the value of only one cell.
As of version 2.2 of the grid, it is possible to provide custom error behaviors for both cells and rows (CellRow) when the value of a cell does not pass the validation process. This is done by setting the ErrorDescription property in either the LeavingEdit event of a cell or in the EndingEdit event of a row.
Custom row validation is done using the EndingEdit and ValidationError events. In the EndingEdit event, the custom validation is provided and in the ValidationError event, you can decided if you want to allow the row to exit edit mode in the case where the values of one or more of its cells do not pass the validation process (see Example 1).
The row's ValidationError event will be raised if e.Cancel is set to true in the EndingEdit event and/or if an exception is thrown in the EndingEdit event.
If validation is used at the row level, the values of each cell in the row will only be validated once the current row changes. This allows the end user to change the values of any cell in the row before they are validated and is most commonly used with the InsertionRow.
Custom cell validation is done using the LeavingEdit and ValidationError events. In the LeavingEdit event, the custom validation is provided and in the ValidationError event, you can decided if you want to allow the cell to exit edit mode in the case where its value does not pass the validation process (see Example 2).
The cell's ValidationError event will be raised when e.Cancel is set to true in the LeavingEdit event, when an exception is thrown in the LeavingEdit event and/or when a validation exception is thrown by the underlying datasource.
If validation is used at the cell level, the value of the cell being edited (current cell) will be validated as soon as the current cell changes. This is most commonly used for cells that already contain data (i.e., DataCell).
Rather than setting e.Cancel to true in the row's EndingEdit event or in the cell's LeavingEdit event, it is also possible to throw an exception and catch it in the ValidationError event (see Example 3).
The CellEditorManager's Validate method can be used to validate if the editor's value is valid before it is assigned to the underlying cell. In addition to the built-in validation provided by the Validate method, the Validating event can be handled to provide additional validation criteria.
When deriving from the CellEditorManager class, the ValidateCore method can be overridden to either add validation criteria to the built-in validation, or to provide complete custom validation.
To provide custom error behaviors for both cells and rows (CellRow) when the value of a cell does not pass the validation process the ErrorDescription property of an cell or cell row can be set to the desired error message. Normally, the ErrorDescription property would be set in either the LeavingEdit event of a cell or in the EndingEdit event of a row and reset in the EditLeft event of the cell or in the EditEnded event of the row (see Example 4).
Depending on the settings of the grid's ErrorBehaviors property, (by default) a tooltip will be displayed when the mouse cursor passes over the cell whose value failed the validation process, the cell will blink with a red background color, a tooltip will appear on the corresponding row's row selector, and the error icon displayed in the row selector will blink.
Note that the all editors, with the exception of the GridComboBox and the GridDateTimePicker, will blink (if that error behavior is active).
The Validating sample application included with Xceed Grid for WinForms demonstrates all of the techniques and examples mentioned above and will provide you with a better understanding of how the grid's validation process works. A visual representation of the validation process is also available here.
Example 1: Row validation
VB.NET |
Copy Code |
---|---|
Dim grid As New GridControl() grid.Columns.Add( New Column( "Item", GetType( string ) ) ) grid.Columns.Add( New Column( "Quantity", GetType( Integer ) ) ) ' Handle the EndingEdit and ValidationError events of all the DataRows in the grid. AddHandler grid.DataRowTemplate.EndingEdit, AddressOf Me.row_endingEdit AddHandler grid.DataRowTemplate.ValidationError, AddressOf Me.row_validation ' Add 2 rows to the grid Dim row1 As Xceed.Grid.DataRow = grid.DataRows.AddNew() row1.Cells( "Item" ).Value = "Computers" row1.Cells( "Quantity" ).Value = 42 row1.EndEdit() Dim row2 as Xceed.Grid.DataRow = grid.DataRows.AddNew() row2.Cells( "Item" ).Value = "Keyboards" row2.Cells( "Quantity" ).Value = 5 row2.EndEdit() grid.FixedHeaderRows.Add( New ColumnManagerRow() ) Me.Controls.Add( grid ) Private Sub row_endingEdit( ByVal sender As Object, ByVal e As System.ComponentModel.CancelEventArgs ) ' Provide custom validation. If the validation fails, ' notify the user and set e.Cancel to true. Dim row As Xceed.Grid.DataRow = CType( sender, Xceed.Grid.DataRow ) If ( CTYpe( row.Cells( "Item" ).Value, string ) = "Computers" ) And _ ( CTYpe( row.Cells( "Quantity" ).Value, Integer ) > 100 ) Then ' You can also throw an exception here rather than setting e.Cancel to true. MessageBox.Show( "Cannot order more than 100 computers at a time" ) e.Cancel = True End If End Sub Private Sub row_validation( ByVal sender As object, ByVal e As RowValidationErrorEventArgs ) 'Prevent the row from leaving edit mode e.CancelEdit = False End Sub |
C# |
Copy Code |
---|---|
GridControl grid = new GridControl(); grid.Columns.Add( new Column( "Item", typeof( string ) ) ); grid.Columns.Add( new Column( "Quantity", typeof( int ) ) ); // Handle the EndingEdit and ValidationError events of all the DataRows in the grid. grid.DataRowTemplate.EndingEdit += new CancelEventHandler( this.row_endingEdit ); grid.DataRowTemplate.ValidationError += new RowValidationErrorEventHandler( this.row_validation ); // Add 2 rows to the grid Xceed.Grid.DataRow row1 = grid.DataRows.AddNew(); row1.Cells[ "Item" ].Value = "Computers"; row1.Cells[ "Quantity" ].Value = 42; row1.EndEdit(); Xceed.Grid.DataRow row2 = grid.DataRows.AddNew(); row2.Cells[ "Item" ].Value = "Keyboards"; row2.Cells[ "Quantity" ].Value = 5; row2.EndEdit(); grid.FixedHeaderRows.Add( new ColumnManagerRow() ); this.Controls.Add( grid ); private void row_endingEdit( object sender, System.ComponentModel.CancelEventArgs e ) { // Provide custom validation. If the validation fails, // notify the user and set e.Cancel to true. Xceed.Grid.DataRow row = ( Xceed.Grid.DataRow )sender; if( ( ( string )row.Cells[ "Item" ].Value == "Computers" ) && ( ( int )row.Cells[ "Quantity" ].Value > 100 ) ) { // You can also throw an exception here rather than setting e.Cancel to true. MessageBox.Show( "Cannot order more than 100 computers at a time" ); e.Cancel = true; } } private void row_validation( object sender, RowValidationErrorEventArgs e ) { // Prevent the row from leaving edit mode. e.CancelEdit = false; } |
Example 2: Cell validation
VB.NET |
Copy Code |
---|---|
Dim grid As New GridControl() grid.Columns.Add( New Column( "Item", GetType( string ) ) ) grid.Columns.Add( New Column( "Quantity", GetType( Integer ) ) ) ' Handle the LeavingEdit and ValidationError events of each cell ' in the grid's DataRows. Dim cell As DataCell For Each cell in grid.DataRowTemplate.Cells AddHandler cell.LeavingEdit, AddressOf Me.cell_leavingEditAddHandler cell.ValidationError, AddressOf Me.cell_validation Next cell ' Add 2 rows to the grid Dim row1 As Xceed.Grid.DataRow = grid.DataRows.AddNew() row1.Cells( "Item" ).Value = "Computers" row1.Cells( "Quantity" ).Value = 42 row1.EndEdit() Dim row2 As Xceed.Grid.DataRow = grid.DataRows.AddNew() row2.Cells( "Item" ).Value = "Keyboards" row2.Cells( "Quantity" ).Value = 5 row2.EndEdit() grid.FixedHeaderRows.Add( New ColumnManagerRow() ) Me.Controls.Add( grid ) Private Sub cell_leavingEdit( ByVal sender As Object, ByVal e As LeavingEditEventArgs ) ' Provide custom validation. If the validation fails, ' notify the user and set e.Cancel to true. Dim cell As Cell = CTYpe( sender, Cell ) If( ( ( cell.FieldName = "Item" ) And _ ( CType( e.NewValue, string ) = "Computers" ) And _ ( CType( cell.ParentRow.Cells( "Quantity" ).Value, Integer ) > 100 ) ) Or _ ( ( cell.FieldName = "Quantity" ) And _ ( Integer.Parse( CType( e.NewValue, string ) ) > 100 ) And _ ( CType( cell.ParentRow.Cells( "Item" ).Value, string ) = "Computers" ) ) ) Then ' You can also throw an exception here rather than setting e.Cancel to true. MessageBox.Show( "Cannot order more than 100 computers at a time" ) e.Cancel = True End If End Sub Private Sub cell_validation( ByVal sender As Object, ByVal e As CellValidationErrorEventArgs ) ' Prevent the cell from leaving edit mode. e.CancelEdit = False End Sub |
C# |
Copy Code |
---|---|
GridControl grid = new GridControl(); grid.Columns.Add( new Column( "Item", typeof( string ) ) ); grid.Columns.Add( new Column( "Quantity", typeof( int ) ) ); // Handle the LeavingEdit and ValidationError events of each cell // in the grid's DataRows. foreach( DataCell cell in grid.DataRowTemplate.Cells ) { cell.LeavingEdit += new LeavingEditEventHandler( this.cell_leavingEdit ); cell.ValidationError += new CellValidationErrorEventHandler( this.cell_validation ); } // Add 2 rows to the grid Xceed.Grid.DataRow row1 = grid.DataRows.AddNew(); row1.Cells[ "Item" ].Value = "Computers"; row1.Cells[ "Quantity" ].Value = 42; row1.EndEdit(); Xceed.Grid.DataRow row2 = grid.DataRows.AddNew(); row2.Cells[ "Item" ].Value = "Keyboards"; row2.Cells[ "Quantity" ].Value = 5; row2.EndEdit(); grid.FixedHeaderRows.Add( new ColumnManagerRow() ); this.Controls.Add( grid ); private void cell_leavingEdit( object sender, LeavingEditEventArgs e ) { // Provide custom validation. If the validation fails, // notify the user and set e.Cancel to true. Cell cell = ( Cell )sender; if( ( ( cell.FieldName == "Item" ) && ( ( string )e.NewValue == "Computers" ) && ( ( int )cell.ParentRow.Cells[ "Quantity" ].Value > 100 ) ) || ( ( cell.FieldName == "Quantity" ) && ( int.Parse( ( string )e.NewValue ) > 100 ) && ( ( string )cell.ParentRow.Cells[ "Item" ].Value == "Computers" ) ) ) { // You can also throw an exception here rather than setting e.Cancel to true. MessageBox.Show( "Cannot order more than 100 computers at a time" ); e.Cancel = true; } } private void cell_validation( object sender, CellValidationErrorEventArgs e ) { // Prevent the cell from leaving edit mode. e.CancelEdit = false; } |
Example 3: Validation via exception handling
VB.NET |
Copy Code |
---|---|
Private Sub cell_leavingEdit( ByVal sender As Object, ByVal e As LeavingEditEventArgs ) ' Provide custom validation. If the validation fails, ' notify the user and set e.Cancel to true. Dim cell As Cell = CTYpe( sender, Cell ) If( ( ( cell.FieldName = "Item" ) And _ ( CType( e.NewValue, string ) = "Computers" ) And _ ( CType( cell.ParentRow.Cells( "Quantity" ).Value, Integer ) > 100 ) ) Or _ ( ( cell.FieldName = "Quantity" ) And _ ( Integer.Parse( CType( e.NewValue, string ) ) > 100 ) And _ ( CType( cell.ParentRow.Cells( "Item" ).Value, string ) = "Computers" ) ) ) Then Throw New Exception( "Cannot order more than 100 computers at a time" ) End If End Sub Private Sub cell_validation( ByVal sender As Object, ByVal e As CellValidationErrorEventArgs ) ' The exception that we placed in the cells LeavingEdit event handler ' (cell_leavingEdit) was thrown so we will display our custom message. If Not e.Exception.InnerException Is Nothing Then MessageBox.Show(e.Exception.InnerException.Message) Else ' Another exception was thrown because of failed validation so we will ' simply show the exception message. MessageBox.Show(e.Exception.Message) End If ' Prevent the cell from leaving edit mode. e.CancelEdit = False End Sub |
C# |
Copy Code |
---|---|
private void cell_leavingEdit( object sender, LeavingEditEventArgs e ) { // Provide custom validation. If the validation fails, // notify the user and set e.Cancel to true. Cell cell = ( Cell )sender; if( ( ( cell.FieldName == "Item" ) && ( ( string )e.NewValue == "Computers" ) && ( ( int )cell.ParentRow.Cells[ "Quantity" ].Value > 100 ) ) || ( ( cell.FieldName == "Quantity" ) && ( int.Parse( ( string )e.NewValue ) > 100 ) && ( ( string )cell.ParentRow.Cells[ "Item" ].Value == "Computers" ) ) ) { throw new Exception( "Cannot order more than 100 computers at a time" ); } } private void cell_validation( object sender, CellValidationErrorEventArgs e ) { // The exception that we placed in the cells' LeavingEdit event handler // (cell_leavingEdit) was thrown so we will display our custom message. if( e.Exception.InnerException != null ) { MessageBox.Show( e.Exception.InnerException.Message ); } else { // Another exception was thrown because of failed validation so we will // simply show the exception message. MessageBox.Show( e.Exception.Message ); } ' Prevent the cell from leaving edit mode. e.CancelEdit = false; } |
Example 4: Custom error behaviors
VB.NET |
Copy Code |
---|---|
OleDbDataAdapter1.Fill( DataSet11 ) Dim grid As New GridControl() Me.Controls.Add( grid ) grid.Dock = DockStyle.Fill grid.BeginInit() grid.DataSource = DataSet11.Tables( 0 ) grid.FixedHeaderRows.Add( New ColumnManagerRow() ) grid.GroupTemplates.Add( New Group( "ShipCountry" ) ) AddHandler grid.DataRowTemplate.Cells( "Freight" ).LeavingEdit, AddressOf Me.cell_leavingEdit AddHandler grid.DataRowTemplate.Cells( "Freight" ).ValidationError, AddressOf Me.cell_validationError AddHandler grid.DataRowTemplate.Cells( "Freight" ).EditLeft, AddressOf Me.cell_editLeft ' Set the desired error behavior grid.ErrorBehaviors = ErrorBehaviors.CellBlink Or ErrorBehaviors.CellColor Or _ ErrorBehaviors.CellToolTip Or ErrorBehaviors.RowToolTip Or _ ErrorBehaviors.RowSelectorToolTip Or ErrorBehaviors.RowSelectorIcon ' Set the desired blink rate. grid.ErrorBlinkRate = 300 grid.EndInit() Private Sub cell_leavingEdit( ByVal sender As Object, ByVal e As LeavingEditEventArgs ) ' Since we only subscribed to the LeavingEdit event of ' the Freight cell, we will not verify the sender ' to make sure that it is Freight. If Decimal.Parse( CType( e.NewValue, String ) ) < 2 Then CType( sender, Cell ).ErrorDescription = "The freight charge cannot be less than 2." e.Cancel = True End If End Sub Private Sub cell_validationError( ByVal sender As Object, ByVal e As CellValidationErrorEventArgs ) ' Prevent the cell from leaving edit mode if the validation ' process has failed. e.CancelEdit = false End Sub Private Sub cell_editLeft( ByVal sender As Object, ByVal e As EditLeftEventArgs ) ' Reset the error description to its default value: "" CType( sender, Cell ).ResetErrorDescription() End Sub |
C# |
Copy Code |
---|---|
oleDbDataAdapter1.Fill( dataSet11 ); GridControl grid = new GridControl(); this.Controls.Add( grid ); grid.Dock = DockStyle.Fill; grid.BeginInit(); grid.DataSource = dataSet11.Tables[ 0 ]; grid.FixedHeaderRows.Add( new ColumnManagerRow() ); grid.GroupTemplates.Add( new Group( "ShipCountry" ) ); grid.DataRowTemplate.Cells[ "Freight" ].LeavingEdit += new LeavingEditEventHandler( this.cell_leavingEdit ); grid.DataRowTemplate.Cells[ "Freight" ].ValidationError += new CellValidationErrorEventHandler( this.cell_validationError ); grid.DataRowTemplate.Cells[ "Freight" ].EditLeft += new EditLeftEventHandler( this.cell_editLeft ); // Set the desired error behavior grid.ErrorBehaviors = ErrorBehaviors.CellBlink | ErrorBehaviors.CellColor | ErrorBehaviors.CellToolTip | ErrorBehaviors.RowToolTip | ErrorBehaviors.RowSelectorToolTip | ErrorBehaviors.RowSelectorIcon; // Set the desired blink rate. grid.ErrorBlinkRate = 300; grid.EndInit(); private void cell_leavingEdit( object sender, LeavingEditEventArgs e ) { // Since we only subscribed to the LeavingEdit event of // the Freight cell, we will not verify the sender // to make sure that it is Freight. if( Decimal.Parse( ( string )e.NewValue ) < 2 ) { ( ( Cell )sender ).ErrorDescription = "The freight charge cannot be less than 2."; e.Cancel = true; } } private void cell_validationError( object sender, CellValidationErrorEventArgs e ) { // Prevent the cell from leaving edit mode if the validation // process has failed. e.CancelEdit = false; } private void cell_editLeft( object sender, EditLeftEventArgs e ) { // Reset the error description to its default value: "" ( ( Cell )sender ).ResetErrorDescription(); } |