XtraGrid not having a correct reference to Child list after calling Save.

XtraGrid not having a correct reference to Child list after calling Save.

Old forum URL: forums.lhotka.net/forums/t/2516.aspx


Plum posted on Tuesday, March 13, 2007

Hi

I am using Developer Express Xtragrid and I am experiencing a problem where the Child Business list objects do not get updated in the database after I call my save routine the second time.

 

I have a parent BusinessListBase with two ChildrenBusinessListBase.  

 

CarManufactures -> CarManufacture

      CarManufacture -> Models ->Model

      CarManufacture -> Regions -> Region

 

Everythings works well when my form pops up and the XtraGrid is loaded. Data is entered and modified and I am able to add and update Models and Regions. I then Call the SaveCarManufactureList with rebind set to try. Data is saved to the database. I can update a Carmanufacture and that will save to the database. If I update or add records to region or model, no update happens to the database. It seems like the Xtragrid is not referencing the correct Child list after the Clone process.

 

I am able to overcome this by setting the grid control datasource to null and then rebind the grid control the the binding source ( I have this code commented out.) but of all the documentation I have seen, I do not think this is the right approach. Besides when a user is updating Model or region and hits the save button, the region and Model will collapse and the Grid control will reload the data from the binding source ( annoying to the user).

 

I was wondering if anyone encountered this problem with a grid and how did they get around this.

 

I am using CSLA version 2.14

 

Thank You

 

 

     

 

  

 -- Code *********************************************************  

      public override bool isDirty()

            {

            this.gvCarManufacture.CloseEditor();

            this.gvRegion.CloseEditor();

            this.cvModel.CloseEditor();

                  return _ CarManufactureList.IsDirty;

            }

 

 

 

 private void SaveCarManufactureList(bool rebind)

            {

            if (isDirty())

            {

                this.bsCarManufactureList.RaiseListChangedEvents = false;

                this.bsCarManufactureList.EndEdit();

               

                try

                {

                    CarManufactureList temp = _CarManufactureList.Clone();

                    _CarManufactureList = temp.Save();

                    if (rebind)

                    {

                    //    this.gcCarManufactureList.DataSource = null;                     

                        this.bsCarManufactureList.DataSource = null;                     

                        this.bsCarManufactureList.DataSource = _CarManufactureList;                     

                    //   this.gcCarManufactureList.DataSource = this.bsCarManufactureList;

                                        

                    }

                }

                catch (Csla.DataPortalException ex)

                {

                    MessageBox.Show(ex.BusinessException.ToString(), "error Saving", MessageBoxButtons.OK, MessageBoxIcon.Exclamation);

                }

                finally

                {

                    this._CarManufactureList.RaiseListChangedEvents = true;

                    this.bsCarManufactureList.ResetBindings(false);

                   

                }

            }

 

            }

 

-- Code __________________

karasko replied on Sunday, August 05, 2007

Hi Plum,

I have the exact same problem. Did you found solution for this problem?

Could anyone else help me, please?

Thanks


RockfordLhotka replied on Sunday, August 05, 2007

Plum:

I am able to overcome this by setting the grid control datasource to null and then rebind the grid control the the binding source ( I have this code commented out.) but of all the documentation I have seen, I do not think this is the right approach. Besides when a user is updating Model or region and hits the save button, the region and Model will collapse and the Grid control will reload the data from the binding source ( annoying to the user).

You always need to rebind the UI after you save a CSLA business object. The result of Save() is a new object. This is not unique to any particular grid or control, it is the way Save() works.

So if you don't rebind your bindingSource to the result of Save(), your user is still editing the old object - the one that wasn't saved, doesn't have any database-assigned primary keys or timestamps for concurrency or anything.

That, by the way, is why Save() returns a new object - a lot of things can change during the Save() call...

karasko replied on Tuesday, August 07, 2007

RockfordLhotka:

You always need to rebind the UI after you save a CSLA business object. The result of Save() is a new object. This is not unique to any particular grid or control, it is the way Save() works.



This is exactly what I'm doing. I understand how the Save() works, or at least I hope that I understand.
But the problem is that after rebinding the BindingSource to the newly returned object the grid stop propagating the changes to the parent's collection.
I have found that when the values in the grid has been changed after the first Save(), the setter of the business object has been fired, but if I call the Save() method of the parent object again, it's child collection is unchanged?!
I have made some further investigation and found that the actual problem is that the BindingSource itself is perfectly correct, but XtraGrid doesn't update its child view to reflect the changes. However, if I manually collapse and expand again the master row, everything comes back in sync as expected. This is exactly what fooled me in the first place, because I have tested the similar scenario with Infragistics and their behavior is almost the same. Actually the Infragistics UltraGrid is even worst because the only way to sync the Grid's view with the actual BindingSource is to set the UltraGrid.DataSource to null and then rebind it again to the BindingSource. Collapsing and expanding the master row doesn't help at all.
This is the "solution" that Plum and I discussing here.

I hope that this time I am explaining the issue much better.

From my point of view, this is obviously a problem of DevExpress / Infragistics and is not caused by any means form the CSLA framework, but I really hope that someone with more experience with these products will suggest something helpful.

So, if anyone has any suggestions please share them with us.

Best regards
Krasimir Evtimov
http://www.linkedin.com/in/KrasimirEvtimov

RockfordLhotka replied on Tuesday, August 07, 2007

karasko:

I have found that when the values in the grid has been changed after the first Save(), the setter of the business object has been fired, but if I call the Save() method of the parent object again, it's child collection is unchanged?!

Two things comes to mind.

First, are you making sure to unbind/rebind all bindingSource objects in the hierarchy? You can't just rebind the root, you must unbind/rebind the child bindingSource objects too - look at ProjectTracker\PTWin for an example.

Second, are you rehooking any event handling in your object model? In particular, your parent/root must rehook the ListChanged of the child collection on deserialization, otherwise events quit flowing up. BLB rehooks its child objects, but BB has no way to automatically rehook events of child objects or collections, so you must do it in OnDeserialized().

karasko replied on Tuesday, August 07, 2007

RockfordLhotka:

First, are you making sure to unbind/rebind all bindingSource objects in the hierarchy? You can't just rebind the root, you must unbind/rebind the child bindingSource objects too - look at ProjectTracker\PTWin for an example.



The sad thing is that there is only one BindingSource which is assigned to XtraGrid / UltraGrid DataSource property. The grid internally get the structure of the data source and create child views, but there is no any public API to access those binding sources.

This is the biggest problem!

In your project, you've used the DataGridView which support only one level and for this reason you have separate BindingSource for all your collection. But DevExpress / Infragistics has only one Binding source for the master collection. This is the one and only reason to use this control: because it supports hierarchical data source in only one grid, very similar to the MS Access behavior.

If I had access to the child's view it would be so much easier than now..

I'm not quite sure if this problem has something to do with the events, because as I've said before, when I collapse and re-expand the master row everything comes back in sync. In my point of view this means that the problem is really with refreshing the views, but all the events are hooked already. In the DevExpress case I don't even need to set the Gris.DataSource property to null and reset it to the BindingSource again. Which leads me to conclusion that everything is OK with the BindingSource, but the XtraGrid itself doesn't refresh itself and actually refresh itself only after collapsing and expanding the master row of the child view.

I have tried to come up with some different hierarchical collection of objects which I want to bind it to the XtraGrid to eliminate any suspicions out of the CSLA, but sadly enough nothing have came to my mind..

If you have idea for such collections with some editable property it would be the best approach.
Obviously, the collection should implement the IBindingList interface.

Best regards,
Krasimir Evtimov

P.S. Thanks for your amazingly fast answers. I really appreciate them.


alef replied on Tuesday, August 07, 2007

Hello,

I have discussed this problem already with the developers of DevExpress and this issue cannot be easily resolved. They feel sorry for that. The problem lies within the binding mechanism used by the XtraGrid. To provide the ability to display several detail views at once, the grid creates separate related DataViews for each detail GridView. In this situation, when a master row is updated, the detail view doesn't receive any notification, which can be used to refresh data. The only solution I can suggest here is to manually collapse and then re-expand the master row:

[C#]

 
      gridView1.CollapseMasterRow(gridView1.FocusedRowHandle); <------- new line
      MasterDetailBL bl = new MasterDetailBL();
      bl.SaveMasterDetailDataSet((MasterDetailDataSet) bindingSource1.DataSource);
      gridView1.ExpandMasterRow(gridView1.FocusedRowHandle); <------- new line
Regards
Alef

RockfordLhotka replied on Tuesday, August 07, 2007

Oh, but this is probably _good_ news J

 

I think then, that raising the ListChanged events for each child list will probably be sufficient to fix the problem – because they must be honoring those events (I hope).

 

Rocky

 

From: alef [mailto:cslanet@lhotka.net]
Sent: Tuesday, August 07, 2007 3:34 PM
To: rocky@lhotka.net
Subject: Re: [CSLA .NET] XtraGrid not having a correct reference to Child list after calling Save.

 

Hello,

I have discussed this problem already with the developers of DevExpress and this issue cannot be easily resolved. They feel sorry for that. The problem lies within the binding mechanism used by the XtraGrid. To provide the ability to display several detail views at once, the grid creates separate related DataViews for each detail GridView. In this situation, when a master row is updated, the detail view doesn't receive any notification, which can be used to refresh data. The only solution I can suggest here is to manually collapse and then re-expand the master row:

[C#]
 
 
      gridView1.CollapseMasterRow(gridView1.FocusedRowHandle); <------- new line
      MasterDetailBL bl = new MasterDetailBL();
      bl.SaveMasterDetailDataSet((MasterDetailDataSet) bindingSource1.DataSource);
      gridView1.ExpandMasterRow(gridView1.FocusedRowHandle); <------- new line
Regards
Alef



RockfordLhotka replied on Tuesday, August 07, 2007

Huh.

 

I wonder if you could do something clever, like implement a method that forces every object (or every collection at least) in the object graph to raise its changed event?

 

The grid control must honor those events, and what I suspect is that the grid simply doesn’t know that the data has changed out from under it.

 

It would be easy enough to try. In the root:

 

Public Sub CascadeEvents()

  _children.CascadeEvents()

  OnUnknownPropertyChanged()

End Sub

 

In the child list:

 

Friend Sub CascadeEvents()

  For Each child As Child in Me

    child.CascadeEvents()

  Next

  OnListChanged(…Reset)

End Sub

 

And in the child:

 

Public Sub CascadeEvents()

  OnUnknownPropertyChanged()

End Sub

 

It might be expensive to raise all those events and have the UI do all that refreshing, but at least you could try this as an experiment to see if it helps. If so, you could try trimming back to just the OnListChanged(…Reset) calls to see if that’s enough.

 

Rocky

jh72i replied on Thursday, October 11, 2007

Hi Rocky et al.
I've just been reading this thread and wonder if you'd comment on my idea from http://forums.lhotka.net/forums/thread/17610.aspx.  Basically, I'd see some similarities between the save problem and the replace problem and I suppose the approach I'm suggesting is simply to copy the event sinks from the old object to the new.

I'm wondering if this is too crazy an idea and if so exactly why?  It'd be a neat solution because the the business layer doing its thing wouldn't affect the UI.

Sending a reset, of course, should work also but that has (certainly in my 'replace' scenario) very negative user impact.

I'm wondering whether to press ahead with this approach and would love to hear the negatives now rather than later.

Thanks for all input.

RockfordLhotka replied on Thursday, October 11, 2007

My guess is that the problem goes deeper than events. Any reference the control is holding to the old object is also incorrect, and you probably have no way of getting the control to talk to the new object.

 

So even if you get the events to work, the control would still call properties/methods on the OLD object I would think – thus causing all sorts of problems.

 

Rocky

 

 

From: jh72i [mailto:cslanet@lhotka.net]
Sent: Thursday, October 11, 2007 7:47 AM
To: rocky@lhotka.net
Subject: Re: [CSLA .NET] XtraGrid not having a correct reference to Child list after calling Save.

 

Hi Rocky et al.
I've just been reading this thread and wonder if you'd comment on my idea from http://forums.lhotka.net/forums/thread/17610.aspx.  Basically, I'd see some similarities between the save problem and the replace problem and I suppose the approach I'm suggesting is simply to copy the event sinks from the old object to the new.

I'm wondering if this is too crazy an idea and if so exactly why?  It'd be a neat solution because the the business layer doing its thing wouldn't affect the UI.

Sending a reset, of course, should work also but that has (certainly in my 'replace' scenario) very negative user impact.

I'm wondering whether to press ahead with this approach and would love to hear the negatives now rather than later.

Thanks for all input.



jh72i replied on Thursday, October 11, 2007

Certainly would seem to make sense Rocky.  In my 'replace' case I always raise the ItemChanged event to which listeners must be aware that an existing item has been replaced with a new and take appropriate action.

But like I mentioned in my post with the Band object of an Infragistics grid I can see that it still holds a ref to the old object whereas all the other 'hookers' hold a ref to the new.

Also, if the listeners do take appropriate action should that not include hooking up its old events to the new object!?

Anyway, thanks for the feedback. Seems a real dodgy way to go right now given that I simply do not know the consequences.  But it is a serious UI issue for me I'm afraid - real-time updates to a hierachical clsa-based datasource.

RockfordLhotka replied on Thursday, October 11, 2007

A more complex, but perhaps necessary, solution here could be to implement a “view” against your objects.

 

If you had a collection of objects with the same shape as the real objects, but in reality just held a reference to those real objects, then the grid would reference this view.

 

And the view could change which underlying objects IT points to, without the grid being any wiser.

 

Such a view would be a bit of code – same shape, but delegating all calls down to the real objects, and relaying all the real object events back through the view.

 

I don’t know if it would work, but it seems reasonable.

 

Rocky

 

 

From: jh72i [mailto:cslanet@lhotka.net]
Sent: Thursday, October 11, 2007 8:54 AM
To: rocky@lhotka.net
Subject: Re: [CSLA .NET] RE: XtraGrid not having a correct reference to Child list after calling Save.

 

Certainly would seem to make sense Rocky.  In my 'replace' case I always raise the ItemChanged event to which listeners must be aware than an existing item has been replaced with a new and take appropriate action.

But like I mentioned in my post with the Band object of an Infragistics grid I can see that it still holds a ref to the old object whereas all the other 'hookers' hold a ref to the new.

Also, if the listeners do take appropriate action should that not include hooking up its old events to the new object!?

Anyway, thanks for the feedback. Seems a real dodgy way to go right now given that I simply do not know the consequences.  But it is a serious UI issue for me I'm afraid - real-time updates to a hierachical clsa-based datasource.



Copyright (c) Marimer LLC