ASP.NET custom control: when is LoadPostData() called?

David picture David · Jul 9, 2010 · Viewed 7.4k times · Source

I have developed a custom control that extends ListBox. The idea is that the control 'remembers' modifications to its elements which occurred client-side, e.g. as a result of an AJAX request.

The way it works is that the control also renders a hidden input, and the result of the AJAX request is stored in the hidden input. This is posted back, and the control's LoadPostData() method looks for the hidden input, and if the hidden input has data, creates the ListItem collection from it.

This works perfectly so long as the user has made a selection from the list box. If they have not, the LoadPostData() method doesn't get called, and consequently the new ListItem collection is not created. (I've established this using the debugger.)

I assume that the LoadPostData method is only called if the POST data collection includes data corresponding to the control's UniqueID (i.e. 'name' attribute in HTML). If the user hasn't made a selection from the list box, nothing is included in the post data for the list box's UniqueID and LoadPostData() isn't called. Is that correct?

Can anyone suggest how I can ensure that my custom ListBox's LoadPostData() method is called every postback regardless of whether the user has made a selection?

Thanks in advance - I'm really stuck with this one.

David

Answer

Rozwel picture Rozwel · Feb 25, 2011

I am a little late jumping in on this but, just for future reference, here is how I accomplished something similar…

My control is a tree that uses templates for the nodes. The issue where I was dealing with this was how to capture the client side changes to the expanded/collapsed state of the nodes. What ended up working was:

In CreateChildControls add the hidden field to the controls collection of my root control.

protected override int CreateChildControls(IEnumerable dataSource, bool dataBinding)
{
    ...
    _cdExpanded = new HiddenField();
    _cdExpanded.ID = "cdExpanded";
    this.Controls.Add(_cdExpanded);
    ...
}

In OnInit call

protected override void OnInit(EventArgs e)
{
    ...
    Page.RegisterRequiresPostBack(this);
    ...
}

In LoadPostData look for a value in the post collection that matches the UniqueID (not ClientID) of the hidden field:

public bool LoadPostData(string postDataKey, System.Collections.Specialized.NameValueCollection postCollection)
{
    ...
    string cdExpanded = postCollection[_cdExpanded.UniqueID];
    ...
}

Within the classes for the individual nodes I have code which populates the onclick events of my toggle buttons with a call to a JavaScript function which takes the ID of the base control and the individual nodes as arguments.

    string ToggleScript
    {
        get
        {
            return "ToggleNode('" + this.ClientID + "', '" + _TreeRoot.ClientID + "');";
        }
    }
    protected override void Render(HtmlTextWriter writer)
    {
        ...
        if (this.HasChildren)
        {
            writer.AddAttribute("onclick", ToggleScript);
        }
        ...
    }

This makes it so that finding the hidden field is fairly easy via getElementById:

function ToggleNode(nodeID, treeID) {
var cdExpanded = document.getElementById(treeID + "_cdExpanded");
...
}

The JavaScript then modifies the value of the hidden field as needed for the event that occurred. When we get back to the server I am able to parse out the contents of this field and modify the control state as necessary before it gets rendered again. (Note: I actually use 3 hidden fields for tracking different events but the concept is the same)

Hope this helps others in the future…