【问题标题】:How can I programmatically remove text from a ReadOnly RichTextBox?如何以编程方式从 ReadOnly RichTextBox 中删除文本?
【发布时间】:2012-11-10 01:12:38
【问题描述】:

以下代码旨在在ReadOnly RichTextBox 中维护一个文本缓冲区,其中存储了最大数量的字符,并且始终保持滚动到底部。它流式传输实时日志。

但在我尝试保持最大字符数时,rtMessages.TextLength()rtMessages.SelectedText = String.Empty 之后并没有改变,因此,如果没有防御性 If 块,我最终会出现无限循环,试图重复删除缓冲区的第一行。

当我删除RichTextBoxReadOnly-ness 时,此功能成功。似乎有点奇怪,因为AppendText 成功了,但我知道选择是另一种野兽。

我可以让ReadOnly RichTextBox 可以以编程方式修改吗?

Private Sub onNewData(ByRef data As String) Handles _server.clientSentData

    ' Add new text
    rtMessages.SelectionStart = rtMessages.TextLength()
    rtMessages.AppendText(data)

    ' Delete oldest text line-by-line until the whole buffer is shorter than MAX_TEXT_LENGTH characters
    Const MAX_TEXT_LENGTH = 200
    Dim textLength = rtMessages.TextLength()
    While textLength > MAX_TEXT_LENGTH
        Dim i As Int16 = 0
        Do While rtMessages.GetLineFromCharIndex(i) < 1
            i += 1
        Loop

        rtMessages.Select()
        rtMessages.SelectionStart = 0
        rtMessages.SelectionLength = i
        rtMessages.SelectedText = String.Empty
        rtMessages.SelectionLength = 0

        If rtMessages.TextLength() = textLength Then
            rtMessages.Clear()
            rtMessages.AppendText("[buffer trimming algorithm failed]")
            Exit While
        End If

        textLength = rtMessages.TextLength()
    End While

    ' Scroll down
    rtMessages.SelectionStart = rtMessages.TextLength()
    rtMessages.ScrollToCaret()
End Sub

【问题讨论】:

  • 我可以确认这个Sub 是在 UI 线程上调用的,而不是在某些通信线程上。
  • +1 因为问题并不像看起来那么简单......我很惊讶之前没有人提到过这个。

标签: vb.net richtextbox


【解决方案1】:

虽然尝试在 ReadOnly RichTextBox 中替换 SelectedText 不起作用,但使用 SelectedRtf 确实有效:

  'rtMessages.Select()
  'rtMessages.SelectionStart = 0
  'rtMessages.SelectionLength = i
  'rtMessages.SelectedText = String.Empty
  'rtMessages.SelectionLength = 0

  rtMessages.Select(0, i)
  rtMessages.SelectedRtf = "{\rtf1\ansi}"

【讨论】:

  • 我会在缓冲区的开头有大量的{\rtf1\ansi} 代码点,还是会崩溃/被忽略?
  • @LightnessRacesinOrbit 他们将被忽略。 RTB “合并” RTF 文档。
  • 那么我们开始吧:) 谢谢
【解决方案2】:
rtMessages.SelectionLength = i - 1

应该替换为

rtMessages.SelectionLength = i

编辑#1

通过将 -1 添加到 SelectionLength,您会丢失第一行的最后一个字符。第二次运行时,第一行只有一个字符(第一次运行时错过的那个)。然后,您将尝试删除 0 的 SelectionLength,并且每隔一次运行您将获得相同的 TextLength,然后您将进入无限循环!

【讨论】:

  • 你确定吗?我在我身边重现了你的错误,改变这条线对我有用。
  • 当我更改代码时,错误仍然存​​在,直到我关闭 ReadOnly。每次迭代都给了我相同的 TextLength,除了第一次之外,不是每次迭代。
  • 对不起,我没有看到你编辑 ReadOnly。好吧,简单的解决方案是将 RTB ReadOnly 属性设置为 false,运行您的代码,然后将 ReadOnly 设置回 True。这是关于同一问题的另一篇文章:microsoftfaqs.com/msg/1440552.aspx
【解决方案3】:

您可以删除只读参数,编写您的附加文本代码,然后再次将富文本框设为只读。

【讨论】:

    【解决方案4】:

    我知道这是一个旧线程,但您可以绕过 ReadOnly 问题替换以下代码:

     rtMessages.Select()
     rtMessages.SelectionStart = 0
     rtMessages.SelectionLength = i
     rtMessages.SelectedText = String.Empty
     rtMessages.SelectionLength = 0
    

    用这个:

     rtMessages.Text = rtMessages.Text.Substring(i)
    

    我不确定这是更好还是更差的性能,但它绕过了将RichTextBox 设置为ReadOnly

    编辑:

    这是用于测试的完整代码(注意:我将代码添加到Button.Click进行测试)

    Private Sub Button1_Click(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles Button1.Click
        Static X As Integer = 0
        X += 1
    
        Dim Data As String = "Line " & X.ToString & ControlChars.NewLine
        Me.onNewData(Data)
    End Sub
    
    Private Sub onNewData(ByRef data As String)
    
        ' Add new text
        rtMessages.SelectionStart = rtMessages.TextLength()
        rtMessages.AppendText(data)
    
        ' Delete oldest text line-by-line until the whole buffer is shorter than MAX_TEXT_LENGTH characters
        Const MAX_TEXT_LENGTH As Integer = 200
        Dim textLength As Integer = rtMessages.TextLength()
        While textLength > MAX_TEXT_LENGTH
            Dim i As Integer = 0
            Do While rtMessages.GetLineFromCharIndex(i) < 1
                i += 1
            Loop
    
            'rtMessages.Select()
            'rtMessages.SelectionStart = 0
            'rtMessages.SelectionLength = i
            'rtMessages.SelectedText = String.Empty
            'rtMessages.SelectionLength = 0
    
            rtMessages.Text = rtMessages.Text.Substring(i)
    
            If rtMessages.TextLength() = textLength Then
                rtMessages.Clear()
                rtMessages.AppendText("[buffer trimming algorithm failed]")
                Exit While
            End If
    
            textLength = rtMessages.TextLength()
        End While
    
        ' Scroll down
        rtMessages.SelectionStart = rtMessages.TextLength()
        rtMessages.ScrollToCaret()
    
    End Sub
    

    【讨论】:

    • 我想在某些情况下可行的解决方法,但我的缓冲区很大;这也会破坏当前的滚动位置。
    • @LightnessRacesinOrbit ,rtMessages.SelectionStart = rtMessages.TextLength()rtMessages.ScrollToCaret() 是否没有重置滚动位置?在我的测试中,它滚动得很好。您使用什么大小的缓冲区?
    • 我现在实际上没有代码,所以这是从内存中推测出来的,但我的记忆说用新文本替换整个文本缓冲区会丢失旧的滚动位置。不是这样吗?缓冲区最大为 10KB 左右 IIRC。
    • @LightnessRacesinOrbit ,您是正确的,它将插入符号设置为顶部,但是,代码的最后两行将插入符号(和滚动位置)强制到底部
    • 啊,是的,忘了我自己的问题。我确实说过它总是会滚动到底部。不过,我仍然不喜欢更换整个缓冲区。
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2015-06-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2012-08-03
    相关资源
    最近更新 更多