Asp Composite control child control (radiobutton) losing checked value

人盡茶涼 提交于 2019-11-28 10:36:46

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.

  1. Don't let the BinaryFormatter get at it!
  2. Truly understanding ViewState

I hope all this helps resolve your problem.

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!