Results 1 to 5 of 5

Thread: [RESOLVED] RichTextBox - Determine lines have changed

  1. #1

    Thread Starter
    Fanatic Member
    Join Date
    Jul 2009
    Posts
    629

    Resolved [RESOLVED] RichTextBox - Determine lines have changed

    Hello everyone.

    I am working on a way to determine if the lines of a RichTextBox have changed.
    I currently use this code:
    Code:
                Dim newhash As Integer = Me.Text.GetHashCode
                If newhash <> Me._prevhash Then
                    Me._prevhash = newhash
                    Me._lines = MyBase.Lines
                    If Me._lines.Count = 0 Then
                        ReDim Me._lines(0)
                        Me._lines(0) = ""
                    End If
                End If
    But this method has a bad issue: The text property takes a while to "load up" plus getting its hash code adds to it. This code runs every time the Lines are needed (it is a property). So this code:
    Code:
    For i As Integer = 0 To Lines.Count - 1
        Dim line As String = Lines(i)
    Next
    Takes ages to complete, because for every line it checks if the lines have changed.

    I am trying to evade using MyBase.Lines too much, since that REALLY takes ages to load.

    - I can not use the modified property since it is unreliable (set to true if only a text color changes)
    - Can't use the RTF since it changes without the text changing (coloring) and the hash code is slow as well.
    - Can't use the TextChanged event, since it is sent after the SelectionChanged event (for some reason)

    Any help on this?

  2. #2
    Super Moderator jmcilhinney's Avatar
    Join Date
    May 2005
    Location
    Sydney, Australia
    Posts
    111,222

    Re: RichTextBox - Determine lines have changed

    Can't use the TextChanged event, since it is sent after the SelectionChanged event (for some reason)
    Why is that a problem? The SelectionChanged event is raised when the SelectionStart or SelectionLength property changes. If you type into the control then the SelectionStart property will change, so it stands to reason that the SelectionChanged event is raised. If you change the SelectionStart or SelectionLength without changing the Text then SelectionChanged is raised but TextChanged isn't. Why can't you use TextChanged to determine when the Text changes? That's exactly what it's for.
    Why is my data not saved to my database? | MSDN Data Walkthroughs
    VBForums Database Development FAQ
    My CodeBank Submissions: VB | C#
    My Blog: Data Among Multiple Forms (3 parts)
    Beginner Tutorials: VB | C# | SQL

  3. #3

    Thread Starter
    Fanatic Member
    Join Date
    Jul 2009
    Posts
    629

    Re: RichTextBox - Determine lines have changed

    Because I need to read the lines in the SelectionStart event(s). If I place it in the TextChanged routine, these events will always read one character too late.
    Could set a variable in the SelectionChanged routine that sets if the text has changed, but for that I need to know IF the text has changed.
    This because the user could have just clicked somewhere else instead of typing.
    Then it is easier to do this directly in the property.

    The goal is the least amount of MyBase.Lines calls. ONLY when the lines are needed and the lines have changed.
    Else I will return the backbuffer array.

    I am currently going to use the Modified property, with some recover functions if I change things other than text. But it is not the best method.

  4. #4
    Super Moderator jmcilhinney's Avatar
    Join Date
    May 2005
    Location
    Sydney, Australia
    Posts
    111,222

    Re: RichTextBox - Determine lines have changed

    What is it that you are actually trying to determine? You say:
    determine if the lines of a RichTextBox have changed
    but that doesn't actually make sense. Do you really mean the number of lines, or the text in those lines? If it's the text in the lines then you don't need to do anything other than handle the TextChanged event because that is raised when and only when the Text changes. If it's the number of lines then your code doesn't really make sense.
    Why is my data not saved to my database? | MSDN Data Walkthroughs
    VBForums Database Development FAQ
    My CodeBank Submissions: VB | C#
    My Blog: Data Among Multiple Forms (3 parts)
    Beginner Tutorials: VB | C# | SQL

  5. #5

    Thread Starter
    Fanatic Member
    Join Date
    Jul 2009
    Posts
    629

    Re: RichTextBox - Determine lines have changed

    I'll fully explain.

    I have a custom control that inherits from the RichTextBox. This control has lots of properties. This includes many line calls. For example:
    Code:
        <Browsable(False), DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden)> _
        Public ReadOnly Property LineIndex(ByVal charindex As Integer) As Integer
            Get
                Return Me.GetLineFromCharIndex(charindex)
            End Get
        End Property
        <Browsable(False), DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden)> _
        Public ReadOnly Property LineStart(ByVal lineindex As Integer) As Integer
            Get
                If lineindex = -1 Then Return SelectionLineStart
                Return Me.GetFirstCharIndexFromLine(lineindex)
            End Get
        End Property
        <Browsable(False), DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden)> _
        Public ReadOnly Property LineEnd(ByVal lineindex As Integer) As Integer
            Get
                Return Me.LineStart(lineindex) + Me.Lines(lineindex).Length
            End Get
        End Property
    For code information I may need to call lineend multiple times, for example, to process the current users' selection. If I use MyBase.Lines, this operation is very slow since it regenerates the lines for each call. So now I added overloaded properties to handle this:
    Code:
        Private _lines() As String
        <Browsable(False), DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden)> _
        Public Overloads Property Lines(ByVal index As Integer) As String
            Get
                If index > Me.Lines.Count - 1 Then Return ""
                If index < 0 Then Return ""
                Return Me.Lines()(index)
            End Get
            Set(ByVal value As String)
                If index > Me.Lines.Count - 1 Then Return
                Dim l() As String = Me.Lines
                l(index) = value
                Me.Lines = l
                ColorizeLines(index, value.Split(vbLf).Count)
            End Set
        End Property
        <Browsable(False), DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden)> _
        Public Overloads Property Lines() As String()
            Get
                If IsNothing(Me._lines) OrElse MyBase.Modified Then
                    Me._lines = MyBase.Lines
                    MyBase.Modified = False
                End If
                If Me._lines.Count = 0 Then
                    ReDim Me._lines(0)
                    Me._lines(0) = ""
                End If
                Return Me._lines
            End Get
            Set(ByVal value As String())
                MyBase.Lines = value
            End Set
        End Property
    This property has two main functions:
    - Make sure the line count is never 0 ; since lineindex can be 0 even if text is empty
    - Make sure the backbuffer is updated with MyBase.Lines Only if it is needed.

    As you saw in the Line* codes, I call "ColorizeLines" to update the text coloring in my control. This is done by "locking" the control, selecting the lines to update, obtain the selection text and generate the selection RTF from this text. Code is pretty complicated, but it comes down to using a "font header" containing the font and colors in the document and finally appending the text proceeded with "/cf1 " to "/cf7 " codes to generate coloring. I also use highlighting in my code which does the same, but then simply selects the text to highlight and sets the selection backcolor.

    This highlighting is done commonly to highlight the current "fragment" info is given of.
    Code:
        Private _highlightstart As Integer = 0
        Private _highlightlength As Integer = 0
        Public Sub Highlight(ByVal start As Integer, ByVal length As Integer)
            If Me._highlightstart <> start Or Me._highlightlength <> length Then
                If start < 0 Then start = 0
                If length < 0 Then length = 0
                Me.Lock()
                Me.SaveSelection()
                Me.Select(Me._highlightstart, Me._highlightlength)
                Me.SelectionBackColor = Color.White
                Me.Select(start, length)
                Me.SelectionBackColor = Color.Yellow
                Me._highlightstart = start
                Me._highlightlength = length
                Me.LoadSelection()
                Me.Unlock()
            End If
        End Sub
    This highlighting causes modified events as well, making the lines update without any text changed. On my main form I may need to get line information when the selection changed, for example to get the current line the user is in.
    Code:
        <Browsable(False), DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden)> _
        Public Property SelectionLine() As String
            Get
                Return Me.Lines(Me.SelectionLineIndex)
            End Get
            Set(ByVal value As String)
                Me.Lines(Me.SelectionLineIndex) = value
            End Set
        End Property
    But if I handle the textchanged for this, this is called after the selectionchanged event, thus the handler reads one character too late.
    Example for you to test out:
    Code:
        Private Sub Form1_Load(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles MyBase.Load
            RichTextBox1_TextChanged(Nothing, Nothing)
        End Sub
    
        Private _lines(-1) As String
        Private Sub RichTextBox1_TextChanged(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles RichTextBox1.TextChanged
            Me._lines = RichTextBox1.Lines
            If Me._lines.Count = 0 Then
                ReDim Me._lines(0)
                Me._lines(0) = ""
            End If
        End Sub
    
        Private Sub RichTextBox1_SelectionChanged(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles RichTextBox1.SelectionChanged
            MsgBox(Me._lines(0))
        End Sub
    At this point I save and reset the Modified property in the Lock and Unlock functions, and hold a separate Modified property which is set to true on text change.
    Code:
        Public Sub Lock(Optional ByVal lock As Boolean = True)
            SendMessage(Me.Handle, WM_SETREDRAW, lock = False, Nothing)
            If Not lock Then Me.Invalidate()
            Me._ignoretextchange = lock
            If lock Then
                Me._prevmod = MyBase.Modified
            Else
                MyBase.Modified = Me._prevmod
            End If
        End Sub
    But when the user select character 'a' for example, and types an 'a' modified is set to true as well, causing another useless line update.
    I need to know, from the control, if the text in the control (or lines) have changed.

    I hope you understand my issue.

    EDIT

    Just for those reading this; I fixed it by using the Modified property of the native RichTextBox. I added my a custom Modified property (with private member) in my custom control which is set to true when the text changes. Also made it activate the ModifiedChanged event.

Posting Permissions

  • You may not post new threads
  • You may not post replies
  • You may not post attachments
  • You may not edit your posts
  •  



Click Here to Expand Forum to Full Width