At a first glance, there are two things I'd check. Firstly, make sure you're implementing IPostBackDataHandler
. this requires you to implement two methods, LoadPostData
and RaisePostDataChangedEvent
. At my first guess, the first one is probably the source of your problem.
Handling postback manually
LoadPostData
takes a string postDataKey
and a NameValueCollection
postCollection
and returns a bool
indicating whether or not the value has changed as a result of the postback. You don't need to implement this the way .Net originally intends, for example I created a control that held several radio buttons (that for reasons that aren't important here couldn't simply be a RadioButtonList
control) and so made sure they were all named by a property string GroupName
and inspected the postCollection
for that GroupName
:
public bool LoadPostData(string postDataKey,
System.Collections.Specialized.NameValueCollection postCollection)
{
bool oldValue = _isChecked;
postCollection = HttpContext.Current.Request.Form; // See note below
_isChecked = (postCollection[this.GroupName] == this.Text);
return oldValue == _isChecked;
}
You'll notice that I'm redefining the postCollection
here; this is because postCollection
only contains a subset of the HttpRequest.Form
corresponding to what ASP.Net thinks your control should care about. As you're also building a composite control here, you probably want to do the same.
Don't worry if this doesn't work first time round; it's worth stepping through what gets passed into this method in debug mode (or outputting things to the HttpContext.Trace
, which I often find easier) to see why your code isn't quite what you need.
A quick caveat
One last thing: LoadPostData
is only called if the posted form contains a field with a name which matches the UniqueID
of your control. As your control is a composite control, you might want to cowboy this slightly, like so:
protected override void Render(HtmlTextWriter writer)
{
base.Render(writer);
writer.WriteBeginTag("input");
writer.WriteAttribute("type", "hidden");
writer.WriteAttribute("name", this.UniqueID);
writer.WriteAttribute("value", "post");
writer.Write(" />");
}
It's a dirty hack, but it'll work ;o)
Handling viewstate manually
If handling the postback manually doesn't solve your problem, it might be that you need to mess with the viewstate of your control. Don't worry, this is nowhere near as scary as it seems, provided you follow a few simple rules.
To handle your viewstate manually, you just need to override two methods called, obviously enough, LoadViewState
and SaveViewState
. The first takes an object
of viewstate to inflate and the other returns that same object
structure. If you make your SaveViewState
override return something containing the structure you need to save all the important properties that need persisting, then you just inflate it again in your LoadViewState
method.
Here's where the first of the cunning tricks comes up. There are certain datatypes that you should use for saving viewstate and you should never use any other type (because other types are stored really inefficiently). The types that will probably be most useful to you are System.Web.UI.Pair
, System.Web.UI.Triplet
and our old friends System.Collections.ArrayList
and System.Collections.Hashtable
. Pairs and Triplets simply store two or three values of type object
; ArrayLists are effectively a List<object>
.
I'd guess that, in your circumstance, you probably want to store either (1) an ArrayList of boolean flags, storing the "checkedness" of your radiobuttons or (2) an ArrayList of strings or ints, storing the IDs or index of the checked radiobuttons.
In the control I mentioned earlier, I just needed to store the checkedness and the Text
property, so my LoadViewState
and SaveViewState
methods looked like this:
protected override void LoadViewState(object savedState)
{
Pair state = savedState as Pair;
if (state != null)
{
_isChecked = state.First as Nullable<bool> ?? false;
this.Text = state.Second as string;
}
}
protected override object SaveViewState()
{
return new Pair(_isChecked, this.Text);
}
Again, if this doesn't work first time, you almost certainly want to step through the code or throw things into the Trace. Importantly, you probably want to avoid throwing Exceptions from these methods, in case your viewstate is corrupt or non-existent or something.
Further reading on viewstate
There are a couple of very useful articles I keep bookmarked for when I'm messing with viewstate. The first one explains about why you should only store certain types in the viewstate (like using ArrayList
and Hashtable
, rather than List<T>
and Dictionary<TKey, TValue>
) and the second is a good in-depth explanation of how all this viewstate stuff actually works.
- Don't let the BinaryFormatter get at it!
- Truly understanding ViewState
I hope all this helps resolve your problem.