如何逐个字符附加文本? [英] How to Append Text character by character?

查看:101
本文介绍了如何逐个字符附加文本?的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述


我希望有一个TextBox可以按字符显示某个字符串,就像有人在为您键入文本一样.我已经做到了,而且行得通!

但是问题在于文本框变大后的性能".当FontSize大于26pt和/或文本变粗时,在添加文本时,文本会闪烁严重.这是我的操作方式:

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!"



我在表单中添加了一个计时器,
并将这些行放入timer_tick子目录中:



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



我尝试使用Label代替TextBox,并且它从未闪烁过!
但是由于Labels没有编辑能力,所以我必须使用TextBox.

您对此文本框的闪烁有解决方案吗?
我发现了有关ThreadSafe Appending的一些知识,但是作为一个新手,我不知道这是怎么回事.请帮忙!



SA,您好!

感谢您的答复.我从Microsoft页面上阅读了 Threading 问题.我在项目中使用了它,并给我带来了一个错误,该错误已根据



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!



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



我在哪里出错了?
我还有什么需要做的吗?

另一个问题:
如果我在AppTxtThread中使用Form2.CheckBox2.Checked,它将始终提供True值(这是它的Form_Loaded主值),无论您是否在非线程函数中进行了更改.

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



好吧,我写了一个更新的代码,但是有些地方我无法通过谷歌搜索来处理:

-ManualResetEvent,我读到了有关它的信息,但是如何实现呢?它只有五个类,包括WaitAny.它如何暂停/恢复或重新启动线程?我现在使用了一种不安全的方法来临时挂起线程.

-StringBuilder,如何将其分配给Textbox.Text?我添加了对文本的绑定,但是在SB.Append()上TextBox中什么也没有发生,因此我现在必须使用=函数(我知道这不好!).

-线程1是否需要IsBackground?


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



好的,我使用ManualResetEvent暂停/播放线程.
我发现了
这篇文章



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

推荐答案

您在尝试什么将永远行不通.您可能不了解表单中更新和渲染中事件的时间和顺序.您只是在考虑一个线程.通过执行任何冗长的操作,您将仅阻塞主应用程序消息传递周期,因此将仅阻塞具有更改属性的视图的呈现.

通过创建其他线程,只能以一种好的方法来实现所需的效果.我不考虑其他选择-使用计时器会带来更多麻烦,通常不建议使用.
(请参阅我过去关于该主题的答案.)

该线程应通过将文本读入字符串,在字符串中附加字符,然后将修改后的文本写回到文本框的Text属性,从而将文本附加到文本框.在循环中以所需的延迟插入System.Threading.Thread.Sleep.

问题是您不能从非UI线程调用任何UI方法和属性.相反,您需要使用System.Windows.Threading.DispatcherInvokeBeginInvoke方法(对于Forms或WPF)或System.Windows.Forms.Control(仅对于Forms).

在我过去的答案中,您将找到有关其工作原理的详细说明和代码示例:
Control.Invoke()与Control.BeginInvoke() [ ^ ],
Treeview Scanner和MD5的问题 [如何获取keydown事件在vb.net中的不同线程上操作 [启用禁用+多线程后控件事件不会触发 [ ^ ].



该代码有很多问题.

为什么要通过按钮创建线程?如果再次按下该按钮会怎样?您可以从应用程序运行时的开始就创建线程,并仅使用按钮启动线程.您什么时候要停止线程?除了按钮,您还可以具有一个复选框(选中-开始-取消选中-暂停).您不能使用不推荐使用的Thread.Pause,这是不安全的.您可以通过限制线程访问EventWaitHandle.WaitOne来限制线程.通过在UI代码中设置/重置同一句柄的实例,您可以在此调用时阻止线程并再次唤醒.在手动模式下使用此手柄(使用System.Threading.ManualResetEvent).

在您的Invoke代码中,您不会调用任何东西. invoke的第一个参数应该是委托附加一个字符.取而代之的是,您在某个奇怪的方法中附加了一个字符,该方法看起来像单击或其他操作的事件处理程序.您想附加线程中的字符.它应该是带有Sleep的循环,并由Invoke附加动作.

摆脱Button2_Click,此代码应在线程主体中.

关于Form2.CheckBox2.Checked:您无法在线程中检查它,或者应该通过Invoke进行检查.我已经解释过了:您无法从非UI线程访问UI中的任何内容.阅读我所有的链接并了解调用的作用.

顺便说一下,用下划线摆脱所有自动生成的名称.它们违反命名约定并且不提供信息.您应该在使用它们时将它们全部重命名.您认为为谁提供了重构引擎?此外,您不会显示将委托添加到事件的调用列表中.我了解,您是通过Designer完成的.我的建议:在您的代码中手动执行并显示此代码,否则不清楚是什么处理.

请不要使用添加新解决方案",如果这不是解决方案.没有人收到通知,它将被删除.您应该在解决方案中使用添加评论".至于代码,请使用改进问题"将其添加到问题中.

—SA
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[^].



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


除了SAKryukov撰写的内容外,您可能还对SendKeys.Send [
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


这篇关于如何逐个字符附加文本?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆