Click here to Skip to main content
15,884,176 members
Please Sign up or sign in to vote.
0.00/5 (No votes)
See more:
Hi,
I wanted to have a TextBox which shows a certain string character by character, like somebody is typing the text for you. I have done it and it works!

But the problem is about its 'performance' after the Textbox becomes large. The text becomes blinking badly while appending text when the FontSize is more than 26pt and/or when the text becomes heavy. Here's the way I did it:

Dim Counter as integer = 0
Dim String1 as string = "This is typed character-by-character!"


I added a timer in my form,
and put these lines into the timer_tick sub:

If Not Counter > String1.Length - 1 Then
  TextBox1.AppendText(String1(Counter))
  Counter += 1
Else
  Timer1.Enabled = False
  Counter = 0
End If


I tried using a Label instead of a TextBox, and it never blinks at all!
But since Labels have not the editing ability, I have to use a TextBox.

Do you have a solution for the blinking of this TextBox?
I found something about ThreadSafe Appending, but as a newbie, I don't know what's that all about. Please Help!

[EDIT -- in response to solution by SA]

Hello SA!

Thanks for your response. I read the Threading issue from the microsoft page. I used it in my project and it brought me an error that I fixed according to this ThreadSafe msdn page.

Now I have removed the timer and used a Thread instead.
But the result doesn't seem to be much better than previous non-threading way.

Here is the new code:
VB
Imports System.Threading
Public Class Form1
    Delegate Sub AppTextCallback(ByVal [text] As String)
    Dim Thread1 As Thread
    Dim Counter As Short
    Dim String1 As String = "This string is typed character by character. It is just fine in the beginning of the text, but as the string becomes longer and longer, then, the blinking issues appear to the viewer! This string is typed character by character. It is just fine in the beginning of the text, but as the string becomes longer and longer, then, the blinking issues appear to the viewer!"

    Private Sub AppTxtThread()
        While (Counter < String1.Length)
            AppText(String1(Counter))
            Thread.Sleep(100)
            Counter += 1
        End While
    End Sub


    Private Sub AppText(ByVal [text] As String)
        If TextBox1.InvokeRequired Then
            Dim d As New AppTextCallback(AddressOf AppText)
            Me.Invoke(d, New Object() {[text]})
        Else
            TextBox1.Select()
            TextBox1.AppendText([text])
            TextBox1.SelectionStart = TextBox1.Text.Length
        End If
    End Sub


    Private Sub Button1_Click(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles Button1.Click
        'ThreadSafe Method
        Thread1 = New Thread(AddressOf AppTxtThread)
        Thread1.IsBackground = True
        Thread1.Start()
    End Sub


    Private Sub Button2_Click(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles Button2.Click
        'Regular Method
        Counter = 0
        TextBox1.Select()
        While (Counter < String1.Length)
            TextBox1.AppendText(String1(Counter))
            TextBox1.SelectionStart = TextBox1.Text.Length
            Thread.Sleep(100)
            Counter += 1
        End While
    End Sub
End Class


Am I gone wrong somewhere?
Is there anything else I should do?

Yet another problem:
if I use Form2.CheckBox2.Checked in the AppTxtThread it always gives True value (which is its Form_Loaded primary value), no matter you've changed it in a non-thread function or not.

- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -

[ EDITS: UPDATING CODE, USING PROPER INVOKING WAY; USING STRING BUILDER ]

Well, I wrote a newer code, but there are some parts that I couldn't handle via googling:

- ManualResetEvent, I read about it, but how do I implement it? It has just five classes including WaitAny. How does it pauses/resumes or restarts threads? I have now used an un-safe way to suspend the thread temporarily.

- StringBuilder, how to assign it to the Textbox.Text? I added binding to the Text, but on SB.Append() nothing happens in the TextBox, that's why I had to use = funcion for now (I know this is not good!).

- Is IsBackground necessary for Thread1?


- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -

[ EDITS: UPDATING CODE, USING WAIT HNDLERS ]

Okay, I used a ManualResetEvent to Pause/Play the Thread.
I found this article and this one useful for the case.


Here's the new Code:

VB
Imports System.Threading
Public Class Form1
    Public Delegate Sub AppendDelegate(ByVal aString As Char)
    Public Delegate Sub CheckSetDelegate(ByVal Status As Boolean)
    Dim Thread1 As Thread
    Dim C As Short
    Dim String1 As String = "This string is typed using threads. How is the performance?"
    Dim WaitHandle As ManualResetEvent
    
    Private Sub Form1_Load(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles MyBase.Load
        Thread1 = New Thread(AddressOf AppTxtThread)
        'Thread1.IsBackground = True
        WaitHandle = New ManualResetEvent(False)
    End Sub


    Private Sub AppTxtThread()
        While (C < String1.Length)
            WaitHandle.WaitOne()
            TextBox1.Invoke(New AppendDelegate(AddressOf AppendTxt), String1(C))
            C += 1
            Thread.Sleep(100)
        End While
        CheckBox1.Invoke(New CheckSetDelegate(AddressOf CheckboxSet), False)
        C = 0
    End Sub


    Private Sub AppendTxt(ByVal Chr As Char)
        TextBox1.AppendText(Chr)
    End Sub


    Private Sub CheckboxSet(ByVal Stat As Boolean)
        If Stat = False Then
            CheckBox1.Checked = False
        Else
            CheckBox1.Checked = True
        End If
    End Sub


    Private Sub CheckToggled(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles CheckBox1.CheckedChanged
        If CheckBox1.Checked Then
            If Thread1.ThreadState = ThreadState.Unstarted Then
                Thread1.Start()
            ElseIf Thread1.ThreadState = ThreadState.Stopped Then
                Thread1 = New Thread(AddressOf AppTxtThread)
                Thread1.Start()
            End If
            WaitHandle.Set()
        Else
            WaitHandle.Reset()
        End If
    End Sub
End Class
Posted
Updated 12-Jul-11 13:03pm
v7

What are you trying will never work. You probably does not understand the timing and sequence of events in update and rendering in the form. You're thinking in terms of just one thread. By doing any lengthy operation you would simply block main application messaging cycle, so rendering of the view with changing properties will be simply blocked.

The effect you want can be achieved in only one good way, by creating additional thread. I don't consider another option — using timer which would bring much more trouble and is generally not recommended.
(See my past answer on the topic.)

This thread should append text to your text box by reading its text into string, appending a character it its string and writing modified text back to your text box's Text property. Insert System.Threading.Thread.Sleep with required delay in the loop.

The problem is that you cannot call any UI methods and properties from non-UI thread. Instead, you need to use the method Invoke or BeginInvoke of System.Windows.Threading.Dispatcher (for both Forms or WPF) or System.Windows.Forms.Control (Forms only).

You will find detailed explanation of how it works and code samples in my past answers:
Control.Invoke() vs. Control.BeginInvoke()[^],
Problem with Treeview Scanner And MD5[^].

See also more references on threading:
How to get a keydown event to operate on a different thread in vb.net[^],
Control events not firing after enable disable + multithreading[^].

[EDIT — answering a follow-up question]

The code has a lot of problems.

Why creating a thread from a button? What happens if you hit this button again? You can create thread from the very beginning of application run-time and use button only to start thread. When are you going to stop thread? Instead of button you can have a check box (check — start, un-check — pause). You cannot use deprecated Thread.Pause, which is unsafe. You can throttle thread by having it to what for a blocking call to EventWaitHandle.WaitOne. By setting/resetting the instance of the same handle in your UI code you can block thread at this call and wake up again. Use this handle in a manual mode (use System.Threading.ManualResetEvent).

In your Invoke code you do not invoke anything. First parameter of invoke should be delegate appending a character. Instead, you append a character in some strange method which looks like event handler of some click or whatever. You wanted to append characters from thread. It should be a loop with Sleep and appending action by Invoke.

Get rid of Button2_Click, this code should be in thread body.

As to Form2.CheckBox2.Checked: you cannot check it in thread, or you should do in via Invoke. I already explained it: you cannot access anything in UI from non-UI thread. Read all my links and understand what invocation does.

By the way, get rid from all those auto-generated names with underscores. They violate naming conventions and not informative. You're supposed to renamed them all as you use them. Who do you think you're given a re-factoring engine? Also, you do not show adding delegates to event's invocation list. I understand, you do it through Designer. My advice: do it manually in your code and show this code, otherwise it's not clear what handles what.

Please, never use "Add new solution" if this is not a solution. Nobody gets notification, and it will be removed. You should use "Add comment" to solution. As to the code, add it to the question using "Improve question".

—SA
 
Share this answer
 
v3
Comments
Espen Harlinn 10-Jul-11 14:03pm    
Impressive effort, my 5
Sergey Alexandrovich Kryukov 10-Jul-11 14:57pm    
Thank you, Espen.
OP did not get it right yet, let's see.
--SA
Espen Harlinn 10-Jul-11 15:35pm    
His real problem is tied to AppendText - it tends to work nicely when you append a new line, but not so well when you are extending an exitsing line ...
With SendKeys even a regular windows timer would work nicely - he is after all not controlling an external precission device :) Knowing how to perform interaction between threads is still quite useful. There is also that tip you wrote - it could easily be extended to fit his needs ...
thatraja 10-Jul-11 14:50pm    
5 for the great effort.
Sergey Alexandrovich Kryukov 10-Jul-11 14:57pm    
Thank you, Raja.
--SA
Apart from what SAKryukov writes, you might be interested in the SendKeys.Send[^] method as a way to send characters to the control. It has to be focused though.

Best regards
Espen Harlinn
 
Share this answer
 
Comments
thatraja 10-Jul-11 14:51pm    
Classic way since VB6. 5!
Espen Harlinn 10-Jul-11 15:26pm    
Thank you, thatraja
[no name] 11-Jul-11 0:29am    
Good Call. My 5 too.
Espen Harlinn 11-Jul-11 12:42pm    
Thank you, Ramalinga
[no name] 12-Jul-11 0:07am    
It's my Pleasure.

This content, along with any associated source code and files, is licensed under The Code Project Open License (CPOL)



CodeProject, 20 Bay Street, 11th Floor Toronto, Ontario, Canada M5J 2N8 +1 (416) 849-8900