Infragistics Wingrid ICancelAddNew - unused new row not cancellingInfragistics Wingrid ICancelAddNew - unused new row not cancelling
Old forum URL: forums.lhotka.net/forums/t/6860.aspx
Tom_W posted on Tuesday, April 28, 2009
Hi All,
first poster I'm afraid! I have spent the last month or so going up the CSLA curve, and I'm loving the framework, it is an awesome achievement
.
I've been working up a simple winforms test app of the standard contact/order header/order line ilk and have hit a problem with the infragistics wingrid, which I'm hoping someone may be able to help with. The scenario is as follows:
- I have an order header edit form which is bound to a Root OrderHeader class via a BindingSource.
- The OrderHeader class has an editable child collection called OrderLines, which unsuprisingly contains OrderLine objects.
- Initially I dragged the OrderLines collection property from the OrderHeader class (DataSource) onto the form, which gave me a DataGridView showing an editable table of the OrderLine objects for this OrderHeader (and a corresponding OrderLinesBindingsource).
- In the OrderLines collection class I overrode AddNewCore and added a line to say AllowNew = True, which now means I can happily add new OrderLines into the blank row at the bottom of the DataGridView
- If I click into a new row then click out of it or press Esc then the new row is cancelled (i.e. ICancelAddNew works as expected).
The above all saves/updates/deletes correctly and is generally great.
However, when I switch to using an Infragistics WinGrid instead of the Windows DataGridView, the new row adding no longer works as expected:
- If I click into a new row on the WinGrid the row is added and the default values from an OrderLine are populated as expected.
- But if I then press Escape or simply click out of the row (without having modified any data manually) the new row is not cancelled/deleted in the same way that it is with the Windows DataGridView
I have had this same problem with Infragistics grids on a framework we wrote and never found a solution, so I'm guessing this maybe an Infragistics issue, but, what I'm hoping is that someone here could tell me:
- Has anyone else experienced this?
- Is this expected behaviour for infragistics wingrids?
- Is there a workaround?
Many thanks in advance
Tom
Versions:
- OS: Windows XP
- .NET: 3.5 SP1
- VS: 2008
- CSLA: 3.6.2
- Infragistics: V8.2
RockfordLhotka replied on Tuesday, April 28, 2009
You might try a small sample of your scenario using the standard Microsoft DataGrid control to help establish whether it is Infragistics, data binding, your object or CSLA that is causing the problem.Tom_W replied on Tuesday, April 28, 2009
Hi Rocky
Thanks for your thoughts, I started with the vanilla Windows DataGridView and it worked absolutely fine (details of how I added it etc are in the original post). No doubt it is either my crappy code or the Infragistics control - I've had this problem with custom business object binding before with these controls and never solved it.
What would be great is to know if anyone has made the Infragistics WinGrid cancel newly added rows successfully and if so what they did to make it play nicely?
Cheers,
Tom
Tom_W replied on Wednesday, April 29, 2009
Hi Rocky
I posted a version of my original question on the Infragistics forums too. Mike Saltzman raised the following thoughts (
http://news.infragistics.com/forums/t/24944.aspx):
ICancelAddNew is a relatively new interface. Before this interface
existed, the proper way to cancel an AddNewRow was to use the
IEditableObject interface.
In order to maintain backward compatbility, the grid checks for the
IEditableObject interface first and if this interface is implemented,
then it calls the CancelEdit method on it. The grid then has to assume
that the CancelEdit method did what it is supposed to do and cancelled
the addnew row. It therefore cannot go ahead and also call into the
ICancelAddNew interface, also.
I have no experience with the CSLA object, but my guess (and this is
only a guess) is that the CSLA objects in this case are implementing
IEditableObject, but not handling the CancelEdit method properly.
Mike Saltzman
R&D Engineer
Infragistics, Inc.
I've just reread the part of Expert VB 2008 BOs that deals with IEditableObject (p279) and it seems like this whole area has been given a heck of a lot of thought and attention in CSLA, so I'm at a bit of a loss. Does any of what Mike's saying here sound like it could be the cause in your opinion?
Thanks
Tom
RockfordLhotka replied on Wednesday, April 29, 2009
I suppose it is possible that there’s a bug in the IEO
implementation.
I’m a little surprised that they give preference to the
old style, not the new style.
Back in .NET 1.x the way this worked was that IEO.CancelEdit was
responsible for removing the item from the parent list. That code is still in
CSLA, but I bet it has been untested and unused for the past several years –
so maybe it is broken. It worked in 2001-2004, but past that I can’t say.
The thing is, any well behaved child object will implement IEO,
even when contained in a modern list (like BindingList<T>) that
implements ICancelAddNew – that came in with .NET 2.0. I would think most
IEO implementations these days probably don’t do the cancel thing, since
it hasn’t been relevant since 2005.
Regardless, if this is what they do, then it is what they do.
You can look at Csla.Core.BusinessBase to see how the
CancelEdit() implementation checks the edit level added value to determine
whether to call into the parent collection to tell it to remove the child. You
can put a breakpoint there to see if that code is reached or if there’s a
problem. That might help figure out what’s going on.
Rocky
Tom_W replied on Wednesday, April 29, 2009
Thanks Rocky.
I don't really understand quite what Mike is saying. If I have it right then ICancelAddNew works at the collection level so it only supercedes the part of IEO's functionality that relates to cancelling unused new rows? IEO would still be responsible for Begin/Cancel/End Edit for an object when it is not being considered as part of a collection?
If I have that right then any custom BO is going to be implementing IEO so by Mike's logic of "if it has IEO then we never use ICancelAddNew " the ICancelAddNew methods would never be run.. that doesn't sound right to me
I took your advice of debugging through BusinessBase and found where the problem lies (I'm using the RolesEdit.vb form with the DGV swapped for an Infragistics WinGrid). Recreating my steps:
1) Opening the form causes:
IEditableObject.BeginEdit to fire three times, which in turn causes BindingBase.BeginEdit to fire once.
EditLevel raised from 0 to 1 (presumably the first row in the list getting focus?)
2) Clicking on a different
existing row (without editing the first row) causes:
IEO.EndEdit to fire once and BB.ApplyEdit to fire once (presumably the first row losing focus)
Then IEO.BeginEdit is called twice, first time causes BB.BeginEdit to fire.
3) Editing the text in that row and then pressing tab causes:
IEO.BeginEdit to fire twice, but BB.BeginEdit is not fired (as expected as the if statement is guarding for these superfluous BeginEdit calls from the UI?)
4) Clicking back to the first row again causes:
IEO.EndEdit to fire twice, first time calling BB.Applyedit
Then IEO.BeginEdit is called twice, first time causes BB.BeginEdit to fire.
5) Now, clicking on a
new row causes:
IEO.EndEdit to fire twice, first time calling BB.Applyedit
Then IEO.BeginEdit is called twice, first time causes BB.BeginEdit to fire.
6) Typing some text in the field then immediately hitting Escape causes:
IEO.CancelEdit to fire, then BB.CancelEdit to fire and then BINGO, the cause of the problem:
The next line of code is:
if (IsNew && _neverCommitted && EditLevel <= EditLevelAdded)
IsNew is True, _neverCommitted is true, but EditLevel = 1 where as EditLevelAdded = 0, meaning the last check fails and so the next line of code...
if (Parent != null)
Parent.RemoveChild(this);
... never fires, which is what causes the row to not get deleted. If I force execution onto that line then the new row is deleted as per standard DGV behaviour.
I think EditLevelAdded relates to the n-undo, rather than being a part of one of the Windows binding interfaces? I'm not sure how the EditLevelAdded value gets raised, but it doesn't seem to be getting raised when BB.EditLevel is raised? Should it be?
I have a bit of an understanding of the binding interfaces having implemented myself on a little framework we did in house, but I'm a bit lost now EditlevelAdded is in the mix. Could you point me in the right direction?
Many Thanks
Tom
RockfordLhotka replied on Thursday, April 30, 2009
EditLevelAdded is the edit level of the collection at the time the child was added to the list.
It acts as the base level - when the edit level reaches that point in a cancel, it means you've canceled the child back to the point where it started, and so if the child is new then it can be removed - because it has been canceled back to the point where it didn't exist to start with.
Since you never called BeginEdit() on the collection, the collection's edit level should be 0, and so it sounds like EditLevelAdded is actually correct. What is odd is that EditLevel should be at 1, and the cancel should be lowering it to 0.
It is quite possible that changes over the past couple years have rearranged the order of events so the EditLevel value isn't decremented until after the check you are seeing - which is a long-winded way of saying there's probably a bug.
Tom_W replied on Thursday, April 30, 2009
Thanks Rocky, that sounds logical. I've stepped through it all line by line and can confirm that there doesn't appear to be any code that reduces the EditLevel when the CancelEdit occurs.
The order of events when I clicked off the automatically added newly created row without having made any changes was:
- IEO.CancelEdit was called, then BB.CancelEdit called
- BB.CancelEdit calls Undo Changes (EditLevel is 1 at this point)
- In UndoableBase.UndoChanges calls UndoingChanges (which just calls protected virtual void UndoingChanges(){})
- Then the code checks this.EditLevel - 1 != parentEditLevel, which it is (EditLevel = 1, parentEditLevel = 0). It checks the editlevel, but doesn't change it.
- The code then restores all the values back from the stack
- UndoChangesComplete() is called which starts to re-setup the validation and business rules and then hits the OnUnknownPropertyChanged() method call. This calls the OnPropertyChanged code which then causes IEO BeginEdit to fire again - this in turn causes BusinessListBase.Child_PropertyChanged to be called.
- BLB.UndoChangesComplete() then calls base.UndoChangesComplete and exits and then UndoableBase.UndoChanges completes and exits
- BusinessBase.CancelEdit then completes and execution returns to IEO.CancelEdit
So, it would appear that although the cancel edit occurs and the object's data is wound back, the EditLevel for that object is never reduced. Should that occur as part of the above chain of events, maybe when the values are restored back from the stack (stage 5 in the above list)?
If it did, then the EditLevel <= EditLevelAdded condition in the next line of code would return true (0<=0) and the child would be removed.
So, all of the above is aimed at hopefully identifying where the issue occurs.. any idea what I need to do to fix it?
ajj3085 replied on Thursday, April 30, 2009
Tom_W: Thanks Rocky.
I don't really understand quite what Mike is saying. If I have it right then ICancelAddNew works at the collection level so it only supercedes the part of IEO's functionality that relates to cancelling unused new rows? IEO would still be responsible for Begin/Cancel/End Edit for an object when it is not being considered as part of a collection?
If I have that right then any custom BO is going to be implementing IEO so by Mike's logic of "if it has IEO then we never use ICancelAddNew " the ICancelAddNew methods would never be run.. that doesn't sound right to me
That doesn't sound right to me either.. and given that the MS grid seems to work fine, maybe you can point that out and say "your implementation is out of skew with the MS equivolent implementation" to get them to acknowledge they need to fix their grid. Good luck though.. they seem to believe that THEIR 3rd party controls are just fine, but anyone elses that are involved in the mix.. oh well it must be that other third party not working right.
Seem if you can't come up with a demo that illustrates the problem without Csla in the mix.. where their grid fails and the MS one works. That will almost certainly get them to fix their grid.
Tom_W replied on Thursday, April 30, 2009
Thanks Andy. To be fair to Infragistics the specific issue does seem to be a CSLA issue, but this whole if IEO exists dont use ICAN thing is a bit confusing to say the least (well to me anyway!).
I've posted a response on the Infragistics forum based on what I've learnt so far and asking for some clarification from them on exactly how it all works. There is a link further up to that thread, so for anyone who has an interest now or in the future that's a good reference too.
It's weird, the number of CSLA users out there and the number of Infragistics users out there I would have thought this would have come up sooner!
ajj3085 replied on Thursday, April 30, 2009
Tom, I hear what you're saying, and there could be a bug in Csla too (and it sounds like there is). But it also sounds like the bug shouldn't matter, if ICAN is the interface to use if present. Again, I would defer to what the MS Grid does. If the MS Grid + Csla behave as expected, then Infragistics should use similar behavior.
I think this actually does affect me, but I've never looked into why. Instead I've told users to highlight the "new" row and hit delete.. but it only happesn on certain grids. Tom_W replied on Thursday, April 30, 2009
Yep, I agree completely, my expectation would be for WinGrid to effectively behave exactly like the standard MS DataGridView as the default. Once I get a bit of clarification from Infragistics on how the control works I will be pressing for an explanation if it isn't the same as the DGV.
Yep, your symptoms sound exactly the same as mine and I've had to tell our users the same thing (in this case for a non CSLA app. using a framework I built). It is hardly an ideal solution considering users already understand the concept of automatically cancelling new rows from MS Access and so on. I've found the Infragistics grids work perfectly with DataSets but are flakier with custom collections.
ajj3085 replied on Thursday, April 30, 2009
Hmm... so this was the issue all along. Well, good thing for me my users were using another program, not Access, so they didn't expect the Access-like behavior to begin with. I found this in the documentation for ICancelAddNew.. I'm not sure I'm reading it correctly, but it sounds like you should use the "newer" method if it's there.. but unfortunatly isn't really all that clear. What do you think?
.NET Framework Class Library
ICancelAddNew Interface
"There are two models that allow transactional addition of an item to a data-bound collection:
The older model relies directly on a collection that implements the IBindingList interface and is data-bound directly using the System.Windows.Forms..::.CurrencyManager class. The AddNew, EndCurrentEdit, and CancelCurrentEdit methods of the System.Windows.Forms..::.CurrencyManager class are responsible for transactional support for adding new items. However, this functionality depends upon the items supporting transactional behavior through the IEditableObject interface. If the items do not support this interface, the item will always be added to the list, regardless of subsequent calls to CancelCurrentEdit.
The newer model supports a more robust data-binding scenario through the generic BindingList)>) class, which implements the IBindingList and ICancelAddNew interfaces. In this case, the transactional support is managed by the BindingList)>) collection directly.
"Tom_W replied on Thursday, April 30, 2009
ajj3085: Hmm... so this was the issue all along. Well, good thing for me my users were using another program, not Access, so they didn't expect the Access-like behavior to begin with. I found this in the documentation for ICancelAddNew.. I'm not sure I'm reading it correctly, but it sounds like you should use the "newer" method if it's there.. but unfortunatly isn't really all that clear. What do you think?
.NET Framework Class Library
ICancelAddNew Interface
"There are two models that allow transactional addition of an item to a data-bound collection:
The older model relies directly on a collection that implements the IBindingList interface and is data-bound directly using the System.Windows.Forms..::.CurrencyManager class. The AddNew, EndCurrentEdit, and CancelCurrentEdit methods of the System.Windows.Forms..::.CurrencyManager class are responsible for transactional support for adding new items. However, this functionality depends upon the items supporting transactional behavior through the IEditableObject interface. If the items do not support this interface, the item will always be added to the list, regardless of subsequent calls to CancelCurrentEdit.
The newer model supports a more robust data-binding scenario through the generic BindingList<(Of <(T>)>) class, which implements the IBindingList and ICancelAddNew interfaces. In this case, the transactional support is managed by the BindingList<(Of <(T>)>) collection directly.
"
Hi Andy, that's great and I agree it sounds like the bits of IEO in question are effectively deprecated.
The demo suggestion is a good one, once they fill me in on exactly what the WinGrid is up to then I will try and put a non CSLA semo together that demonstrates the ICAN not being called. However, this still needs Infragistics to view that as being an issue of course, they may just take the view that as they are implementing IEO then there isn't a problem.
Thanks
Tom
RockfordLhotka replied on Thursday, April 30, 2009
I agree Andy, it seems like they should honor ICAN first, and fall back to
IEO if necessary. That appears to be the behavior of the standard Microsoft
data grid control, against which I measure CSLA's overall requirements.
But it does appear that there's a bug in CSLA that would make it not work
with the old .NET 1.x data grid. And honestly I can't say I'd care, except
it sounds like a more widely used grid control is unfortunately following
the old 1.x model.
I've added an item in bug tracker for this, but I don't know when I'll have
time to dig into it. The trick is that I'll have to either get a failure
case on the 1.x data grid (is that even still around?) or work with
Infragistics to get a license for their stuff (which I'm sure I can do, but
not in the next couple weeks).
The EditLevel DOES get decremented by the way - but it happens later in the
process, and in UndoableBase (if I remember correctly). That whole area
underwent a lot of change a couple years ago as we (this community) helped
me fight through all the data binding issues that are covered in the 'Using
CSLA .NET 3.0' ebook. To get the WinForms 2.0 data binding to really work,
the code dealing with EditLevel changed a lot - and it seems very likely
that I accidentally broke the old 1.x behavior.
And that isn't entirely surprising - I didn't test the 1.x behaviors,
because I kind of thought they were so far in the past no one would care :)
Rocky
-----Original Message-----
From: ajj3085 [mailto:cslanet@lhotka.net]
Sent: Thursday, April 30, 2009 9:30 AM
To: rocky@lhotka.net
Subject: Re: [CSLA .NET] RE: Infragistics Wingrid ICancelAddNew - unused new
row not cancelling
Tom, I hear what you're saying, and there could be a bug in Csla too (and it
sounds like there is). But it also sounds like the bug shouldn't matter, if
ICAN is the interface to use if present. Again, I would defer to what the
MS Grid does. If the MS Grid + Csla behave as expected, then Infragistics
should use similar behavior.
I think this actually does affect me, but I've never looked into why.
Instead I've told users to highlight the "new" row and hit delete.. but it
only happesn on certain grids.
ajj3085 replied on Thursday, April 30, 2009
Yup.. I agree.
My suggestion to prod them I think is the ideal way to go. Build a demo without Csla in the mix, file a bug report with them, and I'm reasonably sure they will agree it's a bug they need to fix. If more than one person is reporting the same issue that might help as well.
That's been my experience anyway.. the initial hurdle is in getting them a demo that clearly illustrates the problem, but after that they are pretty good about fixing it, even if they are a bit slow at it. That's the only gotcha, is that you may be able to address Csla faster than they get their fix. :-)
I have some downtime, so I think I may work on my own demo as well, because it sounds like it does affect me.Tom_W replied on Thursday, April 30, 2009
Thanks again Rocky, that all makes complete sense.
I guess Infragistics are stuck in the situation where they need to support the legacy code out there so it may be difficult for them to recode the control. I do have the source code license so if I get a moment I will have a dig and try and work out what it's all doing.
The simplest solution would seem to be for Infragistics to give the developer the choice about whether the control uses ICAN or IEO to control the new list items, but that is probably a lot easier said than done!
I'm stil waiting for Infragistics to clarify exactly how they handle the overlap between ICAN and IEO, the devil is probably in the detail...
Cheers, Tom
Tom_W replied on Thursday, April 30, 2009
One more quick question, just to check my understanding:
Am I correct in saying that:
- IEO is still the current interface for providing binding for a single instance of a business object
- that ICAN is only concerned with dumping unused items from the collection.
- ICAN only overlaps IEO with regards to removing new items from the collection, with ICAN being the newer and recommended approach.
- I.e. in short, any well behaved BO would always implement IEO?
Apologies, I know this isn't .NET databinding class 101....!
RockfordLhotka replied on Thursday, April 30, 2009
That sounds about right to me.
Rocky
From: Tom_W
[mailto:cslanet@lhotka.net]
Sent: Thursday, April 30, 2009 2:56 PM
To: rocky@lhotka.net
Subject: Re: [CSLA .NET] RE: RE: Infragistics Wingrid ICancelAddNew -
unused new row not cancelling
One more quick question, just to check my understanding:
Am I correct in saying that:
- IEO is still the current interface for providing
binding for a single instance of a business object
- that ICAN is only concerned with dumping unused
items from the collection.
- ICAN only overlaps IEO with regards to removing
new items from the collection, with ICAN being the newer and recommended
approach.
- I.e. in short, any well behaved BO would always
implement IEO?
Apologies, I know this isn't .NET databinding class 101....!
Tom_W replied on Tuesday, May 05, 2009
Update on all of this:
I've discussed all of the above with Infragistics
http://news.infragistics.com/forums/p/24944/92456.aspx#92456 and the upshot is that they concur that their needs to be an option to allow ICAN to fire first ahead of IEO.
They are going to look at either getting this into the next 9.1 Service Release or into Version 9.2 of the controls
Hopefully that will happen and will work as expected, it certainly seems like a better solution for the WinGrid to implement ICAN too, rather than just IEO.
Thanks to everyone for your thoughts and inputs/explanations.
Tom_W replied on Friday, May 29, 2009
Further update on this; Infragistics have now confirmed that the developer will be given the option to use ICAN or IEO as of Release 2009 v2 (i.e. the next version).
RockfordLhotka replied on Friday, May 29, 2009
That's good news. Those guys at Infragistics are pretty responsive on the whole I think.Tom_W replied on Tuesday, September 15, 2009
Just to let anyone else who is interested know; Infragistics Winforms 9.2 is now out and fixes the ICAN/IEO issue described in this thread. Escaping a new row clears it correctly and moving focus from a new (but unedited) row causes it to be deleted.
Details of the new feature are
hereThe code to add to the constructor of the form is:
UltraGrid1.DisplayLayout.Override.AddRowEditNotificationInterface = AddRowEditNotificationInterface.ICancelAddNewAndIEditableObject
Copyright (c) Marimer LLC