Datagrid field validation

Posts   
 
    
carmines
User
Posts: 15
Joined: 25-Oct-2006
# Posted on: 09-Nov-2006 22:55:28   

Can anybody help with this? I have a winforms datagrid bound to an entity collection. I have added an OnValidateFieldValue override function to the entity class. This function validates the entity fields and if invalid I use SetEntityFieldError and return false from the function. This seems to work very well and the datagrid ends up with a nice red error indicator in the appropriate cell. I have 2 problems: 1. because I return false from the validate function the value that the user typed in is lost - this means they see the old value (which was valid) and an error saying the value is invalid if I change the function to return true then the new invalid value is shown in the grid but the error indicator is not shown 2. how do I clear the error - it seems that the user would have to enter a new valid value in the cell - but what if the user wants to leave the old valid value

Just one other thing - I do not have an error provider defined on the form - does anyone know why the datagrid does not need an error provider to display errors?

Thanks John

LLBL v2.0 / selfservicing / vb / .net 1.1

Walaa avatar
Walaa
Support Team
Posts: 14954
Joined: 21-Aug-2005
# Posted on: 10-Nov-2006 07:54:44   

My 2 cents:

I think it's easier to handle the Save event and then validate all the fields that need to be validated (validate the entity to be saved), and display error messages outside the grid.

carmines
User
Posts: 15
Joined: 25-Oct-2006
# Posted on: 13-Nov-2006 22:14:23   

Thanks for your comments Walaa but the type of applications I write really do require field validation at the time of input. A typical example being an order entry screen where the user enters a product code which has to be validated as the result impacts the rest of the screen immediately (eg. displays a price). I agree with your 2 cents in that validation on save is easier my customers won't go for it. Do you have any insight to the problem I described? Thanks

Otis avatar
Otis
LLBLGen Pro Team
Posts: 39625
Joined: 17-Aug-2003
# Posted on: 14-Nov-2006 10:12:42   

carmines wrote:

Can anybody help with this? I have a winforms datagrid bound to an entity collection. I have added an OnValidateFieldValue override function to the entity class. This function validates the entity fields and if invalid I use SetEntityFieldError and return false from the function. This seems to work very well and the datagrid ends up with a nice red error indicator in the appropriate cell. I have 2 problems: 1. because I return false from the validate function the value that the user typed in is lost - this means they see the old value (which was valid) and an error saying the value is invalid if I change the function to return true then the new invalid value is shown in the grid but the error indicator is not shown

Returning true also makes the code accept the value as a value for the field and signals the grid the value is accepted, which isn't correct. I'm not really sure why you want to have the invalid value in the field, as that value is invalid, so why keep it around?

  1. how do I clear the error - it seems that the user would have to enter a new valid value in the cell - but what if the user wants to leave the old valid value

Good point. You might want to clear all errors when the focus moves to another cell? (so bind some handler to the event raised when focus is moving away from a cell. ). Clearing an error is done by setting the error to an empty string.

Just one other thing - I do not have an error provider defined on the form - does anyone know why the datagrid does not need an error provider to display errors?

I've no idea, winforms works in mysterious ways unfortunately.

Frans Bouma | Lead developer LLBLGen Pro
BertS
User
Posts: 89
Joined: 28-Jul-2005
# Posted on: 14-Nov-2006 13:33:14   

Otis wrote:

Returning true also makes the code accept the value as a value for the field and signals the grid the value is accepted, which isn't correct. I'm not really sure why you want to have the invalid value in the field, as that value is invalid, so why keep it around?

i.e. for let the user fixing a typo? When the user enters productcode '3281097' which is not valid, but s/he meaned '3281094', it is sometimes easier to correct than to retype. But ok, most times it's enough to display the wrong value in the errormessage (but then show the errors in a label outside the grid or so, and not only in the tooltip of the errorprovider).

Otis wrote:

  1. how do I clear the error - it seems that the user would have to enter a new valid value in the cell - but what if the user wants to leave the old valid value

Good point. You might want to clear all errors when the focus moves to another cell? (so bind some handler to the event raised when focus is moving away from a cell. ). Clearing an error is done by setting the error to an empty string.

That conflicts with no.1. But you can reset to the old value when the users presses ESC, which cancels 'the attempt to change/update'

Otis avatar
Otis
LLBLGen Pro Team
Posts: 39625
Joined: 17-Aug-2003
# Posted on: 14-Nov-2006 14:05:50   

BertS wrote:

Otis wrote:

Returning true also makes the code accept the value as a value for the field and signals the grid the value is accepted, which isn't correct. I'm not really sure why you want to have the invalid value in the field, as that value is invalid, so why keep it around?

i.e. for let the user fixing a typo? When the user enters productcode '3281097' which is not valid, but s/he meaned '3281094', it is sometimes easier to correct than to retype. But ok, most times it's enough to display the wrong value in the errormessage (but then show the errors in a label outside the grid or so, and not only in the tooltip of the errorprovider).

There's unfortunately no other way: the entity either can accept the value or reject it. What happens in a control displaying the value is up to the control displaying the value, the entity itself can't nor should take care of that.

Otis wrote:

  1. how do I clear the error - it seems that the user would have to enter a new valid value in the cell - but what if the user wants to leave the old valid value

Good point. You might want to clear all errors when the focus moves to another cell? (so bind some handler to the event raised when focus is moving away from a cell. ). Clearing an error is done by setting the error to an empty string.

That conflicts with no.1. But you can reset to the old value when the users presses ESC, which cancels 'the attempt to change/update'

What exactly does conflict with no.1 ?simple_smile

Frans Bouma | Lead developer LLBLGen Pro
BertS
User
Posts: 89
Joined: 28-Jul-2005
# Posted on: 14-Nov-2006 14:24:18   

Otis wrote:

BertS wrote:

Otis wrote:

Returning true also makes the code accept the value as a value for the field and signals the grid the value is accepted, which isn't correct. I'm not really sure why you want to have the invalid value in the field, as that value is invalid, so why keep it around?

i.e. for let the user fixing a typo? When the user enters productcode '3281097' which is not valid, but s/he meaned '3281094', it is sometimes easier to correct than to retype. But ok, most times it's enough to display the wrong value in the errormessage (but then show the errors in a label outside the grid or so, and not only in the tooltip of the errorprovider).

There's unfortunately no other way: the entity either can accept the value or reject it. What happens in a control displaying the value is up to the control displaying the value, the entity itself can't nor should take care of that.

Right, but you asked for a reason to keep the wrong value wink

Otis wrote:

Otis wrote:

  1. how do I clear the error - it seems that the user would have to enter a new valid value in the cell - but what if the user wants to leave the old valid value

Good point. You might want to clear all errors when the focus moves to another cell? (so bind some handler to the event raised when focus is moving away from a cell. ). Clearing an error is done by setting the error to an empty string.

That conflicts with no.1. But you can reset to the old value when the users presses ESC, which cancels 'the attempt to change/update'

What exactly does conflict with no.1 ?simple_smile

If you keep the wrong value visible, the user should (imo) not be able to leave the field which resets the old value. That's very confusing. And besides that, most-time validation is done when leaving the field (because that's the moment the control updates its datasource). (or do I talk nonsense now?)

carmines
User
Posts: 15
Joined: 25-Oct-2006
# Posted on: 14-Nov-2006 14:58:16   

Otis wrote:

Returning true also makes the code accept the value as a value for the field and signals the grid the value is accepted, which isn't correct. I'm not really sure why you want to have the invalid value in the field, as that value is invalid, so why keep it around?

I would want the following behaviour - user enters value and leaves cell - field is validated - if value is false then show red error icon with tooltip containing description of the problem and the cell to contain the invalid value that the error description relates to. The way it works now is that the cell shows an error but the value in the cell is valid. In my validation routine I use SetEntityFieldError to assign the error description to the entity field but the grid only shows an error if I also return False. Could you explain why this happens - what causes the grid to show the error? Returning false from the validation must be doing something else internally to cause the grid to not show the error.

Good point. You might want to clear all errors when the focus moves to another cell? (so bind some handler to the event raised when focus is moving away from a cell. ). Clearing an error is done by setting the error to an empty string.

This sounds like a reasonable work around which I will try out. But could you tell me when focus moves to another cell how can I tell which entity the cell is related to?

I've no idea, winforms works in mysterious ways unfortunately.

frowning I can't believe you have no idea about this as you seem to know lots about everything smile

Otis avatar
Otis
LLBLGen Pro Team
Posts: 39625
Joined: 17-Aug-2003
# Posted on: 15-Nov-2006 10:50:41   

carmines wrote:

Otis wrote:

Returning true also makes the code accept the value as a value for the field and signals the grid the value is accepted, which isn't correct. I'm not really sure why you want to have the invalid value in the field, as that value is invalid, so why keep it around?

I would want the following behaviour - user enters value and leaves cell - field is validated - if value is false then show red error icon with tooltip containing description of the problem and the cell to contain the invalid value that the error description relates to. The way it works now is that the cell shows an error but the value in the cell is valid. In my validation routine I use SetEntityFieldError to assign the error description to the entity field but the grid only shows an error if I also return False. Could you explain why this happens - what causes the grid to show the error? Returning false from the validation must be doing something else internally to cause the grid to not show the error.

the reason I think the grid rejects the error is that the set doesn't succeed, i.e.: the value isn't accepted as the value in the entity, plus the change notification event isn't raised, as the value isn't accepted.

Good point. You might want to clear all errors when the focus moves to another cell? (so bind some handler to the event raised when focus is moving away from a cell. ). Clearing an error is done by setting the error to an empty string.

This sounds like a reasonable work around which I will try out. But could you tell me when focus moves to another cell how can I tell which entity the cell is related to?

currentrow in the grid and hten the data element, cast it to the entity type.

I've no idea, winforms works in mysterious ways unfortunately.

frowning I can't believe you have no idea about this as you seem to know lots about everything smile

heh simple_smile well, with winforms, it's logical to some point and then it's simply random: sometimes this happens, in other situations something else happens. It's not predictable simple software and I really wonder why as on java for example people don't have this complexity as they use completely different patterns with layout managers and proper event delegation, deriving of controls from base controls and by adding your control using code to THAT class, instead of to the form the control is on.

Frans Bouma | Lead developer LLBLGen Pro
carmines
User
Posts: 15
Joined: 25-Oct-2006
# Posted on: 16-Nov-2006 22:44:16   

Thanks for all the help so far but I am starting to get lost with this whole topic. My projects before LLBL used data adapters, datasets and databinding. I would bind a grid to a dataset/table and would use a data adapter to fill and save the dataset. The data adapter had a nice event called RowUpdated which received an event argument that contained any errors raised during the row update. If the row could not be saved back to the database because of a duplicate PK the RowUpdated event still fired and you could test for any errors and use e.Row.RowError to set an error string on the row. The datagrid back on the form would then show the error. I have to say this seemed like sensible behaviour and I am attempting to duplicate using LLBL - but I am failing miserably.

It looks like I am going to have to do the following: 1. catch the ORMQueryExecutionException raised by the SaveMulti 2. check if the inner exception is a SqlException 3. decode the SqlException.Number 4. now determine which entity caused the exception by decoding ORMQueryExecutionException.Parameters collection 5. now somehow I would need to SetEntityError on the problem entity - I guess I would need to loop through the collection looking for the problem entity and then set the error

This all seems very long winded and makes me think I am going about it the wrong way.

Am I missing an equivalent to the data adapters RowUpdated event - eg. an entitycollection EntityUpdated event? (not the AfterSave event because that only fires if the save was successful)

Would anyone like to comment on the above and perhaps make some alternative suggestions.

Thanks

Otis avatar
Otis
LLBLGen Pro Team
Posts: 39625
Joined: 17-Aug-2003
# Posted on: 17-Nov-2006 12:33:10   

carmines wrote:

Thanks for all the help so far but I am starting to get lost with this whole topic.

I'll try to clear things up a bit below.

My projects before LLBL used data adapters, datasets and databinding. I would bind a grid to a dataset/table and would use a data adapter to fill and save the dataset.

that's the same route you'll follow with LLBLGen Pro: you fetch an entity collection, you bind it to a grid, you save the entity collection.

The data adapter had a nice event called RowUpdated which received an event argument that contained any errors raised during the row update. If the row could not be saved back to the database because of a duplicate PK the RowUpdated event still fired and you could test for any errors and use e.Row.RowError to set an error string on the row. The datagrid back on the form would then show the error. I have to say this seemed like sensible behaviour and I am attempting to duplicate using LLBL - but I am failing miserably.

The problem is that having a collection of entities in memory can give all kinds of issues when you persist the changes. These changes have to be addressed in a handler for the exception.

It looks like I am going to have to do the following: 1. catch the ORMQueryExecutionException raised by the SaveMulti 2. check if the inner exception is a SqlException 3. decode the SqlException.Number 4. now determine which entity caused the exception by decoding ORMQueryExecutionException.Parameters collection 5. now somehow I would need to SetEntityError on the problem entity - I guess I would need to loop through the collection looking for the problem entity and then set the error This all seems very long winded and makes me think I am going about it the wrong way.

That's because you combine two things into one. As soon as the gui is over and the save process starts, the gui is nowhere in sight and shouldn't be in your mind when writing the code. When you save the collection and you run into an exception, you can use a FindMatches call to find the entity matching the parameters.

The core problem is that there's no unified way to determine if a UC voilation took place on all databases, or an FK violation for example: every db uses its own exception object and its own error number or numbers.

So the code which handles the exception and which you've to write is very db specific which is a bit of a pain, but unavoidable. I'd suggest to write a generic routine which accepts the entity collection, the ORMQueryExecutionException and returns teh entity at stake. In the routine you do the sqlexception examination.

What's to do after an exception happens is very application dependend. You want an error displayed in the gui, but perhaps your save code is in a service on another server, so the error should be serialized to you (remoting) or should be handled on the server by tossing out the entity which failed and the whole transaction should be re-tried.

Now, as the exception doesn't provide you with enough tools/info, I'll see if I can update the exception in v2.1, so more info is available to you, if applicable, like entityname, PK fields and pk field values. Including the entity object is an option but that could make the exception bigger than expected when the exception is serialized over a remoting wire because serialization is recursive.

Am I missing an equivalent to the data adapters RowUpdated event - eg. an entitycollection EntityUpdated event? (not the AfterSave event because that only fires if the save was successful)

Would anyone like to comment on the above and perhaps make some alternative suggestions.

A selfservicing entity has two methods; OnSave and OnSaveComplete which are called before and after the save action. So when a save of an entity was successful, the OnSaveComplete method is called, otherwise it's not. As you're using selfservicing, the entities do their own saving.

Frans Bouma | Lead developer LLBLGen Pro
carmines
User
Posts: 15
Joined: 25-Oct-2006
# Posted on: 21-Nov-2006 23:19:14   

Thanks for the great response - again simple_smile You have clarified things for me - I did have a little problem with FindMatches which I could not find on my collections. I found another thread in the forum which said this was only available to .NET 2.0 so I took the advice there and used an entityview to extract the offending entity.

Things are coming along nicely now - in no small part to the great support in the forum from Frans and also the rest of the members.

Totally off topic - but this is the only forum I have ever participated in where everyone seems to be 'of the same mind' - professional software developers with real life problems and intelligent questions - it is very refreshing

Otis avatar
Otis
LLBLGen Pro Team
Posts: 39625
Joined: 17-Aug-2003
# Posted on: 22-Nov-2006 09:53:14   

Thanks for the great feedback! smile

FindMatches was declared protected in older builds of the runtime library. It was made public on november 1st so all builds after that date have the method public.

Frans Bouma | Lead developer LLBLGen Pro