Wednesday, May 18, 2011

Integrated Quiz WebApp with Graphical Score Report

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:




Which of the fol....?





<




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 ( ...)
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:

///
/// Retrive Question and its Choices from
/// XML file and render it to screen
///

/// 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 methodAfter that, the user reviews answers history and is allowed to go back or view score report Review():

///
/// generate Review Report
/// by retrive user answers from historyALst ArrayList
/// and Bookmarked Question(s) from bookmarkLst ArrayList
/// and do user descion
///

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 methodIf the user decides to go back, then ReAnswer():


///
/// generate a list of bookmarked questions
/// and fire ShowQuestion() for first one
/// according to some rules
///

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 methodIn the end, call the Score report: ShowResult()

///
/// generate Score Report
/// by some simple calculations
///

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()
+":"
+spentTime.Minutes.ToString()
+":"
+spentTime.Seconds.ToString()
+"Hr(s).";


//Success or ...!
if((score*100/totalQNumber)<50)
{
gradeLbl.ForeColor=Color.Red;
gradeLbl.Text="Failed";
}
else
{
gradeLbl.ForeColor=Color.Green;
gradeLbl.Text="Success";
}
}//end methodDone ..........!

No comments:

Post a Comment