Click here to Skip to main content
13,866,923 members
Rate this:
 
Please Sign up or sign in to vote.
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:
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:

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 14:03pm
v7
Rate this: bad
 
good
Please Sign up or sign in to vote.

Solution 1

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
   
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
Gabriel X 10-Jul-11 18:24pm
   
Well, I'm surprised of your concern. Nothing can say but a big thanks to Espen and SAKryukov!
(Sorry about the "Add new solution". I didn't know that.)

First off, I'm gonna try SAKryukov's advise about invocation issues. But you need to know that my real codes are a bit different than shown up there. I used Button_click to show it to you in a simple way.

But actually, in my own solution, there is a LoadPage sub that Starts Thread1.

Another time I need to mention that I am a newbie, and didn't know anything about threading before here. That's why I need to learn it from scratch.

To Espen, Maybe SendKeys fixes the problem easily. But as we have engaged in threading issues, let's go on with them. I appreciate your advise though.

To SA, sorry about the strange invoking way, I just used what I read in microsoft. But, the whole idea od invoking, threading and delegates including your links are still complicated to my brain. I will put my code-changes tomorrow morning.

Best Regards
-Gabriel
   
Just a warning: I do not recommend using SendKey for UI work-around. It might work but not a clean solution. Adding a character is simple: read Text, append a character to is, assign result to Text. Better performance will be if you use System.TexStringBuilder and append only to it: System.TexStringBuilder.Append, than assign Text to string builder's ToString(). Cyclic append to string is of very poor performance as strings are immutable (you copy whole thing back and forth). Keep instance of string builder on your non-UI thread, do assignment of text box's Text in Invoke.
--SA
Gabriel X 12-Jul-11 3:17am
   
Gabriel X - 11 hrs ago
Hi, I just updated the code. Got rid of Buttons and Auto-gen names, Used a better invoking way, But I still need a more specific help about managing ThreadStatus. And I think there's no need for StringBuilder as TextBox1 has a 999 length limitation in my project.
Gabriel X 12-Jul-11 13:47pm
   
I think there's something wrong with the notification of this site, or maybe I put my comment in a wrong place?!
Ramalinga Koushik 11-Jul-11 0:28am
   
Excellent.+5. Bookmarked for future reference.
   
Thank you, Ramalinga.
--SA
Ramalinga Koushik 12-Jul-11 0:05am
   
It's my Pleasure SA.
Gabriel X 11-Jul-11 15:51pm
   
Hi,
I just updated the code.
Got rid of Buttons and Auto-gen names,
Used a better invoking way,
But I still need a more specific help about managing ThreadStatus.
And I think there's no need for StringBuilder as TextBox1 has a 999 length limitation in my project.
Sergey Alexandrovich Kryukov 12-Jul-11 16:04pm
   
In this case you need almost nothing. Create instance of WaitHandle = System.Threading.ManualResetEvent. Create it unset. In the thread, before each loop iteration call WaitHandle.WaitOne(). In UI thread, call WaitHandle.Set to allow thread to run, WaitHandle.Reset to make it sleep at the WaitOne() call.
That's the simplest schema I can think of. Thread.Pause is deprecated an unsafe.

Is it clear now?
--SA
Gabriel X 12-Jul-11 19:10pm
   
As clear as an azure sky of deepest summer, brother sir!
Thanks for the solution dear SAKryukov. I'm going to Put your name in my blog when I finished working on it.

Now please take a look at the new code to check if anything is ok.

-Best Regards, Gabriel
www.GabrielXSoft.wordpress.com
Gabriel X 13-Jul-11 9:59am
   
Everything's OK? Should I use it now?
Sergey Alexandrovich Kryukov 18-Jul-11 21:31pm
   
Yes, you can try it now, all should work.
Good luck, call again.
--SA
Gabriel X 22-Jul-11 10:52am
   
Thnx.
Rate this: bad
 
good
Please Sign up or sign in to vote.

Solution 3

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
   
Comments
thatraja 10-Jul-11 14:51pm
   
Classic way since VB6. 5!
Espen Harlinn 10-Jul-11 15:26pm
   
Thank you, thatraja
Ramalinga Koushik 11-Jul-11 0:29am
   
Good Call. My 5 too.
Espen Harlinn 11-Jul-11 12:42pm
   
Thank you, Ramalinga
Ramalinga Koushik 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)

  Print Answers RSS
Top Experts
Last 24hrsThis month


Advertise | Privacy | Cookies | Terms of Service
Web01 | 2.8.190214.1 | Last Updated 12 Jul 2011
Copyright © CodeProject, 1999-2019
All Rights Reserved.
Layout: fixed | fluid

CodeProject, 503-250 Ferrand Drive Toronto Ontario, M3C 3G8 Canada +1 416-849-8900 x 100