Avatar of Howard Bash
Howard BashFlag for United States of America

asked on 

Multiline Textbox with colored lines per group (criteria for grouping is the data itself...)

Thanks to one of the experts here,  I was able to build an extension to the textbox that allows me to color the rows of a multicolor textbox.

Now the problem is that if I scroll the textbox vertically,  the grouping of colored row "sets" shifts since the code  uses the row position of the visible rows in the textbox.

I think what I need is to know at any point in time which rows within a set of rows is visible and to color them according to where they are in the entire textbox's rows (using the line data for criteria).

If anyone can tell me how to determine the first and last visible row in the textbox,  I think I will be able to put this problem to rest.

I am including my textbox class for review.  There are two routines of particular interest here.  The original drawRect (drawRectOld) and the new drawRect that uses some row criteria to decide when to switch colors.

Thanks,
Howard

Public Class ColorfulTextbox
    Inherits TextBox
 
    Private Const WM_PAINT As Integer = &HF
    Private Const WM_VSCROLL As Integer = &H115
 
    Protected Overrides Sub OnTextChanged(ByVal e As System.EventArgs)
 
        MyBase.OnTextChanged(e)
        Me.Invalidate()
 
    End Sub
 
    Protected Overrides Sub WndProc(ByRef wndmsg As System.Windows.Forms.Message)
 
        MyBase.WndProc(wndmsg)
 
        Select Case wndmsg.Msg
            Case WM_PAINT
                drawRect()
 
            Case WM_VSCROLL
                Me.Invalidate()
 
        End Select
 
    End Sub
 
    Sub drawRect()
        Dim nRowHeight As Integer = 16
        Dim rectF As System.Drawing.RectangleF
        Dim nRows As Long = Me.Height / nRowHeight + 1
        Dim nlp As Long
 
        Dim clr As Color
        clr = Color.Bisque
        clr = Color.FromArgb(clr.A / 2, clr.R, clr.G, clr.B)
 
        Dim clearIt As Color = Color.White
        clearIt = Color.FromArgb(clearIt.A / 2, clearIt.R, clearIt.G, clearIt.B)
        Dim ClearBrush As New SolidBrush(clearIt)
 
        Dim brush As New SolidBrush(clr)
 
        Dim g As Graphics = Me.CreateGraphics()
        Dim arLines As String() = Me.Text.Split(vbCrLf)
 
        If UBound(arLines) = 0 Then Exit Sub
 
        Dim bColoringGroup As Boolean = True
        Dim bGroupChange As Boolean = False
 
        rectF = New System.Drawing.RectangleF(0, 0, Me.Width, nRowHeight)
        g.FillRectangle(brush, rectF)
 
        For nlp = 1 To nRows - 1
 
            If arLines(nlp - 1).Split(" ")(0).Trim = arLines(nlp).Split(" ")(0).Trim Then
                bGroupChange = False
            Else
                bGroupChange = True
                bColoringGroup = Not bColoringGroup
            End If
 
            If bColoringGroup Then
                rectF = New System.Drawing.RectangleF(0, nlp * nRowHeight, Me.Width, nRowHeight)
                g.FillRectangle(brush, rectF)
            Else
                rectF = New System.Drawing.RectangleF(0, nlp * nRowHeight, Me.Width, nRowHeight)
                g.FillRectangle(ClearBrush, rectF)
            End If
 
        Next
 
        g.Dispose()
        brush.Dispose()
 
    End Sub
 
    Sub drawRectOld()
        Dim nRowHeight As Integer = 16
        Dim rectF As System.Drawing.RectangleF
        Dim nRows As Long = Me.Height / nRowHeight + 1
        Dim nlp As Long
        Dim clr As Color
        clr = Color.Bisque
 
        clr = Color.FromArgb(clr.A / 2, clr.R, clr.G, clr.B)
 
        Dim brush As New SolidBrush(clr)
 
        Dim g As Graphics = Me.CreateGraphics()
 
        For nlp = 0 To nRows - 1 Step 2
            rectF = New System.Drawing.RectangleF(0, nlp * nRowHeight, Me.Width, nRowHeight)
            g.FillRectangle(brush, rectF)
        Next
 
        g.Dispose()
        brush.Dispose()
 
    End Sub
 
End Class

Open in new window

.NET ProgrammingVisual Basic.NET

Avatar of undefined
Last Comment
Bob Learned
Avatar of armoghan
armoghan
Flag of Pakistan image

I would suggest use RichTextBox control, which is made for such things

http://www.startvbdotnet.com/controls/rtb.aspx
Avatar of Bob Learned
Bob Learned
Flag of United States of America image

Yeah, I would have suggested the same thing, if I had gotten up earlier *GRIN*.
Avatar of armoghan
armoghan
Flag of Pakistan image

lucky me.
btw some times i wonder when you sleep :)
Avatar of Howard Bash
Howard Bash
Flag of United States of America image

ASKER

So,  is the answer to the question 'How to determine which lines within a multiline textbox are visible?' :

"You should do something else???"

Really I was hoping for a yes you can and here's how ... or no you can't so use a RichTextBox...
Avatar of armoghan
armoghan
Flag of Pakistan image

In my humble opinion, one should use the control according to the requirement, If you are experimenting with textbox and you must bend it to your needs then it is a separate issue. Otherwise RichTextBox is made of thing you want.

Avatar of Howard Bash
Howard Bash
Flag of United States of America image

ASKER

I appreciate  your opinion on this.  That's why I have posted the question here.  

However,   there are other issues associated with changing to the RTF control that make it less than an optimal solution.  For example,  I am using some the windows apip sendmessage api that I would need to ensure applies to the rtf control as well if I switched.

At the moment,  the only issue that I have to resolve is the answer to the question "how do I  programmatically identify the visible lines within a multiline textbox".  

If you do not have the answer to that question,  then I would rather you just said that do not know how to obtain that information and that I might want to consider rewriting my code to use the RTF control.
Avatar of armoghan
armoghan
Flag of Pakistan image

ok.
I wont be able to help regarding the question posted.
TheLearnedOne or ororiole who worked with you on previously question , may help you
Avatar of Bob Learned
Bob Learned
Flag of United States of America image

My question would be what functionality are you getting with SendMessage, that might be possible (or not) with the RichTextBox?
Avatar of Howard Bash
Howard Bash
Flag of United States of America image

ASKER

For example,  since the built in Get.... functions seemed to fail once the lines within the textbox exceeded some relatively small number,  I wrapped a call within the following function.  It might work with no change, but there you have an example of my concern in switching "widgets" in mid stream.

Function GetTextBoxCurrentLine(ByVal tb As TextBox) As Integer
        Const EM_LINEFROMCHAR As Integer = &HC9
        Return SendMessage(tb.Handle.ToInt32(), EM_LINEFROMCHAR, -1, 0)
End Function
Avatar of Bob Learned
Bob Learned
Flag of United States of America image

Try that same thing with a RichTextBox, and tell me where it fails, because I haven't noticed any problem when I use GetLineFromCharPosition.
Avatar of Howard Bash
Howard Bash
Flag of United States of America image

ASKER

I use the function above as follows:

            LineIndex = GetTextBoxCurrentLine(txtSCFList).  I call this from the mouse double click event for the textbox and get the clicked on line number.

There is NO GetLineFromCharPosition exposed when I type "." after the RichTextBox object name.

There is a
            Dim l as long = rtfTextBox.GetLineFromCharIndex (indexOfChar)

But, that requires the index of the character.  

Still,  I would love to see how to determine the lines visible of a multiline textbox.  








Avatar of Bob Learned
Bob Learned
Flag of United States of America image

   Private Sub RichTextBox1_MouseDoubleClick(ByVal sender As System.Object, ByVal e As System.Windows.Forms.MouseEventArgs) Handles RichTextBox1.MouseDoubleClick
        Dim charIndex As Integer = Me.RichTextBox1.GetCharIndexFromPosition(New Point(e.X, e.Y))
        Dim lineIndex As Integer = Me.RichTextBox1.GetLineFromCharIndex(charIndex)
    End Sub
Avatar of Howard Bash
Howard Bash
Flag of United States of America image

ASKER

The code above does work with the RichTextBox,  so I could use your snippet above along with recoding anything that is widget specific.  

Still, I hate to push the point, but if I could access the indices for the first and last visible rows of my multiline textbox,  we could all go home and have a bowl of vanilla swiss almond ...

Avatar of Bob Learned
Bob Learned
Flag of United States of America image

Yeah, you could figure that out by the height of the TextBox divided by the current font height.
Avatar of Howard Bash
Howard Bash
Flag of United States of America image

ASKER

Determining how many maximum is not so tough.   I'm not sure how that would tell me which rows.  I think it will only tell me how many rows are viewable in the textbox, not which.  I might have 20,000 rows where I can only see 20.  At that point,  I know it's a set of twenty,  but which?

Or is one of us missing something here?
Avatar of Bob Learned
Bob Learned
Flag of United States of America image

You said that you know how to get the line number from the current position, so it would be current line number + number of visible rows.
Avatar of Howard Bash
Howard Bash
Flag of United States of America image

ASKER

Ok.  I see that I can rewrite the app to use the RichTextbox control.  

But... we really never answered the question "at any moment,  what are the 1st and last rows displayed in a multiline textbox?"  
Avatar of Bob Learned
Bob Learned
Flag of United States of America image

So, that means that you didn't understand what I was saying before.
Avatar of Howard Bash
Howard Bash
Flag of United States of America image

ASKER

If I know the line number via either the api call I presented earlier, or the methods you demonstrated above,  tell me which row in the set of visible rows the selected row is.

Say we have 100 rows total and the window shows 20.  The call(s) return I clicked on row 29.  Your formula is 29 + 20.  To get the position in the set of 20 rows you would want something more like 29 mod "set size (== in the case 20)".  Dont' you agree?


ASKER CERTIFIED SOLUTION
Avatar of Bob Learned
Bob Learned
Flag of United States of America image

Blurred text
THIS SOLUTION IS ONLY AVAILABLE TO MEMBERS.
View this solution by signing up for a free trial.
Members can start a 7-Day free trial and enjoy unlimited access to the platform.
See Pricing Options
Start Free Trial
.NET Programming
.NET Programming

The .NET Framework is not specific to any one programming language; rather, it includes a library of functions that allows developers to rapidly build applications. Several supported languages include C#, VB.NET, C++ or ASP.NET.

137K
Questions
--
Followers
--
Top Experts
Get a personalized solution from industry experts
Ask the experts
Read over 600 more reviews

TRUSTED BY

IBM logoIntel logoMicrosoft logoUbisoft logoSAP logo
Qualcomm logoCitrix Systems logoWorkday logoErnst & Young logo
High performer badgeUsers love us badge
LinkedIn logoFacebook logoX logoInstagram logoTikTok logoYouTube logo