A WPF version of the Typing Tutor game
In my prior post I show a Windows Form version of the typing tutor game.
Here is a WPF version.
Start Visual Studio 2008. Choose File->New->Project->VB->WPF Application. Dbl-Click the form in the designer to open the code window. Paste in the code below.
Notice how the logic differs from the WinForm version. In that version, the position of the letter object is changed by manipulating the Top and Left properties of that object. In this WPF version, Attached Properties are changed: the child objects specify values for a property that is actually in the container. The position of the letter objects ( which are instances of a class that inherits from WPF Label) is manipulated via the Canvas.SetTop and SetLeft static methods.
Class Window1
Dim WithEvents oTimer As Timers.Timer
Dim nScore As Integer = 0
Dim nTicks = 0
Dim nHighScore As Integer = 0
Dim nInterval = 100
Const MAXLETS = 10
Dim aLets(MAXLETS - 1) As MyLabel
Dim nMaxSecs = 30
Declare Function Beep Lib "kernel32" (ByVal nFreq As Int32, ByVal nDura As Int32) As Integer
Dim oRand As New Random
Dim sHighScoreFile = My.Application.Info.DirectoryPath + IO.Path.DirectorySeparatorChar + "le"
Private Sub Window1_Loaded(ByVal sender As System.Object, ByVal e As System.Windows.RoutedEventArgs) Handles MyBase.Loaded
Me.Left = 200
Me.Width = 600
Me.Height = 300
Dim oCanvas As New Canvas
Me.Content = oCanvas
For i = 0 To MAXLETS - 1
Dim oLbl = New MyLabel
oCanvas.Children.Add(oLbl)
Me.aLets(i) = oLbl
Next
If My.Computer.FileSystem.FileExists(sHighScoreFile) Then
Me.nHighScore = Int(Val(My.Computer.FileSystem.ReadAllText(sHighScoreFile)))
End If
oTimer = New Timers.Timer
oTimer.Interval = Me.nInterval
oTimer.Start()
End Sub
Private Sub Form1_KeyPress(ByVal sender As Object, ByVal e As System.Windows.Input.KeyEventArgs) Handles Me.KeyDown
If e.Key = Key.Escape Then 'escape key
Me.Close()
Else
Dim oGotOne As MyLabel = Nothing
For i = 0 To MAXLETS - 1
With Me.aLets(i)
If .Visibility = Windows.Visibility.Visible And CType(.Content, TextBlock).Text = e.Key.ToString.ToUpper Then
If oGotOne Is Nothing OrElse Canvas.GetLeft(Me.aLets(i)) < Canvas.GetLeft(oGotOne) Then ' get leftmost
oGotOne = Me.aLets(i)
End If
End If
End With
Next
If oGotOne Is Nothing Then
Me.BadOne(50) ' no match: penalty
Else
oGotOne.Visibility = Windows.Visibility.Hidden
Me.nScore += Canvas.GetLeft(oGotOne) / 10 ' higher score for rightmost
End If
End If
End Sub
Delegate Sub TimerTickMainThread()
Private Sub oTimer_Tick(ByVal sender As Object, ByVal e As System.EventArgs) Handles oTimer.Elapsed
' the timer tick occurs on a background thread: let's invoke the processing on the main (UI) thread.
Dispatcher.Invoke(Windows.Threading.DispatcherPriority.Background, New TimerTickMainThread(AddressOf oTimer_TickMainThread))
End Sub
Private Sub oTimer_TickMainThread()
Me.nTicks += 1
Dim nSecs = Me.nTicks * Me.nInterval / 1000
If nSecs > Me.nMaxSecs Then ' out of time?
oTimer.Stop()
For i = 0 To MAXLETS - 1
Me.aLets(i).Visibility = Windows.Visibility.Hidden
Next
If Me.nHighScore < Me.nScore Then
MsgBox("New High Score: " + Me.nScore.ToString + _
"! Old = " + Me.nHighScore.ToString, MsgBoxStyle.Exclamation)
Me.nHighScore = Me.nScore
My.Computer.FileSystem.WriteAllText(Me.sHighScoreFile, Me.nHighScore.ToString, 0)
Else
MsgBox("Your Score: " + Me.nScore.ToString + " High Score = " + Me.nHighScore.ToString)
End If
Me.nScore = 0 ' restart
Me.nTicks = 0
oTimer.Start()
Else
Me.Title = Me.nScore.ToString + " " + Int(Me.nMaxSecs - nSecs).ToString
For i = 0 To MAXLETS - 1
Dim oLet = Me.aLets(i)
With oLet
If .Visibility = Windows.Visibility.Visible Then
Canvas.SetLeft(oLet, Canvas.GetLeft(oLet) - .dx) '.Left -= .dx
If Canvas.GetLeft(oLet) <= 0 Then
.Visibility = Windows.Visibility.Hidden
Me.BadOne(100)
End If
Else
Dim nFactor = nSecs / Me.nMaxSecs ' 0 - 1
If oRand.NextDouble < 0.5 * nFactor Then
.dx = 1 + oRand.NextDouble * 15 * nFactor
Canvas.SetLeft(oLet, Me.Width - .dx - 10) '.Left = Me.Width - .dx - 10
CType(.Content, TextBlock).Foreground = _
New SolidColorBrush(Color.FromArgb(255, 0, 0, Me.nTicks * 100 Mod 256))
'.ForeColor = Color.FromArgb(Me.nTicks * 100 Mod 256)
Canvas.SetTop(oLet, 0.8 * (oRand.NextDouble * Me.Height)) '.Top = 0.8 * (oRand.NextDouble * Me.Height)
CType(.Content, TextBlock).Text = Chr(Int(65 + oRand.NextDouble * 26)) ' note: VB Rounds, so we need to use Int()
.Visibility = Windows.Visibility.Visible
End If
End If
End With
Next
End If
End Sub
Sub BadOne(ByVal nHowBad As Integer)
Me.nScore = Math.Max(0, Me.nScore - nHowBad)
Beep(2000, 20)
End Sub
Class MyLabel
Inherits Label
Public dx As Integer = 1
Sub New()
Me.Visibility = Windows.Visibility.Hidden
Me.Height = 32
Me.Width = 43
Dim tb As New TextBlock
tb.Visibility = Windows.Visibility.Visible
Me.Content = tb
tb.FontFamily = New FontFamily("Verdana")
tb.FontSize = 20
tb.FontWeight = FontWeights.Heavy
'Me.Font = New Font("Verdana", 20, FontStyle.Bold)
End Sub
End Class
End Class
Comments
Anonymous
July 02, 2008
PingBack from http://blog.a-foton.ru/2008/07/a-wpf-version-of-the-typing-tutor-game/Anonymous
July 02, 2008
Hi Calvin, thanks for the WPF example. Have you looked into F# yet? I'm working on using WPF, F#, XAML, TPL... Any help, examples welcome. art