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.
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,LoadPostDataandRaisePostDataChangedEvent. At my first guess, the first one is probably the source of your problem.Handling postback manually
LoadPostDatatakes a stringpostDataKeyand aNameValueCollectionpostCollectionand returns aboolindicating 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 aRadioButtonListcontrol) and so made sure they were all named by a propertystring GroupNameand inspected thepostCollectionfor thatGroupName:You’ll notice that I’m redefining the
postCollectionhere; this is becausepostCollectiononly contains a subset of theHttpRequest.Formcorresponding 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:
LoadPostDatais only called if the posted form contains a field with a name which matches theUniqueIDof your control. As your control is a composite control, you might want to cowboy this slightly, like so: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,
LoadViewStateandSaveViewState. The first takes anobjectof viewstate to inflate and the other returns that sameobjectstructure. If you make yourSaveViewStateoverride return something containing the structure you need to save all the important properties that need persisting, then you just inflate it again in yourLoadViewStatemethod.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.Tripletand our old friendsSystem.Collections.ArrayListandSystem.Collections.Hashtable. Pairs and Triplets simply store two or three values of typeobject; ArrayLists are effectively aList<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
Textproperty, so myLoadViewStateandSaveViewStatemethods looked like this: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
ArrayListandHashtable, rather thanList<T>andDictionary<TKey, TValue>) and the second is a good in-depth explanation of how all this viewstate stuff actually works.I hope all this helps resolve your problem.