Option Strict On
Imports Microsoft.VisualBasic
Imports System.ComponentModel
Imports System.ComponentModel.Design
Imports System.Web.UI
Imports System.Web.UI.Design
Namespace MikesControls
' The following declaration uses the new CompositeControl abstract class
' in 2.0, which gives you the following for free: INamingContainer,
' EnsureChildControls, and design-time behavior.
Public Class QuizControl : Inherits CompositeControl
Private textAnswer As TextBox
Private labelQuizText As Label
Private layoutTable As Table
Private _answerValue As Integer
Enum OperatorEnum
Add
Subtract
Multiply
Random
' No divide :-)
End Enum
Enum QuizLayoutEnum
Horizontal
Vertical
End Enum
Protected Overrides Sub CreateChildControls()
Me.Controls.Clear()
textAnswer = New TextBox()
textAnswer.ID = "textAnswer"
textAnswer.Width = New Unit("30px")
labelQuizText = New Label()
labelQuizText.ID = "labelQuizText"
labelQuizText.ForeColor = Me.ForeColor
labelQuizText.Font.Bold = Me.Font.Bold
labelQuizText.Font.Size = Me.Font.Size
labelQuizText.Font.Names = Me.Font.Names
If Page.IsPostBack = False Then
labelQuizText.Text = Me.Question()
ViewState("question").ToString()
Else
labelQuizText.Text = ViewState("question").ToString()
End If
' This table does the layout
Dim row1 As New TableRow
Dim row2 As New TableRow
Dim cell1 As New TableCell
Dim cell2 As New TableCell
Dim table1 As New Table
table1.BackColor = Me.BackColor
table1.CellPadding = Me.CellPadding
cell1.Controls.Add(labelQuizText)
cell2.Controls.Add(textAnswer)
row1.Cells.Add(cell1)
table1.Rows.Add(row1)
If Me.QuizLayout = QuizLayoutEnum.Horizontal Then
row1.Cells.Add(cell2)
Else
row2.Cells.Add(cell2)
table1.Rows.Add(row2)
End If
Me.Controls.Add(table1)
End Sub
Public Overrides ReadOnly Property Controls() As System.Web.UI.ControlCollection
Get
EnsureChildControls()
Return MyBase.Controls
End Get
End Property
<Browsable(True)> Public Property QuizLayout() As QuizLayoutEnum
Get
If ViewState("quizLayout") Is Nothing Then
Return QuizLayoutEnum.Horizontal
Else
Return CType(ViewState("quizLayout"), QuizLayoutEnum)
End If
End Get
Set(ByVal value As QuizLayoutEnum)
ViewState("quizLayout") = value
End Set
End Property
<Browsable(True)> Public Property OperatorType() As OperatorEnum
Get
If ViewState("operatorType") Is Nothing Then
Return OperatorEnum.Add
Else
Return CType(ViewState("operatorType"), OperatorEnum)
End If
End Get
Set(ByVal value As OperatorEnum)
ViewState("operatorType") = value
End Set
End Property
<Browsable(True), DefaultValue(2)> Public Property CellPadding() As Integer
Get
If ViewState("CellPadding") Is Nothing Then
Return 2
Else
Return CInt(ViewState("CellPadding"))
End If
End Get
Set(ByVal value As Integer)
ViewState("CellPadding") = value
End Set
End Property
<Browsable(True), DefaultValue(12)> Public Property MaxValue() As Integer
Get
If ViewState("MaxValue") Is Nothing Then
Return 12
Else
Return CInt(ViewState("MaxValue"))
End If
End Get
Set(ByVal value As Integer)
ViewState("MaxValue") = value
End Set
End Property
<Browsable(True), DefaultValue(10)> Public Property MaxMultiplyValue() As Integer
Get
If ViewState("MaxMultiplyValue") Is Nothing Then
Return 10
Else
Return CInt(ViewState("MaxMultiplyValue"))
End If
End Get
Set(ByVal value As Integer)
ViewState("MaxMultiplyValue") = value
End Set
End Property
<Browsable(True), DefaultValue(1)> Public Property MinValue() As Integer
Get
If ViewState("MinValue") Is Nothing Then
Return 1
Else
Return CInt(ViewState("MinValue"))
End If
End Get
Set(ByVal value As Integer)
ViewState("MinValue") = value
End Set
End Property
Public ReadOnly Property IsValid() As Boolean
Get
Dim userValue As String
Dim tb As TextBox = CType(FindControl("textAnswer"), TextBox)
If tb Is Nothing Then
userValue = ""
Else
userValue = tb.Text
End If
' Convert known answer to string for comparison
If userValue = ViewState("answerValue").ToString() Then
Return True
Else
Return False
End If
End Get
End Property
Public Property QuestionFormat() As String
Get
Dim questionFormatString As String
If ViewState("questionFormatString") Is Nothing Then
questionFormatString = "What is {0} {2} {1}?"
Else
questionFormatString = ViewState("questionFormatString").ToString()
If questionFormatString = "" Then
questionFormatString = "What is {0} {2} {1}?"
End If
End If
Return questionFormatString
End Get
Set(ByVal value As String)
ViewState("questionFormatString") = value
End Set
End Property
<Browsable(True)> Public ReadOnly Property Question() As String
Get
Dim questionString As String
If ViewState("question") Is Nothing Then
questionString = CreateQuestion()
ViewState("question") = questionString
' The following is required to force the value to be stored in viewstate.
ViewState.SetItemDirty("question", True)
Return questionString
Else
Return CType(ViewState("question"), String)
End If
End Get
End Property
Public Sub Reset()
Dim question As String = CreateQuestion()
' I'm not sure if it's kosher to do this -- I'm putting the
' question directly into viewstate. The read-only Question property
' returns it to whoever asks for it.
ViewState("question") = question
ViewState.SetItemDirty("question", True)
' The controls have already been created during EnsureChildControls, so
' they have to be updated with new question information now.
CreateChildControls()
Dim tb As TextBox
tb = CType(FindControl("textAnswer"), TextBox)
tb.Text = ""
Dim l As Label
l = CType(FindControl("labelQuizText"), Label)
l.Text = question
End Sub
Protected Function CreateQuestion() As String
Dim num1, num2 As Integer
' This creates an integer based on the ASCII values of the characters in the
' control's ID (which of course must be unique on the page). By plugging this value into
' the Random() constructor, we get different random numbers for each control on the
' page. Relying on Ticks alone gets the same random numbers for all instances of the
' control on the same page. I forget where I got this idea, but probably from someone's
' blog, is a good guess. (Apologies to that person for not being able to credit them here.)
Dim idSum As Integer = 0
For i As Integer = 0 To Me.ID.Length - 1
idSum += Asc(Me.ID.Substring(i, 1))
Next
Dim rnd As New Random(CType(System.DateTime.Now.Ticks Mod (System.Int32.MaxValue - idSum), Integer))
' Create the two numbers for the quiz. If this is a multiplication quiz, the numbers will
' be recreated based on the separate min/max for multiplication.
num1 = rnd.Next(Me.MinValue, Me.MaxValue)
num2 = rnd.Next(Me.MinValue, Me.MaxValue)
Dim questionReturn As String = ""
Select Case Me.OperatorType
Case OperatorEnum.Add
_answerValue = num1 + num2
questionReturn = String.Format(Me.QuestionFormat, num1, num2, "+")
Case OperatorEnum.Multiply
questionReturn = CreateMultiplyQuestion()
Case OperatorEnum.Subtract
' Do a swap so the highest number is first
If num1 < num2 Then
Dim temp As Integer = num1
num1 = num2
num2 = temp
End If
_answerValue = num1 - num2
questionReturn = String.Format(Me.QuestionFormat, num1, num2, "-")
Case OperatorEnum.Random
' I don't want to explicitly (re)set the current value of the
' OperatorType property (e.g., by doing this logic first and then
' setting Me.OperatorType before dropping into the big
' Select statement.) The property value should remain the same as
' what the developer has set it to.
Dim allOperators As String = "*-+"
Dim randomOperator As String = allOperators.Substring(rnd.Next(0, allOperators.Length), 1)
Select Case randomOperator
Case "+"
_answerValue = num1 + num2
questionReturn = String.Format(Me.QuestionFormat, num1, num2, "+")
Case "-"
' Do a swap so the highest number is first
If num1 < num2 Then
Dim temp As Integer = num1
num1 = num2
num2 = temp
End If
_answerValue = num1 - num2
questionReturn = String.Format(Me.QuestionFormat, num1, num2, "-")
Case "*"
questionReturn = CreateMultiplyQuestion()
End Select
Case Else
_answerValue = num1 + num2
questionReturn = String.Format(Me.QuestionFormat, num1, num2, "+")
End Select
ViewState("answerValue") = _answerValue
Return questionReturn
End Function
Function CreateMultiplyQuestion() As String
' Note! This resets the num1 and num2 values to accommodate maxumum
' multplier property settings.
Dim num1, num2 As Integer
Dim rnd As New Random(CType(System.DateTime.Now.Ticks Mod System.Int32.MaxValue, Integer))
If Me.MaxMultiplyValue >= 0 Then
num1 = rnd.Next(Me.MinValue, Me.MaxMultiplyValue)
num2 = rnd.Next(Me.MinValue, Me.MaxMultiplyValue)
Else
num1 = rnd.Next(Me.MinValue, Me.MaxValue)
num2 = rnd.Next(Me.MinValue, Me.MaxValue)
End If
_answerValue = num1 * num2
Return String.Format(Me.QuestionFormat, num1, num2, "*")
End Function
End Class
End Namespace
Colorized by: CarlosAg.CodeColorizer