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
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 With
If oGotOne Is Nothing Then
Me.BadOne(50) ' no match: penalty
oGotOne.Visibility = Windows.Visibility.Hidden
Me.nScore += Canvas.GetLeft(oGotOne) / 10 ' higher score for rightmost
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))
Private Sub oTimer_TickMainThread()
Me.nTicks += 1
Dim nSecs = Me.nTicks * Me.nInterval / 1000
If nSecs > Me.nMaxSecs Then ' out of time?
oTimer.Stop()
Me.aLets(i).Visibility = Windows.Visibility.Hidden
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)
MsgBox("Your Score: " + Me.nScore.ToString + " High Score = " + Me.nHighScore.ToString)
Me.nScore = 0 ' restart
Me.nTicks = 0
Me.Title = Me.nScore.ToString + " " + Int(Me.nMaxSecs - nSecs).ToString
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)
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
Sub BadOne(ByVal nHowBad As Integer)
Me.nScore = Math.Max(0, Me.nScore - nHowBad)
Beep(2000, 20)
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 Class