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 (2)

  1. art_scott@msn.com says:

    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