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