CancelEdit() not moving children in DeletedList back

CancelEdit() not moving children in DeletedList back

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


cberthold posted on Wednesday, July 08, 2009

I'm beating my head against the wall a bit on this one.  I'm using 3.6.2 with managed properties and EF as my backend (not that it matters for this part).  I'll include the appropriate code as necessary.  I use lots of partial classes to separate the long list of properties from the business logic so I will do my best to copy and past the necessary pieces.  I roughly have the following:

public partial class Estimate : BusinessBase<Estimate>

private static PropertyInfo<EstimateWorkItemList>
            EstimateWorkItemsProperty = RegisterProperty<EstimateWorkItemList>(c => c.EstimateWorkItems);
        public EstimateWorkItemList EstimateWorkItems
        {
            get
            {
                return GetProperty(EstimateWorkItemsProperty);
            }
        }

public partial class EstimateWorkItem : BusinessBase<EstimateWorkItem>

public class EstimateWorkItemList
        : BusinessListBase<EstimateWorkItemList, EstimateWorkItem>

I load my list using using internal static methods and the DataPortal.FetchChild<> and BeginEdit is automatically called by my bindingsource.  I use the common Rebind(bool save, bool rebind) method to do the changes but I also have done it in a test program and have the same issue.  If I find the item in the EstimateWorkItemList I wish to remove and then use Remove(obj) I correctly see the item move to the DeletedList but when I call CancelEdit it leaves the EstimateWorkItemList as being dirty and does not move the items back into the editable list.  What am I doing wrong?  I have every other scenario working in my screen and this one is the last.  I can just reload the screen if I have to but I am anal retentive and prefer the right way Smile [:)]



sergeyb replied on Wednesday, July 08, 2009

How do you load the work item list?  FetchChild?

 

Sergey Barskiy

Principal Consultant

office: 678.405.0687 | mobile: 404.388.1899

cid:_2_0648EA840648E85C001BBCB886257279
Microsoft Worldwide Partner of the Year | Custom Development Solutions, Technical Innovation

 

From: cberthold [mailto:cslanet@lhotka.net]
Sent: Wednesday, July 08, 2009 1:02 AM
To: Sergey Barskiy
Subject: [CSLA .NET] CancelEdit() not moving children in DeletedList back

 

I'm beating my head against the wall a bit on this one.  I'm using 3.6.2 with managed properties and EF as my backend (not that it matters for this part).  I'll include the appropriate code as necessary.  I use lots of partial classes to separate the long list of properties from the business logic so I will do my best to copy and past the necessary pieces.  I roughly have the following:

public partial class Estimate : BusinessBase<Estimate>

private static PropertyInfo<EstimateWorkItemList>
            EstimateWorkItemsProperty = RegisterProperty<EstimateWorkItemList>(c => c.EstimateWorkItems);
        public EstimateWorkItemList EstimateWorkItems
        {
            get
            {
                return GetProperty(EstimateWorkItemsProperty);
            }
        }

public partial class EstimateWorkItem : BusinessBase<EstimateWorkItem>

public class EstimateWorkItemList
        : BusinessListBase<EstimateWorkItemList, EstimateWorkItem>

I load my list using using internal static methods and the DataPortal.FetchChild<> and BeginEdit is automatically called by my bindingsource.  I use the common Rebind(bool save, bool rebind) method to do the changes but I also have done it in a test program and have the same issue.  If I find the item in the EstimateWorkItemList I wish to remove and then use Remove(obj) I correctly see the item move to the DeletedList but when I call CancelEdit it leaves the EstimateWorkItemList as being dirty and does not move the items back into the editable list.  What am I doing wrong?  I have every other scenario working in my screen and this one is the last.  I can just reload the screen if I have to but I am anal retentive and prefer the right way Smile <img src=">





cberthold replied on Wednesday, July 08, 2009


Inside of my list I have the internal static method and likewise I have an internal static method that loads the data in the EstimateWorkItem passing the DAL.EstimateWorkItem.  I'm using EF with includes to do a single round trip but wouldn't be any different if I used DataSets or MARS:

internal static EstimateWorkItemList GetEstimateWorkItemList(DAL.Estimate estimate)
        {
            return DataPortal.FetchChild<EstimateWorkItemList>(estimate);
        }

private void Child_Fetch(Spectrum.Estimation.Module.DAL.Estimate estimate)
        {           
            this.RaiseListChangedEvents = false;
            foreach (var child in estimate.EstimateWorkItem)
                Add(EstimateWorkItem.GetEstimateWorkItem(child));
            this.RaiseListChangedEvents = true;
        }

in my EstimateWorkItem I have:

internal static EstimateWorkItem GetEstimateWorkItem(DAL.EstimateWorkItem ewi)
        {
            return DataPortal.FetchChild<EstimateWorkItem>(ewi);
        }

 private void Child_Fetch(DAL.EstimateWorkItem data)
        {
            LoadProperty(IdProperty, data.Id);
            LoadData(data); // load all properties           
        }

RockfordLhotka replied on Wednesday, July 08, 2009

It is important to realize that data binding will not automatically call BeginEdit() on the collection itself. And without a call to BeginEdit() on the collection itself, there's no way to use a CancelEdit() to undelete items.

You must make the call to BeginEdit() on the collection itself, before binding the collection to the UI - at least in Windows Forms.

In WPF or Silverlight, if you use CslaDataProvider, things are a little more automatic.

cberthold replied on Wednesday, July 08, 2009

I'm confused then.  My understanding was that the BindingSource will automatically call a BeginEdit on the Root object and in turn since the ELB has the managed properties would automatically call the BeginEdit on the children which I would assume to mean itself and the children?

RockfordLhotka replied on Wednesday, July 08, 2009

No, the bindingsource will call BeginEdit() on the object with currency. In the case of a collection, that is never the collection itself, but instead is exactly one item within the collection.

 

Rocky

cberthold replied on Wednesday, July 08, 2009

Rocky,

That makes sense, which in my case the object with Currency is the root object.  I figured out the problem of why it is not updating but I have not figured out the why of how it gets in the state it is in.  I've determined that the Root object is EditLevel = 1 but the child collection and its child objects are all EditLevel = 0, when I call the Remove, hence the reason it doesn't role it back.  The child object being removed from the collection was added at EditLevel = 0 and was deleted at 0 so it might have well been added new.  If I call BeginEdit directly on the Root object after the object is loaded I see all of the EditLevels = 1 (Root,Child Collection, and Children) as I had expected from my understanding of what should be going on.  I then set a breakpoint directly after I load the BindingSource with the object and now I see the Root object go to EditLevel = 2 and the Child Collection and Children stay EditLevel = 1.
Now that I understand the intended behavior, what behavior am I missing that causes the IEditableObject.BeginEdit not to put the Child Collection into the same EditLevel as the Root object when object is loaded into BindingSource.DataSource?

RockfordLhotka replied on Wednesday, July 08, 2009

The n-level undo behaviors where changed (in 3.0 I think) to mirror the DataTable/DataSet, because that’s what Windows Forms expects. Basically I’d been fighting for years trying to use IEditableObject in a way that Windows Forms didn’t expect, and it brought nothing but pain.

 

So in 3.0 (see the Using CSLA .NET 3.0 ebook for a complete chapter on this) I changed things so IEditableObject behaves the way data binding expects. It still uses n-level undo, but doesn’t cascade the calls – which is exactly what data binding doesn’t expect.

 

By changing IEO, data binding now works as expected in the normal drag-and-drop scenarios.

 

And by leveraging n-level undo underneath, it is still possible – before binding and after unbinding to do n-level undo for something like a Cancel button.

 

But it did change the way the two concepts interact, that is true.

 

Rocky

cberthold replied on Thursday, July 09, 2009

That makes a lot of sense. What else makes sense is that wonderful DisableIEditableObject flag which is exactly what fixed it for me.  Even after almost 7 years of developing .NET I still have not figured out Binding 100%.  In my complex binding scenario with the BindingSource I had a BindingSource on one control bound to the root and a tab control with a single BindingSource on each of the children (not the child collection) and when I was unbinding the BindingSource it was leaving the root at EditLevel = 1 and the Children at EditLevel = 0.  No matter what I did I couldn't figure out how to keep them at the same level. Disabling the IEO functionality let me keep the important property changing events and the ease of binding in the IDE without the headache of the BeginEdit and Cancel/ApplyEdits firing.  I didn't bring into the fact that I had GrandChildren two (the tabs have details).  It's working now but something about the bindingsources not being cascaded caused the problem.  That brings me to a few more questions I'm interested in and haven't been able to find in the book:

1. When unbinding parent - children - grandchild, which direction should I unbind from parent or grandchild? or does it matter?

2. I see the recommendation not to use DisableIEO in the book.  I only use the BindingSource to bind my objects and none of the other functionality like addnew etc. Are there going to be other bugs I have created as result? (knowing that is a hard question but in general is all I can ask for)

RockfordLhotka replied on Thursday, July 09, 2009

1. You must unbind from grandchild to child to parent. Order absolutely matters. The CslaActionExtender control in 3.6 can help you do the right thing with a lot less code than doing it by hand.

 

2. I have done virtually no testing with DisableIEO. I put in that functionality so people can use it as they see fit – but at their own risk. You are on your own to research what does and doesn’t work if you use it.

 

Rocky

 

cberthold replied on Wednesday, July 08, 2009

I think I figured out my problem now but now I need some more explanation.  If I am using a BindingSource and it calls BeginEdit on the root object  the BLB Core.IUndoableObject.CopyState prevents the CopyState from being called on itself if it the parent is being bound.  Why is that?  It almost seems like that needs to check if any of the children are being bound and only copy state for those that aren't for the complex binding scenarios.  I'm still pretty green but I know there are no stupid questions only stupid people Smile [:)]

cberthold replied on Wednesday, July 08, 2009

I gave that a shot and it does not allow me to BeginEdit() telling me "BeginEdit is not valid on a child object"

Copyright (c) Marimer LLC