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