CancelEdit() / Binding Issue.

CancelEdit() / Binding Issue.

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


rxelizondo posted on Saturday, December 03, 2011

 

I have a problem that is driving me crazy. Not sure if this is a CSLA issue or a WPF (possible Silverlight) issue or both.

In essence, a property set accessors is being called on a property bounded to a combobox. It’s hard to explain so I created a supper simply sample project that reproduces the behavior.

If anyone has some time or is interested, please download the attached project and see if you can find what the issue is. The project consists of a very straight forward CSLA parent child collection.

SomeParent -> SomeCollection -> SomeChild

To reproduce the issue:

1) Start the project: You will see the MainWindow appear with a single button on it.

2) Click the button: Behind the button there is code that gets a reference to the “SomeParent” object with one child added to it, after that, the code calls a “parent.BeginEdit()” and popups a dialog form displaying the “SomeChild” object data. 

3) Click the “Cancel” button on the popup form: The pop-up form only has one combobx and a cancel button. Do nothing except click on the cancel button, this will resume execution on the MainWindow form where a “parent.CancelEdit()” call will be made and that’s where bad things start to happen.

 

Basically, the “PayrollNumber” property of the “SomeChild” object gets call when it shouldn’t be called.

The sample project is very, very simple to follow (at least I think so) so I ask you to take a minute to see if you can see why such weird behavior is happening.

THANK YOU!.

JonnyBee replied on Sunday, December 04, 2011

The "issue" is caused by having the list items as part of the edit/undo operation. If you change SomeChild to this:

        public static PropertyInfo<List<Employee>> ValidPayrollNumbersProperty = RegisterProperty<List<Employee>>(c => c.ValidPayrollNumbers, RelationshipTypes.PrivateField);
        [NotUndoable]
        private List<Employee> _validPayrollNumbers;
        public List<Employee> ValidPayrollNumbers
        {
            get { return GetProperty(ValidPayrollNumbersProperty, _validPayrollNumbers); }
        }
 
        public static SomeChild NewChild()
        {
            return DataPortal.CreateChild<SomeChild>();
        }
 
        protected void Child_Create()
        {
            LoadProperty(PayrollNumberProperty, 222);
 
            var lst = new List<Employee>();
            lst.Add(new Employee() { EmployeePayrollNumber = 111, EmployeeName = "Eeny" });
            lst.Add(new Employee() { EmployeePayrollNumber = 222, EmployeeName = "Meeny" });
            lst.Add(new Employee() { EmployeePayrollNumber = 333, EmployeeName = "Miny" });
            lst.Add(new Employee() { EmployeePayrollNumber = 444, EmployeeName = "Moe" });
            _validPayrollNumbers = lst;
        }

The code will work as expected.

So for my own preference:

A: Add list/namevalue lists for use in combo boxes into a UOW object (not a part of the BO)

B: Add as a property with private backing field and mark as NotUndoable

When the list is part of the Edit/Undo the content of the list will also get a "rollback" so the items in the list is cleared and reread - and this causes databinding to update the field.

It doesn't matter that you do not have a setter on the list. It is a managed field and as of up tilll now - all managed fields are part of the Edit/Undo. You must use a private field to use the NotUndoable attribute and exclude that field from being part of the Edit/Undo.

 

rxelizondo replied on Sunday, December 04, 2011

Jonny,

Thank you *very* much for taking the time to try to help me out with my issue. I also suspected that the culprit was the ItemSource being rebinded during the undo operation but believe it or not, this is not always the case. I have updated the sample project to show that under special circumstances the issue does not occur.

The revised project, works the same way but instead of having a List<Employee>() as the combobox items source I now use a simple array of integers (also removed the SelectedValuePath="EmployeePayrollNumber" DisplayMemberPath="EmployeeName" XAML from the combo). If you follow the same steps as before, you will now see that the issue is no longer there, Can you tell why is that?

Also, unfortunately, my business object is a little bit more complicated than the sample project I posted. In my real business object I *do* need my list to be undoable. The reason for this is because the list values depend on the value of some other property so when the other property undoes, the list also has to be reverted to its previous value to stay in sync.

So far my solution has been to do a "DataContext = null" on the form before I do a "CancelEdit" but I don't like the hack.

What is up with the rating by the way? Did you rated the post?

Thanks again.

 

rxelizondo replied on Monday, December 05, 2011

Somethign else that is kind of weird is that if you do:

          someChild.BeginEdit();

          someChild.CancelEdit();

Istead of:

          someParent.BeginEdit();

          someParent.CancelEdit();

Then things work as expected.This seems odd to me because I would expect for both way to work or both ways to break....mmmmm.

 

JonnyBee replied on Monday, December 05, 2011

The difference is in which object gets restored.

Rene Elizondo

          someChild.BeginEdit();

          someChild.CancelEdit();

Will only restore the properties on someChild

Rene Elizondo

          someParent.BeginEdit();

          someParent.CancelEdit();

This will restore the field values of someParent and all child/grandchild objects. And since someChild is in a collection the object instance is restored to a new instance.

 

rxelizondo replied on Monday, December 05, 2011

JonnyBee

This will restore the field values of someParent and all child/grandchild objects. And since someChild is in a collection the object instance is restored to a new instance.

 

Really? I was always under the impression that the big thing about BeginEdit() / CancelEdit() was that property values where edited in place. So basically I thought that BeginEdit() just copied property values and CancelEdit() just replace the property values. I did't know the whole child instance was recreated.

Man, I really need to go back and re-familiarize myself with the CSLA source code, its been a looooong time.

 

Can't help but wonder....  Am I really the first person to run into this type of issue? Wonder if the way I am doing things is all wrong.

Thanks.

JonnyBee replied on Monday, December 05, 2011

Hi,

I could be wrong about restoring into new instances - it could easily have to do with CollectionChanged event args being sent to the UI as well.

I'll have to look into the actual code to check what happends in the CancelChanges part.

 

JonnyBee replied on Monday, December 05, 2011

Yes, I was wrong -  the objects are NOT restored into new instances.

But when a collection is "Undo"ed the code will suppress ListChanged events and when completed send a OnCollectionChanged evet:

        this.OnCollectionChanged(new NotifyCollectionChangedEventArgs(NotifyCollectionChangedAction.Reset));

It's most likely this event that mess up the databinding.

rxelizondo replied on Tuesday, December 06, 2011

 

Jonny,

Out of curiosity, do you see anything particularly wrong with the sample project I uploaded? In other words, do you see something being done there that is out of the ordinary that you would't expect anyone to do?

I am only asking because if you don’t see anything wrong (and I personally don’t see anything wrong) then I would call this issue a limitation of the CSLA.

I realize that maybe the issue is with WPF (or maybe even Silverlight) but since you will have better luck of squeezing water from a desert rock than to have Microsoft touch the WPF code to fix anything then perhaps something could be added to the CSLA to make this scenario work.

Just curious..

 

rxelizondo replied on Wednesday, December 07, 2011

 

Well, incase anyone cares..... after many hours of screwing around with this thing, I finally figured out what the issue was.

As it appears, the issue had to do with me not having overridden Equals on the object being used as the backing store for the combobox item. The effects of this were that when the ItemsSource property on the combobox got updated, the currently selected item (a reference type) was no longer able to find an item on the collection that corresponded to itself (because all list items where new references) so the selected items would be cleared out triggering an update on the business object property.

Once I override Equals on the object being used as the backing store for the combobox item, I was able to replace the ItemsSource collection and as long as one item on the collection was considered "Equals" to the selected item there were no side effects.

Special thanks to Jonny for trying to help me out with the issue.

Cheers.

 

 

 

Copyright (c) Marimer LLC