Question

i'm building a control that inherits from CompositeControl and creates/maintains a number of dynamically generated child controls. i'm trying to save a class to viewstate before the control tears down the control hierarchy and rebuilds it with a new set of dynamically generated controls. other than persisting this class (as xmlserialized) in viewstate on save, i have no viewstate customization and i am not overriding SaveViewState, SaveControlState, etc.

what's happening in the code below: i have a MySuperClass property on the control that handles my info from load to render. to persist the info through paging events i added a MySuperClassStore to hold onto it. currently, the only data being changed by users is within SubClassControl's SubClass.SubClassResponse (not shown). these changes are being correctly handed back to the SuperClass at the SuperClassControl level (using INotifyPropertyChanged).

the problem: seems to occur on postback (OnControlSave & OnControlPageChange) when you have an existing SuperClass already in viewstate. i'm picking up the existing SuperClass, updating it with new responses, and saving it back to viewstate. but if the SubClassResponse coming from user input is set to empty (by erasing text from or un-selecting the items in the child controls of the SubClassControl), the empty response class (not null; actually initialized as ValueType.Empty) does not update the corresponding entry in the MySuperClassStore before saving back to the viewstate. i've even tried to clear the value in SubClassCollection.UpdateResponse() before setting it, but no luck. i thought i might have a problem with my IEquatable<> but it seems fine. new response values and (non-empty) updated response values are both correctly updated and saved to the viewstate on each save/page change.

BUT (and here's where i'm losing it), when i pop some breakpoints in and step through the code (vs2010), it correctly overwrites the removed entry with the empty (but initialized!) response class and saves back to viewstate (in MySuperClassStore). perfect. every single time. until i quit the debugger, and then it stops working.

stripped down version of my classes:

public class SuperClass {
    public Guid SuperClassId { get; set; }
    public SubClassCollection ThisCollection { get; set; }
    public static SuperClass Deserialize(string s) { /* deserialize xml */ }
}
public class SubClassCollection : KeyedCollection<Guid, SubClass> {
    public void UpdateResponse(Guid id, SubClassResponse scr) {
        this[id].Response = scr;
        //this[id].Response = (!scr.IsEmpty) ? scr : new SubClassResponse();
        //** seriously?!? that didn't fix it?
    }
}
public class SubClass {
    public Guid SubClassId { get; set; }
    public int SomeIntProp { get; set; }
    private SubClassResponse _response;
    public SubClassResponse Response {
        get { return (_response != null) ? this._response : new SubClassResponse(); }
        set { if (!_response.Equals(value)) { _response = value; OnPropertyChanged("Response"); } }
    }
}
public class SubClassResponse : IEquatable<SubClassResponse> {
    public string Value { get; set; }
    public ValueType ThisValueType { get; set; }
    public bool IsEmpty {
        switch (this.ThisValueType) {
            case ValueType.Empty: return true; break;
            case ValueType.StringValue: return String.IsNullOrEmpty(this.Value); break;
            case ValueType.ListItemCollection: /* check for null, then .Count > 0 */ break;
            default: return true; break;
        }
    }
    public enum ValueType { Empty, StringValue, ListItemCollection, Etc }
}

and my top-level control has this going on:

public class SuperClassControl : CompositeControl, INamingContainer {
    // OnInit: load MySuperClass from db
    // OnPreRender: get MySuperClassStore from viewstate and output as debug info
    protected SuperClass MySuperClass { get; set; }
    protected SuperClass MySuperClassStore {
        get {
            return (ViewState["SuperClassStore"] == null) ? new SuperClass() : SuperClass.Deserialize((string)ViewState["SuperClassStore"]);
        }
        set { ViewState["SuperClassStore"] = value.ToSerialized(); }
    }

    protected override void CreateChildControls() {
        // generate control hierarchy
        SuperClass mysuperclass = this.MySuperClass;
        foreach (SubClass mysubclass in mysuperclass.ThisCollection) {
            this.Controls.Add(new SubClassControl(mysubclass));
        }
        // add top-level control linkbuttons, hook up events, etc...
    }
    protected void OnControlSave(object sender, EventArgs e) {
        SuperClass mysuperclass = this.MySuperClass;
        SuperClass mystorageclass = this.MySuperClassStore;
        // loop through control hierarchy, make sure we've got any response changes
        // copy the changes to mystorageclass for persistence

        // ***** problem is occurring here?? *****
        foreach (Guid g in mysuperclass.ThisCollection.HasChanges) {
            mystorageclass.ThisCollection.UpdateResponse(g, mysuperclass.ThisCollection[g].Response);
        }

        // if the current page only displayed i=5 to 8, will only update i=5 to 8
        this.MySuperClassStore = mystorageclass;
    }
    protected void OnControlPageChange(object sender, EventArgs e) {
        OnControlSave(sender, e);
        // set next or previous page index then reset control hierarchy
    }
}

any thoughts? i've been messing with this for several days now, and i'm running out of ideas. thanks in advance!

will

Was it helpful?

Solution

so, i solved this problem by inverting the update process:

mystorageclass.ThisCollection.UpdateResponse(g, mysuperclass.ThisCollection[g].Response);
this.MySuperClassStore = mystorageclass;

to:

mysuperclass.ThisCollection.UpdateResponse(g, mystorageclass.ThisCollection[g].Response);
this.MySuperClassStore = mysuperclass;

where the loop gets all SubClass NOT existing on the current page.

i works, so "yay?" - but i'm still at a loss for why this actually works better than the original piece of code, so if anyone has some insight into this, i'll happily give you points for the answer!

Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top