I'm having a problem with my CLSA collections. This has been mentioned by serveral of you but, as yet, I still have no satisfactory solution so any help would be GREATLY appreciated....
First, to make my explanation a little simple consider the following object model: 'Person' object may have children which would be a
collection of 'Person' objects..
::(People)Group
-> (Person) Jack
-:: (People) Jack's Children
-> (Person) Jack's only child: Jack Junior
-> (Person) Jill
-:: (People) Jill's Children
-> (Person) Jill's 1st Child: Jill Junior
-:: (People) Jill Junior's Children
-> (Person) Jill Junior's 1st Child
-> (Person) Jill's 2nd Child
-> (Person) Jill's 3rd Child
Now, what I simply want to do is bind this object model to a grid (I use Infragistics but despite finger point in their direction I don't believe the problem lies with them). However, when I do bind to a grid the various People collections get sent numerous IBindingList.AddNew() events. My understanding is that this is CurrencyManager/BindingContext behaviour to find out the schema of an object. Trouble is that these collection may be 1. read only or 2. have very complex NewObject behaviour that either results in exceptions being raised or performance absolutely dying.
Now, I have read of the IsBindingNow()/IsFinishedBinding() approach but while this works for the first call to SetDataBinding() it does not solve my problem as the tree is navigated and these IBindingList.AddNew() events trigger as + buttons are pressed.
NOW, why am I posting here!? This is where I'm going to be devils advocate and suggest that the problem must lie in the way collection are implemented in CSLA. See, when I try this stuff with the DataSet/Relationship approach I do no get any of these issues. This leads me, obviously, to conclude that the DataView is a lot more clever when it comes to databinding!???? So what does it do that CSLA collection fail to do!?
I have tried implementing the ICustomTypeDescriptor and ITypedList interfaces on the objects/collections but still that damn AddNew() gets called in the background.
I know I'm not alone in dealing with this so please spare a minute to tell me what you've done/are doing to get around this problem.
And, just to note, my object hierarchies/graphs are pretty complex - the People/Person is purely for illustration.
Thanks as always.
jh
The key in 1.x or 2.0 is to realize that if you turn on AllowNew in your collection, then you MUST be able to easily and cheaply create a new child object on demand. You mention that the problem doesn't exist with a dataset - which of course it wouldn't, because a datatable is always able to easily and cheaply create a new datarow on demand.
The problem isn't with the way collections are implemented in CSLA as such, but rather with the base assumption of complex data binding (binding with grids and grid-like controls), that when AllowNew returns true that the grid can feel free to create a new row as it sees fit. YOUR object model must conform to this restriction if you set AllowNew to true - that's just a fact of life.
If you can't make your child objects create quickly or cheaply, then you can't set AllowNew to true, and you'll need to have another UI metaphor to allow the user to add new items to your collection.
Again, thanks for the replies. I read your entry with interest Rocky and my one question was....answered in a way by you Dawn - the fact that my problem is almost always with collections whose AllowNew is set to false. These ones raise the exception from the Core... which cause all manner of problems when binding to Infragistic controls.
Implementing ICustomType.../etc. theoretically means that the object doesn't actually need to be created by BindingManager/whatever for it to figure out its schema but, of course, this doesn't work consistently.
I'm not at v2.0 yet so am stuck with a very big problem in the UI layer - any suggestions (however whacky) on how I might workaround this problem given that the business layer is complex and, more importantly, complete!?
Maybe someone would be interested enough to do a Lutz Roeder's .NET Reflector on the new BindingSource component and tell me what it is doing under the hood to circumvent these AddNew problems!?
Thanks again.
Thanks for that Dawn. Suggestion there is that the problem lies in the BindingContext and not the grid code. I'll keep trying to find a solution but I'm not optimistic. Thanks again for your help.
Hi all.
It appears that I'm still having this same problem even though I am using a bindingSource in the middle and AllowNew is set to false on both the collection and the bindingSource. Anybody have a solution to this problem? I'm starting to think that it's Infragistics related and not so much .Net or CSLA related.
Dustin
Folks,
This goes back to an old problem discussed at http://forums.lhotka.net/forums/thread/2054.aspx.
What I NEED to figure out is why I can bind my empty collection to a BindingSource and later bind that to a grid and all will work while I get AddNew problems when binding directly to the collection.
i.e. can any of you gurus out there tell me what the BindingSource does that navigates around the problem of AddNew being called to discover the schema of objects in a collection.
See, using BindingSource in the UI of all our apps seems a huge amount of work and I was hoping that I could do something at the base collection classes to sort the problem out (implementing ITypedList, etc. - that kind of thing).
Thanks
I doubt the DataSet works any differently. When you bind an empty DataTable to a bindingSource/grid it immediately adds a new row to the DataTable for display in the grid. This occurs through a call to AddNewCore() in the DataTable. You just don't have a lot of heavy initialization code in that AddNewCore() - though you could if you use the partial class support added in .NET 2.0.
I also doubt that it is creating a new item just to discover the schema. In .NET 2.0, using the generic collection base classes to create a collection means that the indexer (Item property in VB) is strongly typed. The normal behavior for data binding is to use reflection against a strongly typed indexer if it exists, and to only fall back to looking at items in the collection as a last resort.
At no point have I seen data binding create a new item in a list to get type information. Instead of doing that, it simply gives up and binds the grid to type Object (meaning no columns).
Thanks for that reply Rocky - I'm one of your original tormentors and will soon be looking to move our apps to be more inline with your newer versions and approaches - expect more torment :)
It is confusing because the stack trace from binding a collection to a grid is this (from bottom up below) and clearly shows a path to AddNew from a simple data binding operation:
CSLA.Core.BindableBase.dll!CSLA.Core.BindableCollectionBase.System.ComponentModel.IBindingList.AddNew()
System.Windows.Forms.dll!System.Windows.Forms...
.CurrencyManager.AddNew()
.RelatedCurrencyManager.ParentManager_CurrentItemChanged(object sender, System.EventArgs e)
.RelatedCurrencyManager.Bind(System.Windows.Forms.BindingManagerBase parentManager, string dataField)
.BindingContext.EnsureListManager(object dataSource, string dataMember)
.BindingContext.this[object, string].get(object dataSource, string dataMember)
Infragistics.Win.v7.1.dll!Infragistics.Win.DataBindingUtils.GetCurrencyManager(System.Windows.Forms.BindingContext bindingContext, object dataSource, string dataMember)
Infragistics.Win.UltraWinGrid.v7.1.dll!Infragistics.Win.UltraWinGrid...
.UltraGridBase.GetCurrencyManager(string dataMember)
.UltraGridColumn.CreateChildBandBindingManagerHelper(out string newDataMember)
.UltraGridColumn.InitPropertyDescriptor(System.ComponentModel.PropertyDescriptor propertyDescriptor, Infragistics.Win.UltraWinGrid.UltraGridBand[] oldBands = null)
.UltraGridBand.InitColumns(Infragistics.Win.UltraWinGrid.UltraGridBand[] oldBands = null)
.UltraGridBand.InitListManager(System.Windows.Forms.BindingManagerBase bindingManager, string dataMember, Infragistics.Win.UltraWinGrid.UltraGridBand[] oldBands)
.UltraGridLayout.ListManagerUpdated(System.Windows.Forms.BindingManagerBase bindingManager)
.UltraGridLayout.ListManagerUpdated()
.UltraGridBase.Set_ListManager(object newDataSource, string newDataMember)
.UltraGridBase.SetDataBindingHelper(object dataSource, string dataMember, bool hideNewColumns = true, bool hideNewBands = true)
.UltraGridBase.SetDataBinding(object dataSource, string dataMember, bool hideNewColumns, bool hideNewBands)
Just now I'm suspecting that maybe the fact that BusinessCollection is a property of a base UI form that is only populated with an actual business collection at run-time may be a contributing factor. This mechanism allows us to write particularly lightweight UIs by using the patterns of the business objects at base.
BaseUI....
public virtual BusinessCollectionBaseEx BusinessCollection
Example Use....
this.BusinessCollection = Customers.GetCollection(AllowActions.ReadOnly | AllowActions.IsRealtimeSubscriber)
...
this.grdData.SetDataBinding(this.BusinessCollection,"", true, true);
Where Customers Inherit BusinessCollectionBaseEx...
public class Customers: BusinessCollectionBaseEx
Whereas using the BindingSource as a trial I have had to set its type expliticly to, example, typeof(Customers)
mmmm, I'm going to have to investigate that more on Monday but feel free to post any insights you may have.
Have a good weekend all.
* from System.Windows.Forms.RelatedCurrencyManager and seems to be the crux of my AddNew problem.
private void ParentManager_CurrentItemChanged(object sender, EventArgs e) { if (!this.ignoreParentCurrentItemChanged) { int listposition = base.listposition; try { base.PullData(); } catch (Exception exception) { base.OnDataError(exception); } if (this.parentManager is CurrencyManager) { CurrencyManager parentManager = (CurrencyManager) this.parentManager; if (parentManager.Count > 0) { this.SetDataSource(this.fieldInfo.GetValue(parentManager.Current)); base.listposition = (this.Count > 0) ? 0 : -1; } else { parentManager.AddNew(); **************** try { this.ignoreParentCurrentItemChanged = true; parentManager.CancelCurrentEdit(); } finally { this.ignoreParentCurrentItemChanged = false; } } } else { this.SetDataSource(this.fieldInfo.GetValue(this.parentManager.Current)); base.listposition = (this.Count > 0) ? 0 : -1; } if (listposition != base.listposition) { this.OnPositionChanged(EventArgs.Empty); } this.OnCurrentChanged(EventArgs.Empty); this.OnCurrentItemChanged(EventArgs.Empty); } }
RockfordLhotka:I also doubt that it is creating a new item just to discover the schema. In .NET 2.0, using the generic collection base classes to create a collection means that the indexer (Item property in VB) is strongly typed. The normal behavior for data binding is to use reflection against a strongly typed indexer if it exists, and to only fall back to looking at items in the collection as a last resort.
At no point have I seen data binding create a new item in a list to get type information. Instead of doing that, it simply gives up and binds the grid to type Object (meaning no columns).
Copyright (c) Marimer LLC