Questions re Validation

Posts   
 
    
tedh
User
Posts: 34
Joined: 14-Dec-2006
# Posted on: 15-Dec-2006 06:04:21   

I am using an Infragistics UltraGrid version 6.3 and the most recent demo version of LLBLGenPro.

My validation code is as follows:

    
public partial class CurrencyEntity : EntityBase, ISerializable
    {
        protected override bool OnValidateFieldValue(int fieldIndex, object value)
        {
            bool valid = true;
            switch ((CurrencyFieldIndex)fieldIndex)
            {
                case CurrencyFieldIndex.Code:
                    SetEntityFieldError(CurrencyFieldIndex.Code.ToString(), string.Empty, false);
                    if (((string)value).Length != 2)
                    {
                        SetEntityFieldError(CurrencyFieldIndex.Code.ToString(), "Code must be two characters in length", false);
                        valid = false;
                    }
                    break;
                case CurrencyFieldIndex.Description:
                    SetEntityFieldError(CurrencyFieldIndex.Description.ToString(), string.Empty, false);
                    if (((string)value).Length <= 50)
                    {
                        SetEntityFieldError(CurrencyFieldIndex.Description.ToString(), "Description must be <= 50 characters in length", false);
                        valid = false;
                    }
                    break;
                default:
                    valid = true;
                    break;
            }
            return valid;
        }
    }

When I run my code and the Description field error message is displayed it is prepended with the following text: "Cell-Display DataError ToolTip(): Description:" before my stated error message text "Description must be <= 50 characters in length". How do I stop the prepended text from showing?

Also I want to provide feedback to the user as they tab from one grid cell to the next. Each time the error message is displayed the keyed text is cleared. I want the user to see what they keyed and be able to correct it. How can I accomplish this?

Thanks

Walaa avatar
Walaa
Support Team
Posts: 14950
Joined: 21-Aug-2005
# Posted on: 15-Dec-2006 09:53:10   

Also I want to provide feedback to the user as they tab from one grid cell to the next. Each time the error message is displayed the keyed text is cleared. I want the user to see what they keyed and be able to correct it. How can I accomplish this?

Check these threads: http://www.llblgen.com/tinyforum/Messages.aspx?ThreadID=8090 http://www.llblgen.com/tinyforum/Messages.aspx?ThreadID=7927

When I run my code and the Description field error message is displayed it is prepended with the following text: "Cell-Display DataError ToolTip(): Description:" before my stated error message text "Description must be <= 50 characters in length". How do I stop the prepended text from showing?

I don't know about this, but it seems to me that the UltraGrid is adding this part after you call SetEntityFieldError(), would you please set a breakpoint at your code to check for the values in the DataErrorInfoErrorsPerField dictionary property of your entity, just before and after the SetEntityFieldError() call.

Max avatar
Max
User
Posts: 221
Joined: 14-Jul-2006
# Posted on: 15-Dec-2006 09:55:17   

I believe that it's about the ultraGrid.

You can take a look at the following ultragrid events:

*) ultragrid.CellDataError event here you have some usefull property, I use the following e.StayInEditMode = True e.RaiseErrorEvent = True

*) ultragrid.Error event e.Cancel = True 'this tell to uGrid to don't show his own error message box e.DataErrorInfo 'this contain usefull information from the error catched by ultraGrid

Hi, Max

tedh
User
Posts: 34
Joined: 14-Dec-2006
# Posted on: 15-Dec-2006 17:26:33   

Thank you for all the suggestions.

There are still some issues:

1) If a grid cell value fails validation (as determined within OnValidateFieldValue) the error symbol and error tooltip display but the ultragrid.CellDataError and ultragrid.Error events are not raised. I am going to raise this with Infragistics because maybe as part of their support for IDataErrorInfo they should provide the option of having those events raised.

2) Reading through the referenced threads it was suggested that a failed validation means bad data which cannot be persisted and so is reverted back to its original value which was presumably valid (the result being that the user sees an error message but not the data in error (eg. sees "Bad Part Number" message but cannot see the part number that caused the error). I agree that bad values cannot be persisted, but there is a difference between the temporary work-in-process data that is displayed in gui components versus what is ultimately persisted. It seems that what I am trying to accomplish is also being requested by other users (see referenced threads), but maybe the solution lies with the component vendors (eg. Infragistics).

Otis avatar
Otis
LLBLGen Pro Team
Posts: 39614
Joined: 17-Aug-2003
# Posted on: 16-Dec-2006 12:47:39   

tedh wrote:

Thank you for all the suggestions.

There are still some issues:

1) If a grid cell value fails validation (as determined within OnValidateFieldValue) the error symbol and error tooltip display but the ultragrid.CellDataError and ultragrid.Error events are not raised. I am going to raise this with Infragistics because maybe as part of their support for IDataErrorInfo they should provide the option of having those events raised.

That's indeed outside the scope of our code, so Infragistics should solve this.

2) Reading through the referenced threads it was suggested that a failed validation means bad data which cannot be persisted and so is reverted back to its original value which was presumably valid (the result being that the user sees an error message but not the data in error (eg. sees "Bad Part Number" message but cannot see the part number that caused the error). I agree that bad values cannot be persisted, but there is a difference between the temporary work-in-process data that is displayed in gui components versus what is ultimately persisted. It seems that what I am trying to accomplish is also being requested by other users (see referenced threads), but maybe the solution lies with the component vendors (eg. Infragistics).

That's impossible: you have ONE value for an entity field: the value shown in the grid cell, as databinding does this. If the user changes that value and moves to the next cell, the value set action is performed, validation is done, and if validation fails, the value thus can't be set and the field thus DOESN'T receive the value and as the field has to have a value (either null, or a real value) the previous value is set. Otherwise, the wrong value which doesn't pass validation is set as the field value which would of course be wrong.

Frans Bouma | Lead developer LLBLGen Pro
tedh
User
Posts: 34
Joined: 14-Dec-2006
# Posted on: 16-Dec-2006 20:32:43   

Hi Otis,

Regarding point 2 are you aware that Infragistic's ultraGrids have an UpdateMode property with 5 configurable values?. This controls the timing as to when changes to a cell value updates the databinding. In my case ultraGrid1UpdateMode = UpdateMode.OnRowChange.

Infragistics says "The Bound IList is updated when the user modifies cell(s) in a row and then selects a different row.".

In addition each Infragistics grid cell has an OriginalValue property in addition to a Text (Current) property.

So if cell1 in row1 of an ultraGrid is modified and we proceed to tab to cell2, then OnValidateFieldValue() executes for the entity field associated with cell1. If the validation fails then the entity is not updated and OnValidateFieldValue() returns false. Now this has no effect yet on databinding because we are still on the same grid row. So why is it unreasonable to expect that returning to the grid we would see the error message plus the value causing the error rather than the original cell value?

We could also take this a step further. We might have set ultraGrid1.UpdateMode = UpdateMode.OnUpdate.

Infragistics defines this as " The bound IList is updated when the update function on the grid is called."

In this scenario the user might have changes to any number of cells on different rows and databinding would not be affected until the user in the end clicked on an update button on the screen which invoked ultraGrid.update(). If there were multiple cells in error then these would all be decorated with errors as we exited each cell on the grid.

So my point is that Infragistics maintains a current and original value for each cell and that when a LLBLGenPro validation fails that does not mean that the current incorrect value cannot be displayed in the grid and identified as in error. As I understand it you are saying that this cannot be the case because of databinding and I replied that with Infragistics the timing of the databinding is configurable with ultraGrid1.UpdateMode .

If I am wrong then please accept my apologies and I hope to learn something from your response.

Otis avatar
Otis
LLBLGen Pro Team
Posts: 39614
Joined: 17-Aug-2003
# Posted on: 16-Dec-2006 21:23:55   

You've to understand that we can't write code especially for infragistics, but if I understand your text correctly, you should call the validation after an event is raised from the grid. This is because otherwise the event code is called after the value is set by the grid in the object bound to the grid row (the entity), so all the entity can do: accept the value or reject the value.

You see: the entity field has just one value: customer.CompanyName has just one return value, and if the grid sets that value, and validation fails, what should customer.CompanyName be? -> the value it had, not the value you set but which failed validation.

what happens inside the grid is the grid's responsibility. If the grid decides to set the entity's property, then the entity's validation kicks in and if that validation rejects the value, the value isn't set in the entity, so the grid either has to show a previous value in teh cell, or do other things, but that's not the concern of the entity.

Databinding in .net is very strict: you have a control bound to a property, and the control displays the property and when the control updates the property, the property gets the new value. UNLESS the property rejects the value and the CHANGE event isn't raised.

What the grid does with the value is up to the grid, the entity simply can't accept a value that fails to pass validation, simply because it doesn't make any sense: customer.CompanyName is then suddenly returning a value, but it's not really a valid value... that can't happen.

So if the grid is able to store multiple values, then the validation should be done when the cell's value is updated. You can do that rather easily by calling the entity's validator object with the value currently in the cell. But it can't be the job of the entity, as that would mean the entity field gets a value it can't accept.

With datatables etc. this doesn't happen, as there's no validation inside a datatable when the row fields are filled.

Frans Bouma | Lead developer LLBLGen Pro
Max avatar
Max
User
Posts: 221
Joined: 14-Jul-2006
# Posted on: 18-Dec-2006 12:50:09   

tedh wrote:

Thank you for all the suggestions.

There are still some issues:

1) If a grid cell value fails validation (as determined within OnValidateFieldValue) the error symbol and error tooltip display but the ultragrid.CellDataError and ultragrid.Error events are not raised. I am going to raise this with Infragistics because maybe as part of their support for IDataErrorInfo they should provide the option of having those events raised.

2) Reading through the referenced threads it was suggested that a failed validation means bad data which cannot be persisted and so is reverted back to its original value which was presumably valid (the result being that the user sees an error message but not the data in error (eg. sees "Bad Part Number" message but cannot see the part number that caused the error). I agree that bad values cannot be persisted, but there is a difference between the temporary work-in-process data that is displayed in gui components versus what is ultimately persisted. It seems that what I am trying to accomplish is also being requested by other users (see referenced threads), but maybe the solution lies with the component vendors (eg. Infragistics).

1) try look at property UltraGrid.DisplayLayout.Override.SupportDataErrorInfo = SupportDataErrorInfo.RowsAndCells

2) try look at UltraGrid.UpdateMode = UpdateMode.OnRowChangeOrLostFocus

Max avatar
Max
User
Posts: 221
Joined: 14-Jul-2006
# Posted on: 18-Dec-2006 13:01:32   

tedh wrote:

Thank you for all the suggestions.

2) [...CUT...] I agree that bad values cannot be persisted, but there is a difference between the temporary work-in-process data that is displayed in gui components versus what is ultimately persisted.

My approach is: *) inside EntityValidator.ValidateFieldValue I need to create validation code that can handle the "temporary work-in-process data",

) inside EntityValidator.ValidateEntityBefore/ EntityValidator.ValidateEntityAfter* I put the code that handle the "ultimately persisted" data.

tedh
User
Posts: 34
Joined: 14-Dec-2006
# Posted on: 19-Dec-2006 01:32:19   

Hi Max,

Thanks for the suggestions.

I already have set UltraGrid.DisplayLayout.Override.SupportDataErrorInfo = SupportDataErrorInfo.RowsAndCells because this is the only way to get IDataErrorInfo errors to display in the grid. The error message part is working fine. My problem is that LLBLGen rejects the invalid data value and resets the cell to its original value whereas I want the cell to display the error message plus the invalid data value.

I would be very interested to see a code example of how you have managed to overcome this behaviour.

tedh
User
Posts: 34
Joined: 14-Dec-2006
# Posted on: 19-Dec-2006 02:56:29   

One solution to the issue above is as follows:

    public class NoActionException: ApplicationException
    {
        public NoActionException() { }
    }
        protected override void OnValidateEntityBeforeSave()
        {
            base.OnValidateEntityBeforeSave();
            bool valid = true;

            // Currency.Code Validations
            if (Code.Length == 2)
            {
                SetEntityFieldError("Code", string.Empty, false);
            }
            else
            {
                SetEntityFieldError("Code", "Code must be two characters in length", false);
                valid = false;
            }

            // Currency.Description Validations

            if (Description.Length == 5)
            {
                SetEntityFieldError("Description", string.Empty, false);
            }
            else
            {
                SetEntityFieldError("Description", "Description must be five characters in length", false);
                valid = false;
            }

            if (!valid)
            {
                throw new Lv.Exceptions.NoActionException(); 
            }
        }
        protected internal override void ultraGrid1_AfterRowUpdate(object sender, RowEventArgs e)
        {
            // Update database - add new record or modify existing record
            base.ultraGrid1_AfterRowUpdate(sender, e);
            try
            {
                ((EntityClasses.CurrencyEntity)(ultraGrid1BS.Current)).Save();
            }
            catch (NoActionException) 
            {
                return;
            }
            catch (Exception ex) 
            {
                MessageBox.Show(Lv.Utility.FormatErrorString(ex), "LLBLGenPro Error", MessageBoxButtons.OK, MessageBoxIcon.Error);
                return;
            }
       }
Max avatar
Max
User
Posts: 221
Joined: 14-Jul-2006
# Posted on: 19-Dec-2006 11:55:10   

tedh wrote:

Hi Max,

Thanks for the suggestions.

I already have set UltraGrid.DisplayLayout.Override.SupportDataErrorInfo = SupportDataErrorInfo.RowsAndCells because this is the only way to get IDataErrorInfo errors to display in the grid. The error message part is working fine. My problem is that LLBLGen rejects the invalid data value and resets the cell to its original value whereas I want the cell to display the error message plus the invalid data value.

I would be very interested to see a code example of how you have managed to overcome this behaviour.

this is the code I'm using simple_smile

'relevant grid property
mUGrid.UpdateMode = UpdateMode.OnRowChangeOrLostFocus
mUGrid.DisplayLayout.Override.CellClickAction = CellClickAction.Edit
mUGrid.DisplayLayout.Override.SupportDataErrorInfo = SupportDataErrorInfo.RowsAndCells

'relevant grid event handler

'handle the editing for the error generated by the entityValidator
Protected Overridable Sub Ug_CellDataError(ByVal sender As UltraGrid, ByVal e As Infragistics.Win.UltraWinGrid.CellDataErrorEventArgs)
    Try
        e.StayInEditMode = True   'this tell to the grid to stay in edit-mode (unless it will reset the old value)
        e.RaiseErrorEvent = True  'this tell to the grid to generate the [Error] event
    Catch ex As Exception
        Throw
    End Try
End Sub
'handle the error message generated by EntityValidator or directly by ultragridd
Protected Overridable Sub Ug_Error(ByVal sender As UltraGrid, ByVal e As Infragistics.Win.UltraWinGrid.ErrorEventArgs)
    Try
        If e.DataErrorInfo IsNot Nothing Then

            If e.DataErrorInfo.Cell IsNot Nothing Then

                'check if it's an LLBLGen Entity
                If e.DataErrorInfo.Cell.Row.ListObject.GetType.IsSubclassOf(GetType(EntityBase2)) Then

                    'tell to the grid to don't show the error message box
                    e.Cancel = True


                    'check if this column/fields has an error in the underlyng entity
                    If DirectCast(e.DataErrorInfo.Cell.Row.ListObject, IDataErrorInfo).Item(e.DataErrorInfo.Cell.Column.Key) = "" Then
                        'the underlyng entity don't have any error, so I get (and set) the error message from the error generated by the grid
                        'this can happen, for example, when you try to set a dateTimePicker to 31/02/2000, that can't exist
                        'The grid handle directly these type of error, without calling entity validation code
                        DirectCast(e.DataErrorInfo.Cell.Row.ListObject, EntityBase2).SetEntityFieldError(e.DataErrorInfo.Cell.Column.Key, e.ErrorText, False)
                    End If

                End If
            End If
        End If

    Catch ex As Exception
        Throw
    End Try
End Sub
'entityValidator

Public Overrides Function ValidateFieldValue(ByVal involvedEntity As IEntityCore, ByVal fieldIndex As Integer, ByVal value As Object) As Boolean
    Dim ret As Boolean
    
    ret = True

    Select Case CType(fieldIndex, DAL.PersonaFieldIndex)


        Case PersonaFieldIndex.CodiceFiscale
            Dim sLen As Integer
            sLen = DirectCast(value, String).Length

            If sLen <> 16 Then
                ret = False
                involvedEntity.SetEntityFieldError(DirectCast(involvedEntity, EntityBase2).Fields(fieldIndex).Name, "CodiceFiscale is wrong, it's length must be 16 chars!", False)

                Throw New Exception("CodiceFiscale is wrong")
            End If


        Case Else
    End Select


    Return ret
End Function

In my validator, when I have an error, I don't return false, instead I throw an exception. Maybe that this is the difference... the ultragrud catch this exception, and generate the CellDataError event.

tedh
User
Posts: 34
Joined: 14-Dec-2006
# Posted on: 19-Dec-2006 21:52:16   

Hi Max,

Thanks for posting the code. Very much appreciated. Nice solution and I learned a lot by reading it.

tedh
User
Posts: 34
Joined: 14-Dec-2006
# Posted on: 19-Dec-2006 22:33:14   

Hi Max,

Just one more question. In your solution, if an invalid value is keyed into a field then validation fails -> an exception is raised preventing the entity from updating -> the cell shows the invalid data plus the error symbol and the cell is set to StayInEditMode. I suspect that this means that the user cannot leave the cell before fixing the error.

Is it possible to not set StayInEditMode so that the cell shows the error symbol and invalid data but the user is not fixed to the cell in error?

The intent here is to let the user enter other data and to address the error later. If this is possible there would also need to be validation at the entity level to cover the scenario that the user does not come back to fix the error and ValidateFieldValue does not execute a second time.

Max avatar
Max
User
Posts: 221
Joined: 14-Jul-2006
# Posted on: 20-Dec-2006 15:22:10   

tedh wrote:

Hi Max,

Just one more question. In your solution, if an invalid value is keyed into a field then validation fails -> an exception is raised preventing the entity from updating -> the cell shows the invalid data plus the error symbol and the cell is set to StayInEditMode. I suspect that this means that the user cannot leave the cell before fixing the error.

Is it possible to not set StayInEditMode so that the cell shows the error symbol and invalid data but the user is not fixed to the cell in error?

If you set StayInEditMode = False, the error message/symbol will show up, and the user can go to another cell; but the invalid cell value will be reverted to it's previous value. This is normal, because if the entity validator don't accept a value, this value will not be stored anywhere, and so il the ultragrid can't store the invalid value in the datasource, it will show the old (valid) value.

tedh wrote:

The intent here is to let the user enter other data and to address the error later. If this is possible there would also need to be validation at the entity level to cover the scenario that the user does not come back to fix the error and ValidateFieldValue does not execute a second time.

I think you ca do that: *) in the entityValidator.ValidateField you can set the error, and return true; so ultragrid can store the value in the datasource, anc can also show the error (I believe that, but I've not tried)

*) in the entityValidator.ValidateBeforeSave you can check the data, and throw an exception if the user hasn't corrected all the data.