Update object reference bound silverlight listbox after save

Update object reference bound silverlight listbox after save

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


AaronH posted on Monday, March 29, 2010

Wondering how others have handled a similar situation.  I have a ViewModel (not a CSLA VM) that exposes 2 properties:  VM.Items and VM.CurrentItem (left out getter/setter code for sake of brevity - PropertyChanged is being fired properly.  Also, Parent is a EditableRoot)

 

public 

 

 

class MyViewModel : INotifyPropertyChanged {

   public ObservableCollection<Parent> Items { get; set; }

   public Parent CurrentItem { get; set;} }

 

 

Here is my ListBox declaration (Excluding DataTemplate)

 

<

 

 

ListBox Name="list" ItemsSource="{Binding Items}" SelectedItem="{Binding CurrentItem, Mode=TwoWay}">

 

 

My object model supports a Parent.Children relationship.  So, I can call Parent.Children.Add(new Child());

The problem I am having is that when I call Parent.Save(), I need the originally bound object to update to reflect the new item sent back from the server.  Failure to do so will cause the newly added child to be inserted whenever the user makes a change to the bound Parent and saves the changes. (because the child.IsNew property will ALWAYS be true)

Manually setting CurrentItem to e.NewObject doesn't do the trick.  Apparently (from my current understanding) a bound ListBox item reference cannot changed or swapped out.  What I have found works is the following hack:

        internal void Save(object sender, SavedEventArgs e)
        {
            int currentIndex = CurrentItems.IndexOf(CurrentItem);
            CurrentItems.Remove(CurrentItem);         
            CurrentItems.Insert(currentIndex, e.NewObject);
     // This line alone will not work - it WILL update CurrentItem, but that will leave the bound ListBoxItem pointing to the original object
            CurrentItem = e.NewObject; 
        }

Surely, there must be a better way do this?

Thanks in advance for sharing your thoughts and/or experiences.

AaronH replied on Tuesday, March 30, 2010

Ok, so admittedely, I have questioned my own design since I've started this approach.  My experience with CSLA over the years has been mostly within an ASP.NET context.  In an ASP.NET context (let's exclude Ajax patterns for now), the page was typically rebuilt on post back, relying on business objects that either populated themselves with state from the db on postback, or were stuffed in the session.  In either case, the UI was always rebuilt after some user action invoked a postback.

With any SL, WPF, or Win Forms UI, this is obviously not the case.  I'm sure I'm killing a dead horse here, so I'm hoping for any links to old conversations on good UI design to accomodate business objects if this is the case.

One thought I had was to have my listbox display records based on ReadOnly objects.  This has typically worked well for me from an ASP.NET scenario, Where each item in the listbox contains an edit link/button that when clicked, would present some Add/Edit UI to the user. 

What I don't like about this approach within a thick client context, is that if the user edits a piece of information in the business object (let's say Person.FirstName), and Person.FirstName is being displayed in a listbox item that is bound to a ReadOnly object, that Person.FirstName won't be reflected in the listbox item.

So a way around this, of course, is to re-pull the readonly list to reflect any changes that may or may not be displayed in listbox item anyhow.  Seems like alot of chattiness to me.  In addition, rebuilding the listbox will cause issues, such as losing the spot where the user was when they edited the object.

So that is why I resorted to a list of EditableRoot objects.  I would load them all once, and if the user edited one, its changes would be reflected in the bound listbox item as well.

Hopefully this provides some more insight as to my approach.  I would really love to hear how some of you have dealt with this challenge.  Or maybe I'm not thinking far enough out of the box?

Thanks again!

RockfordLhotka replied on Tuesday, March 30, 2010

It almost sounds like the user is editing and saving one item at a time, not editing several items and saving them as a batch?

BLB is designed to support the batch scenario. ERLB is designed to support the one at a time scenario. Maybe you are just using the wrong base type for your list?

Though ERLB must be a root object, it can't be a child of another object, so if you really do need a parent above the list, then BLB is the only real option.

AaronH replied on Tuesday, March 30, 2010

Yes, that's correct, each object is saved independently of each other.  The only reason why I'm using a ERLB is for a transport mechanism.  I don't want to save the BOs via the ERLB because it is to my understanding that the WHOLE ERLB would be sent back if the user edited only one item in the collection.  Is this correct?  If that's the case, I want to avoid sending 20 objects (and their children) back to the server just to save one of them.

So what I have is:

ERLB --> Parent Objects (each Parent containing a Children collection).  Once the SL client receives the loaded ERLB, the items are copied in the ObservableCollection, and the view is bound to it via the VM.

Who knows, maybe just dealing with sending the entire ERLB is acceptable from a performance standpoint.  Admittedely, I haven't tested to capture the payload size with Fiddler, etc. using the full-blown ERLB approach.  Logistically, however, seems like a waste though.

How are other folks handling this design dilemna?

Thanks again!

ajj3085 replied on Tuesday, March 30, 2010

That's not correct; a root object in Csla terms means its saved independantly of other objects.  So when you leave the row in SL, it should ONLY save that row's object, not the entire list.  You should simply be able to bind the ERLB to your grid and have it work, i would think.  That's how you'd do it in WinForms anyway.

RockfordLhotka replied on Tuesday, March 30, 2010

Andy is correct.

A BLB saves the list as a batch - the entire list travels to the server, items are saved, and the list returns to the client as a new list.

An ERLB saves one item at a time. That item travels to the server, the item is saved, and the item returns to the client where it replaces the old item in the ERLB. The actual binding source of the list is always the same ERLB instance, but the child item in the list is replaced.

It is possible that the SL data binding doesn't understand this concept, in a ListBox anyway. I've tested and used this technique in the SL datagrid control and it works fine. It doesn't work in the WPF datagrid control because that control is extremely buggy (or was as of .NET 4 Beta 2 - I haven't checked the RC).

I would expect the SL data binding to handle this fine though. When the item is replaced in the ERLB, the ERLB raises the appropriate CollectionChanged event to indicate the change. It is just a standard set operation, where an item in the list is set to a new object instance - something that is a native concept in ObservableCollection<T>.

RockfordLhotka replied on Tuesday, March 30, 2010

There is no such thing as AddNewCore() in SL. We do support the concept in CSLA, but data binding doesn't know about it. Also, AddNewCore() in SL is async, so it is void, where the .NET implementation returns the new child object.

The reason we put it into the SL codebase is that the pattern is useful, and even if you have to explicitly call AddNew() from your code-behind or viewmodel it is still a really nice way to manage the addition of new items to a list.

Of course, just like on .NET, it is entirely optional.

RockfordLhotka replied on Tuesday, March 30, 2010

I don't know if it works automatically with the DataForm.

ERLB triggers off the use of IEditableObject. So if DataForm uses IEO (and I doubt it does) then it would work automatically. If not (which I suspect), then you'll need to call the IEO methods yourself based on UI events. Just make sure to go through the IEO interface to trigger the behavior.

AaronH replied on Tuesday, March 30, 2010

Awesome, thanks for the feedback everyone.  I guess I could have checked myself whether it was the case that the entire ERLB was sent back.  I was told that it did, so I believed what I heard...

RockfordLhotka replied on Tuesday, March 30, 2010

Let's be very clear on terminology. There's a difference between the base classes and stereotype names. I've been using base classes in this thread.

BLB = BusinessListBase - used for an editable list stereotype

ERLB = EditableRootListBase - used for a dynamic list stereotype

BLB does batch saves of its child objects. ERLB contains root objects and saves them one at a time based on data binding interacting with the child objects through the IEditableObject interface (or your code simulating data binding by using the IEditableObject interface).

AaronH replied on Wednesday, March 31, 2010

Perhaps I should start another thread.  If you think I should, let me know.

I'm having 2 issues.  This thread began talking about using a iistbox bound to an observableCollection containing EditableRoot objects that I retrieved via an EditableRootList.

So after the question of whether the entire ERLB is sent back to the server or not was cleared up, I now bind the list directly to the ERLB.  ONLY, the list i've been speaking of is not a list.  It is an Accordian control.  Why an Accordian control?  Because it's pretty cool, believe it or not.  I could have done something similar with expanders, but not quite.  I like the fact that the accordian can show only one item at a time. 

Anyhow, first issue: unfortunately when adding a new object to the ERLB, the Accordian does not refresh.  I tried replacing the Accordian with a listbox, and that DOES work, however.

The second issue is that when I call BeginEdit/ApplyEdit/BeginSave() on any of the objects, it makes 2 calls to the dataportal.  So what happens is that either DP.Insert or DP.Update is invoked twice.

Here's the interesting thing: if I load an individual object via Parent.GetParent(123);, then call BeginEdit/ApplyEdit combo, it does not do this.  So I'm wondering if by loading up a EditableRoot into the ERLB, that there is some sort of flag being set by the ERLB that is messing with the internal state of the EditableRoot.  REALLY strange.

What's more strange is that I cannot walk through the CSLA SL code.  If I set project references to CSLA projects, it compiles just fine.  However, when I go to run, the CSLA barks at me about not being able to find the ClientConfig information.  So getting this to work is my top priority for the day, but I cannot walk through the CSLA code to see what's going on to help me debug my issues.  So I guess you could say I"m having 3 issues.

Any thoughts on any of these issues would be most appreciated!

RockfordLhotka replied on Wednesday, March 31, 2010

AaronH

The second issue is that when I call BeginEdit/ApplyEdit/BeginSave() on any of the objects, it makes 2 calls to the dataportal.  So what happens is that either DP.Insert or DP.Update is invoked twice.

This one is easy. ERLB is designed to save the object for you, and that behavior is triggered when you call ApplyEdit() on the object. So your explicit BeginSave() call is redundant and is where the duplicate call occurs.

AaronH replied on Wednesday, March 31, 2010

I apologize, I left out a crucial detail from my last post, and it may turn out that i just end up ditching the usage of the Accordian.

So because the Accordian doesn't reflect the addition of BO's to a bound ERLB, I instead have an Accordian bound to an ObservableCollection of BO's.  The BO's are EditableRoots that were loaded from an ERLB. 

After the ERLB is returned to the SL client, then the BO's are copied from the ERLB into the OC at that point.  The Accordian does respond to events raised by the OC when adding new items to it, so I'm not sure what events other than INotifyPropertyChanged are being raised by OC that are NOT being raised by ERLB on AddNew.

Anyhow, so when I edit a BO in this scenario, the ERLB is completely out of the picture.  So before editing it, I call BeginEdit() and ApplyEdit() after the user clicked "OK" to save their changes.  This alone does not invoke a DP.Insert/Update method.  Then I invoke BeginSave(), and THEN it makes 2 calls.

So this leads me to believe that ERLB is changing the internal state of the BO when it is being added to it on the server.

Does this make sense?

Man, the things I'm going through in order to get this Accordian to work....

RockfordLhotka replied on Wednesday, March 31, 2010

ERLB on Silverlight is an ObservableCollection, so I don't know why binding to an OC would be any different from binding to an ERLB.

When you call AddNew() on an ERLB, that triggers the AddNewCore() method. In the AddNewCore() method you ultimately call OnCoreAdded() - either synchronously or asynchronously - to get the new item added to the collection.

OnCoreAdded() simply calls Add() to add the item - this Add() method is the ObservableCollection method - no different from calling Add() on any other OC. OnCoreAdded() also raises an AddedNew event for parity with pre-existing CSLA code from other collection types, but that's not relevant to this particular scenario.

The OC Add() method raises a CollectionChanged event to indicate that a new item was added, and this is what any UI control should be listening for. Obviously ERLB raises that event, because it delegates to its base class to do the add.

The bit about ApplyEdit() doesn't make sense, unless you are letting the user edit the item before adding it to the ERLB. But if the root object is contained in an ERLB and you call ApplyEdit() such that the edit level goes to 0, that is what triggers ERLB to save the root object.

But the more you describe your scenario, the less I understand why you'd use ERLB at all. You are already manually handling the create/beginedit/applyedit/save operations - so you are already doing everything ERLB would normally do for you, which implies that ERLB is offering essentially zero value.

If the only reason for using ERLB is to get a list of root objects (and not to manage them once they are on the client), then I wouldn't use ERLB. Instead I think I'd create my own collection using Csla.Core.MobileList<T>. This collection would just have a factory method to get the collection, and would have no provision for saving or managing the items once it returned them to the client.

AaronH replied on Wednesday, March 31, 2010

Rocky, that's exactly right.  The ONLY reason at this point for using the ERLB is to bring down my list of editable roots.  So other than using it as my transport mechanism to retrieve a list of EditableRoots, it is providing zero value.

Because calling AddNew on the ERLB doesn't cause a bound Accordian to reflect the newly added item, I introduced the OC as a middle man, because for whatever reason, it is reacting properly to it when invoking OC.Add().  When a new EditableRoot is added to the OC, the Accordian updates properly to reflect the new BO.  I then at some point invoke BeginSave directly on the new BO, not on any ERLB.

Hopefully this provides you with more insight for my design choice here.

I was not aware of a MobileList.  Admittedely, I've been out of touch with CSLA for some time as my previous client had me go down the WCF RIA path (nowhere near production ready, imo).  But that's another story :)

RockfordLhotka replied on Wednesday, March 31, 2010

MobileList is the base class for all list types in CSLA for Silverlight. It inherits from ObservableCollection and just adds serialization capabilities, so it is the most basic list type that is useful for n-tier development within CSLA.

AaronH replied on Wednesday, March 31, 2010

Yeah, I've definitely run into a few issues with the control, to be sure.  I'm still contemplating abandoning the accordian.  We'll see.  But thanks for your thoughts and comments, I really appreciate them.

AaronH replied on Wednesday, March 31, 2010

Thanks for all your help, Rocky.  Really appreciate it!

RockfordLhotka replied on Wednesday, March 31, 2010

AaronH

Anyhow, first issue: unfortunately when adding a new object to the ERLB, the Accordian does not refresh.  I tried replacing the Accordian with a listbox, and that DOES work, however.

Maybe Accordian has a bug. Keep in mind that the toolkit has various bands of stability, and Accordian is in Preview, which is second from the bottom.

Copyright (c) Marimer LLC