Integrated Quiz WebApp with Graphical Score Report






4.13/5 (19 votes)
Integrated online quiz app with graphical score report. Users can bookmark questions in order to review in the end.
Background
The quiz application demonstrated here allows users to proceed with multi-choice questions like in an IQ Quiz. The database is a simple XML file. The application is a single web user control. The data holder between the pages is the Session bag (Viewstate[ data ]
).There are three panels within our quiz user control. They are Quiz panel, Review panel and Score Report panel. I have used my own progress bar control within the Score Report...
Database
Our database is a simple XML file:
<?xml version="1.0" encoding="utf-8" ?>
<quiz>
<mc>
<question correctAnsNo='1'>Which of the fol....?
</question>
<answer correct='No' ><![CDATA[ <img src="x...]]>
</answer>
<answer correct='No' ><![CDATA[ <img src="x...]]>
</answer>
<answer correct='Yes'><![CDATA[ <img src="x...]]>
</answer>
<answer correct='No' ><![CDATA[ I Don't Know ]]>
</answer>
</mc>
</quiz>
Using the code
Our quiz user control has four methods:
ShowQuestion(int _qNo)
Review()
ReAnswer()
ShowResult()
The code
First, there are some common member variables:
//create xmlDocument holder
XmlDocument doc=new XmlDocument();
//create empty navigator in order to navigate
//through our XMLDocument
XPathNavigator nav;
//to loop through
XPathNodeIterator nodeItrator;
//hold user Answers
ArrayList historyALst=new ArrayList();
//hold bookmarked Question(s)
ArrayList bookmarkLst=new ArrayList();
//used to replace index 1 by A and 2 by B ....etc
char[] perfix=new char[10] {'A','B','C','D','F','G','H','J','K','L'};
int currentQNo; //current question number
int totalQNumber; // total quiz question number
int totalMarkedQuestion; // total marked questions number
int currentMarkedQuestion; //current bookmarked question number
int markLoop; // the res number of reanswered bookmarked questions
DateTime startTime; // quiz start time
TimeSpan spentTime;
bool isReview; // to indicate the status of current qustion
bool wasChecked=false; //signed as marked Q within Review report.
In the page_load(....)
, do the following:
private void Page_Load(object sender, System.EventArgs e)
{
//load our XML DB
doc.Load(Server.MapPath("quiz.xml"));
//create navigator for loaded xml File
nav=doc.CreateNavigator();
//Is New Quiz?.
if(!IsPostBack)
{
//Yes.
quizPanal.Visible=true;// show quiz screen.
//record start time
startTime=DateTime.Now;
currentQNo=1;
//count and record Total questions Number
//using our XmlDocumentnavigator by passing
//XPath sring of question node ( <mc>...</mc>)
totalQNumber=nav.Select("//mc").Count;
//store our data in Session bag
//simply,I use the same name for Sessionbag object
//and member Field..
ViewState["startTime"]=startTime;
ViewState["totalQNumber"]=totalQNumber;
ViewState["currentQNo"]=currentQNo;
ViewState["score"]=0;
//go to first Question
ShowQuestion(currentQNo);
}
}
Let's render question number as currentQNo : ShowQuestion(currentQNo);
Render a specific multi-choice question. This method takes question number as parameter, and uses this number within different XPath strings to navigate through our XML nodes:
/// <summary>
/// Retrive Question and its Choices from
/// XML file and render it to screen
/// </summary>
/// <param name="_qNo">Question Numberv/param>
public void ShowQuestion(int _qNo)
{
quizPanal.Visible=true;
//clear any selected radioList item
answersRBL.Items.Clear();
//retrieve Data from session bag and calculate spent time
startTime=(DateTime)ViewState["startTime"];
totalQNumber=Convert.ToInt32(ViewState["totalQNumber"]);
spentTime=DateTime.Now.Subtract(startTime);
//XPath String of current question node
//quiz/mc[current question number]
string xPath="quiz/mc["+_qNo.ToString()+"]";
int counter=1;
string str;
//read current Q Text by by using Select() method
//of our navigator by set the XPath string
//as address of current Question text Node:
//quiz/mc[current question number]/question
nodeItrator=nav.Select(xPath+"/question");
nodeItrator.MoveNext();
//display some data
qNoLbl.Text="Q: "+_qNo.ToString()+" / "+totalQNumber.ToString();
questionLbl.Text=_qNo.ToString()+": "+nodeItrator.Current.Value;
spentTimeLbl.Text=spentTime.Minutes.ToString()
+"~"+spentTime.Seconds.ToString();
//answer nodes Xpath string quiz/mc[current question number]/answer
nodeItrator=nav.Select(xPath+"/answer");
while(nodeItrator.MoveNext())
{
str=counter.ToString();
//add new radioBtn to Answers RadioList with perfix A, B..
//with text o the current text node
answersRBL.Items.Add(new ListItem(perfix[counter-1].ToString()
+":"+nodeItrator.Current.Value,str));
//keep correct answer in mind
if(nodeItrator.Current.GetAttribute("correct","")=="Yes")
{
ViewState["correctAnswer"]=counter;
}
counter++;//next
}
There are some actions that take place when this method renders bookmarked questions:
//retrieve current status from session bag
isReview=Convert.ToBoolean(ViewState["isReview"]);
//Is Review Bookmarked questions Only?
if(isReview)
{//yes
//disable bookmark checkbox
bookmarkCKBX.Enabled=false;
//dispaly an image to notify user
Image1.Visible=true;
qNoLbl.ForeColor=Color.Red;
RequiredFieldValidator1.Text="Answer The Question !";
}
currentQNo++;//next
ViewState["currentQNo"]=currentQNo;
}//end method
After that, the user reviews answers history and is allowed to go back or view score report Review()
:
/// <summary>
/// generate Review Report
/// by retrive user answers from historyALst ArrayList
/// and Bookmarked Question(s) from bookmarkLst ArrayList
/// and do user descion
/// </summary>
public void Review()
{
revisionPanal.Visible=true;
int seqance=0;
string _is="No";
//retrieve marked Questions and answers List
bookmarkLst=(ArrayList)ViewState["bookmarkLst"];
historyALst=(ArrayList)ViewState["historyALst"];
totalQNumber=Convert.ToInt32(ViewState["totalQNumber"]);
wasChecked=Convert.ToBoolean(ViewState["wasChecked"]);
//are you marked some questions for review ?
if(!wasChecked){ /* No */ markBtn.Enabled=false;}
//create Answers History report
//
//table header
TableCell c=new TableCell();
TableCell cc=new TableCell();
TableCell ccc=new TableCell();
c.Text="Question No.";
cc.Text="Your Answer";
ccc.Text="Bookmark ?";
TableRow hr=new TableRow();
hr.Cells.Add(c);
hr.Cells.Add(cc);
hr.Cells.Add(ccc);
hr.ForeColor=Color.White;
hr.BackColor=Color.LightSlateGray;
Table1.Rows.Add(hr);
Table1.Font.Bold=true;
//your records
for(int i=1;i<=totalQNumber;i++)
{
//Cell: Q Number
TableCell c1=new TableCell();
c1.Text=i.ToString();
//Cell: your answer A,B, or C...
TableCell c2=new TableCell();
c2.HorizontalAlign=HorizontalAlign.Center;
c2.Text=perfix[Convert.ToInt32(historyALst[i-1])-1].ToString();
//Cell: bookmarked or not
TableCell c3=new TableCell();
for(int n=0;n < bookmarkLst.Count;n++)
{ //Is this Q bookmarked ?
if(Convert.ToInt32(bookmarkLst[n].ToString())==i)
{//Yes
_is=" Yes ";
}
}
c3.Text=_is;
c3.HorizontalAlign=HorizontalAlign.Center;//align=center
_is=" No ";//Re-Assign
TableRow r=new TableRow();
r.Cells.Add(c1);
r.Cells.Add(c2);
r.Cells.Add(c3);
r.ForeColor=Color.SlateGray;
//
//Just, Table template
if(seqance % 2 != 0)
{
r.BackColor=Color.Gainsboro;
}
//display it
Table1.Rows.Add(r);
eqance++;//next ..
}//end for
}//end method
If the user decides to go back, then ReAnswer()
:
/// <summary>
/// generate a list of bookmarked questions
/// and fire ShowQuestion() for first one
/// according to some rules
/// </summary>
public void ReAnswer()
{
quizPanal.Visible=true;
isReview=true;
//retrieve data
bookmarkLst=(ArrayList)ViewState["bookmarkLst"];
totalMarkedQuestion=bookmarkLst.Count;
currentMarkedQuestion=Convert.ToInt32(bookmarkLst[0]);
//store data
markLoop=1;
ViewState["markLoop"]=markLoop;
ViewState["isReview"]=isReview;
ViewState["totalMarkedQuestion"]=totalMarkedQuestion;
ViewState["currentMarkedQuestion"]=currentMarkedQuestion;
//Show first marked question
ShowQuestion(currentMarkedQuestion);
}//end method
In the end, call the Score report: ShowResult()
/// <summary>
/// generate Score Report
/// by some simple calculations
/// </summary>
public void ShowResult()
{
resultPanal.Visible=true;
//retrieve data
int score=Convert.ToInt32(ViewState["score"]);
totalQNumber=Convert.ToInt32(ViewState["totalQNumber"]);
startTime=(DateTime)ViewState["startTime"];
spentTime=DateTime.Now.Subtract(startTime);
//set own Progress Bar Max. Value
progressBar.Total=totalQNumber*100;
//set your score as progressed value
progressBar.Value=score*100;
//display some data:
//
//date
dateLbl.Text=DateTime.Now.ToUniversalTime().ToShortDateString();
//total Q number
totalQNoREsLbl.Text=totalQNumber.ToString();
//Total Score (as Total Question number * 100 )
totalScoreLbl.Text=(totalQNumber*100).ToString();
//Your Score (as your correct answers * 100)
scoreLbl.Text=(score*100).ToString();
//number of correct answers
correctAnswersLbl.Text=score.ToString();
//passing score
passingScoreLbl.Text=(totalQNumber*100/2).ToString();
//Time Spent
timeTakenLbl.Text=spentTime.Hours.ToString()
+"<font color=\"#666699\">:</font>"
+spentTime.Minutes.ToString()
+"<font color=\"#666699\">:</font>"
+spentTime.Seconds.ToString()
+"<font color=\"#666699\" size=\"1\">Hr(s).</font>";
//Success or ...!
if((score*100/totalQNumber)<50)
{
gradeLbl.ForeColor=Color.Red;
gradeLbl.Text="Failed";
}
else
{
gradeLbl.ForeColor=Color.Green;
gradeLbl.Text="Success";
}
}//end method
Done ..........!