问题
I am working on a quiz control in asp.net with dynamically created questions and options. The main control is basically a container to hold all of the questions. In design view users can add questions through a custom Collection Editor. Everytime i add a question to the collection editor list it generates a question tag for me. Inside each question object is a label and a n amount of Option objects that inherit the Radiobutton Control. Each of these Option objects in turn represent a option the user can select for each question.
This all works except i am now at the part where i want to be able to read the Checked value of each radiobutton. When i want to implement this quiz inside a page and check the questions i want to put a button in this page and call the following function that is inside the control:
$
public String checkQuestions()
{
if (questions != null)
{
foreach (Question question in questions)
{
options = question.readOptions();
int i = 0;
foreach (Option option in options)
{
testLabel.Text = option.Checked.ToString(); // test purposes only
}
}
}
return errors;
}
However once i select a radiobutton and click on the submit button the Checked value will always turn out false for all of the options. Basically it is losing its checked value after a Postback and i am just stuck in trying to solve it. Would appreciate it if anyone could point me in the right direction.
回答1:
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.
来源:https://stackoverflow.com/questions/3854193/asp-composite-control-child-control-radiobutton-losing-checked-value