Persisting Events

Persisting Events

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


beerisgood posted on Monday, July 14, 2008

Hello, experts.

I have an issue I need help with. First some background: I'm using Winforms and CSLA 3.5. I have a QuoteEdit form that uses a Quote object derived from Business Base. This Quote object contains a CustomerInfo object derived from Readonly Base. The form has text boxes that are bound to the customerid and name fields of the CustomerInfo object within the quote, so that when the user selects a new customer using a popup form, the new CustomerInfo object is stored in the Quote object and the customerid and name field are updated on the form. So far so good.

Now, if the user clicks on the customer name, a new Customer (BusinessBase) object is loaded from the database and passed to a CustomerEdit form. What I want to happen is, if the user changes the company name and clicks Apply on the CustomerEdit form, the name should also update on the QuoteEdit form. I have got it mostly working by adding the following to the Customer object:

public class CustomerNameChangedEventArgs : EventArgs

{

private CustomerInfo _customer;

public CustomerInfo customer

{ get { return this._customer; }}

public CustomerNameChangedEventArgs(CustomerInfo info)

{ this._customer = info; }

}

public delegate void CustomerNameChangedEventHandler(object sender, CustomerNameChangedEventArgs e);

[NonSerialized]

[NotUndoable]

private CustomerNameChangedEventHandler _nameChanged;

public event CustomerNameChangedEventHandler NameChanged

{

add { _nameChanged += value; }

remove { _nameChanged -= value; }

}

private void OnNameChanged()

{

if (_nameChanged != null) _nameChanged(this, new CustomerNameChangedEventArgs(this.CreateCustomerInfoObject()));

}

string oldValue;

protected override void CopyStateComplete()

{

oldValue = _name;

}

protected override void AcceptChangesComplete()

{

if (_name != oldValue) OnNameChanged();

}

When the user clicks the customer name field, the QuoteEdit form subscribes to the Customer object's NameChanged event before displaying the CustomerEdit form. The first time the user changes the name and clicks Accept everything works as planned and the fields are updated on the QuoteEdit form. However I have 2 issues with this.

1. As I discovered in a previous post, the CopyStateComplete() event can fire many times due to data binding. Not that this breaks my code, but I find it a little irksome that the oldValue is saved potentially many times when the button was only clicked once. Call me anal. Is this event the proper place to store the old value? I haven't found a better place yet.

2. This is the big one. As I said, this only works the first time the button is clicked. Subsequent name changes do not trigger an event. No doubt this is because the Customer object is getting replaced by a brandy new one returned from base.Save(). And since the event handler must be nonserialized, any objects that have subscribed to it are not cloned with the Customer object. So when the new Customer object has a name change and fires its event, no one is listening. So my question is...how to I get the list of NameChanged event subscribers to persist through the saving of the Customer object?

Thanks in advance.

xxxJasonxxx replied on Monday, July 14, 2008

Gotta be honest, I did not follow all of your post so please ignore me if it doesn't apply. 

I noticed that you mentioned problems with your custom events and serialization.  A while back I ran into a problem with my custom event handlers breaking after the object was serialized/deserialized.  To get it to work, I ended up having to re-attach the handlers after deserialization.  Just thought I'd mention it in case this is part of your problem.  Here's the code that fixed my problem...

Protected Overrides Sub OnDeserialized(ByVal context As System.Runtime.Serialization.StreamingContext)

   MyBase.OnDeserialized(context)

   'REATTACH CUSTOM EVENT HANDLERS BROKEN BY SERIALIZATION
   'These custom event handlers get broken when this Order 
   'object is serialized/deserialized. To fix this problem 
   'we must to programmatically reattach them here (after
   'the object is deserialized).

   'PropertyChanged handlers.
   AddHandler Me.PropertyChanged, AddressOf OnAnyPropertyChanged
   AddHandler mobjFeesTaxesTotals.PropertyChanged, AddressOf OnAnyPropertyChanged

   'ListChanged handlers.
   AddHandler mcolLineItems.ListChanged, AddressOf OnAnyListChanged
   AddHandler mcolPayments.ListChanged, AddressOf OnAnyListChanged
   AddHandler mcolProgressHistoryEntries.ListChanged, AddressOf OnAnyListChanged
   AddHandler mcolShipDateHistoryEntries.ListChanged, AddressOf OnAnyListChanged

End Sub

beerisgood replied on Tuesday, July 15, 2008

After reading your post I decided to try the OnDeserialized event.  That technique may have worked for you because all of your event subscribers (child classes and collection objects) are contained within your business base, so they persist after the deserialization and are therefore available to resubscribe.  For me, however, my subscriber is a form, so when the business object deserializes, the subscribers have been lost (_nameChanged event is now null) so the object doesn't know who needs to resubscribe.  The only way to get OnDeserialized to work is to have it fire yet another event that the form needs to subscribe to, to let it know that it needs to resubscribe to all events.  This got me thinking that maybe such an animal already exists.

Lo and behold I found the Saved event that is generated from BusinessBase.  This little beauty not only lets me know that a save has been completed, but also provides a reference to the newly deserialized object to which the form can resubscribe.  To take it one step further, since the form already has to subscribe to the Saved event, there really is no point in having my own custom event for a name change, since the form can now check the old value in its CustomerInfo object to that in the newly provided Customer object, and act accordingly.  So now everything seems to be working just fine.

It's a beautiful thing. 

Copyright (c) Marimer LLC