DevX Home    Today's Headlines   Articles Archive   Tip Bank   Forums   

Results 1 to 8 of 8

Thread: Char. Indexes in a TextBox

  1. #1
    Ozzy Guest

    Char. Indexes in a TextBox


    How to get the indexes of the first/last visible characters in a TextBox,
    where the length of the text exceeds the width of the TextBox?

  2. #2
    dnagel Guest

    Re: Char. Indexes in a TextBox

    I remember something about using SendMessage with the
    EM_GETFIRSTVISIBLELINE to get the line number of the
    top visible line of the TB.

    Heres a sample that works...

    Its not all that straight foreward...

    Drop a multi line text box on the form. Set the HideSelection to False
    to see it work. Also needs a Command Button to fire the code to
    hilight the first or last character of the visible window. It does
    this in an alternating fashion for each click of the command button.

    ToDo:

    Modify it to be a function which takes TopChar and BotChar as
    parameters to use the values elsewhere in your code...

    hth,

    D.




    Option Explicit

    Private Const EM_GETFIRSTVISIBLELINE = &HCE
    Private Const EM_LINEFROMCHAR = &HC9

    Private Declare Function SendMessage Lib "user32" _
    Alias "SendMessageA" (ByVal hwnd As Long, ByVal wMsg As _
    Long, ByVal wParam As Long, lParam As Any) As _
    Long

    Private xp As PictureBox

    Private Sub Command1_Click()
    Dim TopLine As Long
    Dim BotLine As Long
    Dim CharPos As Long
    Dim TopChar As Long
    Dim BotChar As Long

    'lines returned as 0-based number
    TopLine = SendMessage(Text1.hwnd, EM_GETFIRSTVISIBLELINE, 0, 0)
    BotLine = TopLine + (Text1.Height / xp.TextHeight("H") - 1) - 1

    Debug.Print TopLine, BotLine

    CharPos = 0
    Do While SendMessage(Text1.hwnd, EM_LINEFROMCHAR, CharPos, 0&) < TopLine
    CharPos = CharPos + 1
    Loop
    TopChar = CharPos

    CharPos = Len(Text1.Text)
    Do While SendMessage(Text1.hwnd, EM_LINEFROMCHAR, CharPos, 0&) > BotLine
    CharPos = CharPos - 1
    Loop

    BotChar = CharPos

    TopSpot:
    Select Case Asc(Mid$(Text1.Text, BotChar, 1))
    Case 13, 10
    BotChar = BotChar - 1
    GoTo TopSpot
    End Select

    'Chars are zero based as well
    Debug.Print TopChar, BotChar

    Static Alternate As Boolean
    If Alternate = False Then
    Text1.SelStart = TopChar
    Text1.SelLength = 1
    Alternate = True
    Else
    Text1.SelStart = BotChar - 1
    Text1.SelLength = 1
    Alternate = False
    End If

    End Sub

    Private Sub Form_Load()

    Text1.Text = "1This is a test." & vbCrLf & _
    "2This is a test." & vbCrLf & _
    "3This is a test." & vbCrLf & _
    "4This is a test." & vbCrLf & _
    "5This is a test." & vbCrLf & _
    "6This is a test." & vbCrLf & _
    "7This is a test." & vbCrLf & _
    "8This is a test."

    Set xp = Controls.Add("VB.PictureBox", "XP")
    Set xp.Font = Text1.Font
    End Sub





    constant or something to that effect.


    "Ozzy" <tycho688@yahoo.com> wrote in message news:3c9d1765$1@10.1.10.29...
    |
    | How to get the indexes of the first/last visible characters in a TextBox,
    | where the length of the text exceeds the width of the TextBox?



  3. #3
    Mark Alexander Bertenshaw Guest

    Re: Char. Indexes in a TextBox


    "dnagel" <GrandNagel@hotmail.com> wrote:
    >I remember something about using SendMessage with the
    >EM_GETFIRSTVISIBLELINE to get the line number of the
    >top visible line of the TB.
    >
    >Heres a sample that works...
    >
    >Its not all that straight foreward...
    >
    >Drop a multi line text box on the form. Set the HideSelection to False
    >to see it work. Also needs a Command Button to fire the code to
    >hilight the first or last character of the visible window. It does
    >this in an alternating fashion for each click of the command button.


    "dnagel" -

    Unfortunately, you haven't read the question:

    "How [do you] get the indexes of the first/last visible characters in a TextBox,
    where the length of the text exceeds the width of the TextBox?"

    This isn't about line, it's about character i.e. along the horizontal. Unfortunately,
    there seems to be no EDIT message to do this directly. Yesterday, I tried
    using EM_GETRECT to retrieve the formatting rectangle, and then EM_CHARFROMPOS
    using the first and last point horizontal of that rectangle. Using the values
    returned, you can retrieve the appropriate value from the textbox's text.
    Unfortunately, this does not give you consistent values if the box autoscrolls
    i.e. when the caret gets to the end of the formatting rectangle.

    I can provide the "work in progress" if anybody's interested.

    --
    Mark Alexander Bertenshaw
    Programmer/Analyst
    Chordiant Software, Inc.
    Brentford
    UK

  4. #4
    dnagel Guest

    Re: Char. Indexes in a TextBox

    | Unfortunately, you haven't read the question:
    |
    | "How [do you] get the indexes of the first/last visible characters in a TextBox,
    | where the length of the text exceeds the width of the TextBox?"
    |
    | This isn't about line, it's about character i.e. along the horizontal. Unfortunately,
    | there seems to be no EDIT message to do this directly.

    I did read it, I just didn't test it thoroughly...

    | This isn't about line, it's about character i.e. along the horizontal. Unfortunately,
    | there seems to be no EDIT message to do this directly.

    Yes, that much is obvious..., but to get the char, you need to identify what
    lines are visible...

    Didn't you notice the declarations for Top and Bottom Char?

    Dim TopChar As Long
    Dim BotChar As Long

    | Unfortunately, this does not give you consistent values if the box autoscrolls
    | i.e. when the caret gets to the end of the formatting rectangle.

    Does this imply word wrapping??? The version accompanying this post will
    handle it either way, with or without scroll bars (which causes or prevents the
    word wrapping ability).



    Sorry for not being thorough the first time, but hey, this stuff IS free... :->

    Heres a version that works for widths outside the width of the textbox.
    (Tested, Verified) Again, I used a TextBox and a CommandButton (Text1
    and Command1)

    D.


    '------------ Begin Code Snip ------------------
    Option Explicit

    Private Const EM_GETFIRSTVISIBLELINE = &HCE
    Private Const EM_LINEFROMCHAR = &HC9

    Private Declare Function SendMessage Lib "user32" _
    Alias "SendMessageA" (ByVal hwnd As Long, ByVal wMsg As _
    Long, ByVal wParam As Long, lParam As Any) As _
    Long

    Private Type RECT
    Left As Long
    Top As Long
    Right As Long
    Bottom As Long
    End Type
    Private Declare Function GetClientRect Lib "user32" _
    (ByVal hwnd As Long, lpRect As RECT) As Long

    Private xp As PictureBox


    Private Sub Command1_Click()
    'line locations
    Dim TopLine As Long, BotLine As Double
    'Char locations
    Dim CharPos As Long, TopChar As Long, BotChar As Long
    'used to determine the client area of the textbox
    Dim xRect As RECT
    ' used only to alternate when using combobox for testing
    Static Alternate As Boolean

    'lines returned as 0-based number
    TopLine = SendMessage(Text1.hwnd, EM_GETFIRSTVISIBLELINE, 0, 0)
    'Determine how much vertical space we have to work with
    Call GetClientRect(Text1.hwnd, xRect)
    'Calc the bottom line as a function of the height in twips
    'divided by the text height minus the top line index--
    BotLine = (ScaleY(xRect.Bottom - 2, vbPixels, vbTwips) _
    / xp.TextHeight("H")) + (TopLine - 1)
    'determine the first char position of the top line
    CharPos = 0
    Do While SendMessage(Text1.hwnd, EM_LINEFROMCHAR, CharPos, 0&) < TopLine
    CharPos = CharPos + 1
    Loop
    TopChar = CharPos
    'determine the last char position of the Bottom line
    CharPos = Len(Text1.Text)
    Do While SendMessage(Text1.hwnd, EM_LINEFROMCHAR, CharPos, 0&) > BotLine
    CharPos = CharPos - 1
    Loop
    BotChar = CharPos
    'Walk back before the CR and LF chars
    TopSpot:
    Select Case Asc(Mid$(Text1.Text, BotChar, 1))
    Case 13, 10
    BotChar = BotChar - 1
    GoTo TopSpot
    End Select
    'Chars are zero based as well
    Debug.Print TopChar, BotChar

    If Alternate = False Then
    Text1.SelStart = TopChar
    Text1.SelLength = 1
    Alternate = True
    Else
    Text1.SelStart = BotChar - 1
    Text1.SelLength = 1
    Alternate = False
    End If

    End Sub

    Private Sub Form_Load()

    Text1.Width = 3000
    'Used to put text into the textbox that will be longer than the width
    Text1.Text = "1This is a test1This is a test1This is a test1This is a test." & vbCrLf & _
    "21This is a test1This is a test1This is a testThis is a test." & vbCrLf & _
    "31This is a test1This is a test1This is a testThis is a test." & vbCrLf & _
    "41This is a test1This is a test1This is a testThis is a test." & vbCrLf & _
    "51This is a test1This is a test1This is a testThis is a test." & vbCrLf & _
    "61This is a test1This is a test1This is a testThis is a test." & vbCrLf & _
    "7This is a test1This is a test1This is a test7This is a test." & vbCrLf & _
    "81This is a test1This is a test1This is a testThis is a test."

    'Used to put text into the textbox that will be shorter than the width
    'Text1.Text = "1This is a test1" & vbCrLf & _
    ' "2This is a test1" & vbCrLf & _
    ' "3This is a test1" & vbCrLf & _
    ' "4This is a test1" & vbCrLf & _
    ' "5This is a test1" & vbCrLf & _
    ' "6This is a test1" & vbCrLf & _
    ' "7This is a test1" & vbCrLf & _
    ' "8This is a test1"

    'Picturebox used to evaluate textboxes Character height
    Set xp = Controls.Add("VB.PictureBox", "XP")
    Set xp.Font = Text1.Font

    End Sub

    '------------ End Code Snip ------------------

    "Mark Alexander Bertenshaw" <mark.bertenshaw@virgin.net> wrote in message news:3c9f09de$1@10.1.10.29...
    |
    | "dnagel" <GrandNagel@hotmail.com> wrote:
    | >I remember something about using SendMessage with the
    | >EM_GETFIRSTVISIBLELINE to get the line number of the
    | >top visible line of the TB.
    | >
    | >Heres a sample that works...
    | >
    | >Its not all that straight foreward...
    | >
    | >Drop a multi line text box on the form. Set the HideSelection to False
    | >to see it work. Also needs a Command Button to fire the code to
    | >hilight the first or last character of the visible window. It does
    | >this in an alternating fashion for each click of the command button.
    |
    | "dnagel" -
    |
    | Unfortunately, you haven't read the question:
    |
    | "How [do you] get the indexes of the first/last visible characters in a TextBox,
    | where the length of the text exceeds the width of the TextBox?"
    |
    | This isn't about line, it's about character i.e. along the horizontal. Unfortunately,
    | there seems to be no EDIT message to do this directly. Yesterday, I tried
    | using EM_GETRECT to retrieve the formatting rectangle, and then EM_CHARFROMPOS
    | using the first and last point horizontal of that rectangle. Using the values
    | returned, you can retrieve the appropriate value from the textbox's text.
    | Unfortunately, this does not give you consistent values if the box autoscrolls
    | i.e. when the caret gets to the end of the formatting rectangle.
    |
    | I can provide the "work in progress" if anybody's interested.
    |
    | --
    | Mark Alexander Bertenshaw
    | Programmer/Analyst
    | Chordiant Software, Inc.
    | Brentford
    | UK



  5. #5
    Mark Alexander Bertenshaw Guest

    Re: Char. Indexes in a TextBox


    Hmmm. I was using a single line edit box.

    --
    Mark Alexander Bertenshaw
    Programmer/Analyst
    Chordiant Software, Inc.
    Brentford
    UK

  6. #6
    Mark Alexander Bertenshaw Guest

    Re: Char. Indexes in a TextBox


    "dnagel" -

    If you were to use a multi-line box, using EM_GETRECT to return the formatting
    rectangle, and EM_CHARFROMPOS would still be valid, don't you think? Unfortunately,
    EM_CHARFROMPOS seems to count characters which you are unable to see, because
    they would otherwise overlap the edge of the formatting rectangle. And after
    auto-scrolling horizontally, it goes a little haywire.

    --
    Mark Alexander Bertenshaw
    Programmer/Analyst
    Chordiant Software, Inc.
    Brentford
    UK


  7. #7
    dnagel Guest

    Re: Char. Indexes in a TextBox

    | If you were to use a multi-line box, using EM_GETRECT to return the formatting
    | rectangle, and EM_CHARFROMPOS would still be valid, don't you think?

    In case its not obvious, I don't truly understand Windows under workings...

    Looking up, EM_GETRECT and EM_SETRECT I see that this is likely the
    rectangle that windows uses to determine if it should display scrollbars,
    as well as for other features of Edit controls.

    If you use EM_GETRECT without setting scrollbars on, you'll get a rect
    the same width as the textbox client area and total height of the text
    contained therein. If its on, then you will get a rect with the widest
    width and total height of the control's text.

    So It should be possible to use EM_CHARFROMPOS to get char and line
    information

    EM_CHARFROMPOS
    wParam = 0; // not used
    lParam = MAKELPARAM(xPos, yPos); // coordinates of a point

    Return Values
    The low word of the return value specifies the zero-based index of the
    character nearest the specified point. This index is relative to the beginning
    of the control, not the beginning of the line. If the specified point is beyond
    the last character in the edit control, the return value indicates the last
    character in the control.

    The high word of the return value specifies the zero-based index of the line
    that contains the character. For single-line edit controls, this value is zero.
    #The index indicates the line delimiter if the specified point is beyond the
    #last visible character in a line.

    The above is from the MSDN.

    Does this mean you have to build a lo/hi word value in a long and pass that
    as the lParam? I can't figure out how to make a MAKELPARAM value. How
    would you convert two singles to a long?

    What exactly does this mean?

    #The index indicates the line delimiter if the specified point is beyond the
    #last visible character in a line.

    It sounds like it barfs if you're ouside the rectangle.

    ???

    D.

    "Mark Alexander Bertenshaw" <mark.bertenshaw@virgin.net> wrote in message news:3c9f6234$1@10.1.10.29...
    |
    | "dnagel" -
    |
    | If you were to use a multi-line box, using EM_GETRECT to return the formatting
    | rectangle, and EM_CHARFROMPOS would still be valid, don't you think? Unfortunately,
    | EM_CHARFROMPOS seems to count characters which you are unable to see, because
    | they would otherwise overlap the edge of the formatting rectangle. And after
    | auto-scrolling horizontally, it goes a little haywire.
    |
    | --
    | Mark Alexander Bertenshaw
    | Programmer/Analyst
    | Chordiant Software, Inc.
    | Brentford
    | UK
    |



  8. #8
    Mark Alexander Bertenshaw Guest

    Re: Char. Indexes in a TextBox


    "dnagel" <GrandNagel@hotmail.com> wrote:
    >| If you were to use a multi-line box, using EM_GETRECT to return the formatting
    >| rectangle, and EM_CHARFROMPOS would still be valid, don't you think?
    >
    >In case its not obvious, I don't truly understand Windows under workings...
    >
    >Looking up, EM_GETRECT and EM_SETRECT I see that this is likely the
    >rectangle that windows uses to determine if it should display scrollbars,
    >as well as for other features of Edit controls.


    Yup - I think so too.

    >If you use EM_GETRECT without setting scrollbars on, you'll get a rect
    >the same width as the textbox client area and total height of the text
    >contained therein. If its on, then you will get a rect with the widest
    >width and total height of the control's text.
    >
    >So It should be possible to use EM_CHARFROMPOS to get char and line
    >information


    <skipped MSDN example>

    >Does this mean you have to build a lo/hi word value in a long and pass that
    >as the lParam? I can't figure out how to make a MAKELPARAM value.


    >How would you convert two singles to a long?


    You wouldn't. You would convert the width and height from twips to pixels
    (divide by Screen.TwipsPerPixel{X|Y} , which would make the width and height
    integers.

    MAKELPARAM should be equivalent to

    Function MAKELPARAM(ByVal X As Integer, ByVal Y As Integer) As Long
    MAKELPARAM = (Y * &H10000&) Or X
    End Function

    >What exactly does this mean?
    >
    > #The index indicates the line delimiter if the specified point is beyond

    the
    > #last visible character in a line.
    >
    >It sounds like it barfs if you're ouside the rectangle.


    I think it is giving the position of the carriage return or the position
    of the first character in the new line string.

    --
    Mark Alexander Bertenshaw
    Programmer/Analyst
    Chordiant Software, Inc.
    Brentford
    UK

Posting Permissions

  • You may not post new threads
  • You may not post replies
  • You may not post attachments
  • You may not edit your posts
  •  
HTML5 Development Center
 
 
FAQ
Latest Articles
Java
.NET
XML
Database
Enterprise
Questions? Contact us.
C++
Web Development
Wireless
Latest Tips
Open Source


   Development Centers

   -- Android Development Center
   -- Cloud Development Project Center
   -- HTML5 Development Center
   -- Windows Mobile Development Center