Textbox line property

T

Tom Shelton

It works OK here for any text of 3 or 4 lines, which is what OP wanted it
for.  It can go wrong after that in some cases.   You might like to replace
the new rectangle in the MeasureCharacterRanges argument list with
tb.ClientRectangle, but that's only for neatness.  However you should use
fonts with an integer font size, and larger fonts are more reliable than
smaller ones.  If it continues to give trouble, try adding options to the
StringFormat - I think the one to do with trimming spaces might be relevant.
The method already takes account of the formatting factors that you have
mentioned.

Seems like a lot of extra work to inaccurately duplicate what can
accurately be done with a couple of api calls. What's your beef with
using the API?
 
M

Miro

In the end I wrote my own little function /class to split up the words into
the proper length taking into account tabs / vbcr / vbcrf / vblf 's and so
on.

User can specify the amount of characters and then it goes from there.

Basically I first just do a string.SPLIT on the " " character.

I get an arraylist of words.

I loop through the words testing for "tabs" and replacing them to 3 or 5
spaces.
Then as I add the words to the new "string" that will be no more than x
amount of characters I check for the enterkeys/line feeds, and then starta
new line on them replacing them to be "" chars.

Thans for everyones ideas...im still following along to see the whole thread
I started....


cheers'

Miro
 
M

Mike Williams

However you should use fonts with an integer font size,
and larger fonts are more reliable than smaller ones.

As I've previusly said, the code at the link you posted in many cases does
not work even if you use only a few lines of text and even if you do stick
to the "integer font size and larger fonts" that you mentioned. Personally
I'm a VB6 man myself and I have never used VB.Net (I'm just nosing around in
here really) and so I would perform the task in VB6 simply by sending the
TextBox the appropriate EM_GETLINE etc messages. However, this morning (much
against my better judgement!) I downloaded VB.Net 2008 Express and after
almost no time at all I could see what I think would be a reliable VB.Net
way of accomplishing the task, so I'm not sure why the author of the code
(which does not actually work) at the link you posted has said that it is a
difficult task in VB.Net, and I don't see why you are persisting with that
code even though it has been clearly demonstrated that it does not work. As
I've said, I'm extremely new to VB.Net having downloaded it just this
morning but after a quick look at the available functions surely you can
perform a very simple loop from p=0 onwards using
TextBox1.GetFirstCharIndexFromLine(p) until you get a result that indicates
you have deal with the final line. That will allow you to pull the
appropriate substrings out from the TextBox Text and it should be totally
accurate and extremely quick. I don't write VB.Net code myself (and I don't
think I ever intend to!) but surely a VB.Net programmer could knock
something like that up in less than ten minutes?

Mike
 
M

Mike Williams

The method already takes account of the formatting
factors that you have mentioned.

Well it most certainly does not do so correctly because in all the cases I
mentioned in my earlier post it fails to produce the correct result. As I've
already said, I'm really a VB6 coder and I'm really just nosing about this
VB.Net group but I downloaded VB.Net 2008 Express today (much against my
better judgement) to have a quick look at it. In VB6 I would simply send the
appropriate messages to the TextBox (which works extremely reliably and
which is something that would work just as well in VB.Net). However, having
played with VB.Net Express for just a very short time and having noticed the
native VB.Net GetFirstCharIndexFromLine function (which of course is not
available in VB6) I reckon that function would be one of the easiest way to
accomplish the task in Net. Here is a very simple VB.Net example that I've
just knocked up. Admittedly is is coded in a fairly sloppy fashion (because
it is just a quick "knock up" at the moment and I have never used VB.Net
before) but it does work very well. It needs a bit of tidying up of course
and I think it needs a bit of code adding to take account of empty TextBoxes
but it works reliably and it works whatever the font or font size in use.

Private Sub Button1_Click(ByVal sender As System.Object, ByVal e As
System.EventArgs) Handles Button1.Click
Dim s1 As String, p1 As Integer, p2 As Integer, n As Integer
s1 = "Now is the time for all good men to come to the aid of the
country."
TextBox1.Text = s1
p1 = TextBox1.GetFirstCharIndexFromLine(n)
Do
n = n + 1
p2 = TextBox1.GetFirstCharIndexFromLine(n)
If p2 = -1 Then
Console.WriteLine(Mid(s1, p1 + 1))
Else
Console.WriteLine(Mid(s1, p1 + 1, p2 - p1))
End If
p1 = p2
Loop Until p2 = -1
End Sub

Mike
 
T

Tom Shelton

As I've previusly said, the code at the link you posted in many cases does
not work even if you use only a few lines of text and even if you do stick
to the "integer font size and larger fonts" that you mentioned. Personally
I'm a VB6 man myself and I have never used VB.Net (I'm just nosing around in
here really) and so I would perform the task in VB6 simply by sending the
TextBox the appropriate EM_GETLINE etc messages. However, this morning (much
against my better judgement!) I downloaded VB.Net 2008 Express and after
almost no time at all I could see what I think would be a reliable VB.Net
way of accomplishing the task, so I'm not sure why the author of the code
(which does not actually work) at the link you posted has said that it is a
difficult task in VB.Net, and I don't see why you are persisting with that
code even though it has been clearly demonstrated that it does not work. As
I've said, I'm extremely new to VB.Net having downloaded it just this
morning but after a quick look at the available functions surely you can
perform a very simple loop from p=0 onwards using
TextBox1.GetFirstCharIndexFromLine(p) until you get a result that indicates
you have deal with the final line. That will allow you to pull the
appropriate substrings out from the TextBox Text and it should be totally
accurate and extremely quick. I don't write VB.Net code myself (and I don't
think I ever intend to!) but surely a VB.Net programmer could knock
something like that up in less than ten minutes?

Mike

LOL.. I've been using .NET for a long time now and never noticed that method
:) And just so you know Mike, I checked the implementation in reflector, and
sure enough it is calling SendMessage with EM_LINEINDEX - so, it should be
fast. Still, I looking closer, there seems to be no way to get the line length
(equivalent to EM_LINELENGTH) or to get the actual wrapped line (EM_GETLINE).

So, I revised the loop like this:

Dim line As Integer = 0
Dim firstChar As Integer = TextBox1.GetFirstCharIndexFromLine(line)
While (firstChar <> -1)
Dim lineLength = SendMessage(TextBox1.Handle, EM_LINELENGTH, firstChar, 0)
Dim chars(lineLength) As Char
chars(0) = Convert.ToChar(lineLength)
SendMessage(TextBox1.Handle, EM_GETLINE, line, chars)
MessageBox.Show(New String(chars))
line += 1
firstChar = TextBox1.GetFirstCharIndexFromLine(line)
End While

Still, you learn something new everyday...
 
M

Mike Williams

In the end I wrote my own little function /class to split up the
words into the proper length taking into account tabs / vbcr
/ vbcrf / vblf 's and so on. User can specify the amount of
characters and then it goes from there. Basically I first just
do . . . . . then as I add the words to the new "string" that
will be no more than x amount of characters . . .

That approach is fine if you are dealing with fixed width fonts such as
Courier New for example (as you appear to be doing) because in such a case
the number of characters that fit onto a line of a specific sized TextBox
(or into any other fixed width space) will be fixed because all characters
in such a font have exactly the same cell width. However, if you are dealing
with proportionally spaced fonts (Arial, Times New Roman, Calibri, etc, in
fact almost all the available fonts on a typical system) then a simple count
of the characters will not be sufficient because the characters cells are
different widths and, for example, one line might contain 35 characters
whereas the next line may might contain 50 or more characters, depending on
the actual characters present in each line. So, since it appears that you
are actually using a fixed width font your own solution will be fine, but
most people here were presenting you more flexible methods that work for
both fixed width and proportionally spaced fonts. Therefore, although your
own code works fine for the specific task you are currently doing (using
fixed width fonts) you would be wise to still take notice of the various
other more general solutions you have been offered as these, and the
principles behind them, will come in handy in the future when you have a
more general need for these kind of things.

Mike
 
M

Mike Williams

Here is a very simple VB.Net example that I've . . .

Just to point out something that I forgot to mention in my earlier response,
the code I posted which uses the VB.Net GetFirstCharIndexFromLine function
will as it stands return each of the TextBox displayed lines by extracting
them from the string that the TextBox is displaying (TextBox1.Text) by
pulling out the string contents between the first character position of the
specific line and the first character position of the succeeding line.
Therefore at the moment the returned substrings will include any trailing
space or carriage return / linefeed characters. This makes it very easy to
reconstruct the original text from the returned substrings should the need
ever arise, but in some cases it will not be what is wanted. It would be
very easy though to amend the routine so that it strips any trailing space
and / or trailing control characters from the returned substrings, depending
on your exact requirements. Note when doing this that not all wrapped lines
will have a trailing space even if those lines are all part of the same
"sentence" because it is quite possible that the TextBox has wrapped a
specific displayed line in the middle of a word, so it is not wise to assume
that a visible line always contains complete words. The latter by the way
will apply whatever method you are using to get the wrapped lines. These
things are all easy to take into account though.

Mike
 
T

Tom Shelton

Well it most certainly does not do so correctly because in all the cases I
mentioned in my earlier post it fails to produce the correct result. As I've
already said, I'm really a VB6 coder and I'm really just nosing about this
VB.Net group but I downloaded VB.Net 2008 Express today (much against my
better judgement) to have a quick look at it. In VB6 I would simply send the
appropriate messages to the TextBox (which works extremely reliably and
which is something that would work just as well in VB.Net). However, having
played with VB.Net Express for just a very short time and having noticed the
native VB.Net GetFirstCharIndexFromLine function (which of course is not
available in VB6) I reckon that function would be one of the easiest way to
accomplish the task in Net. Here is a very simple VB.Net example that I've
just knocked up. Admittedly is is coded in a fairly sloppy fashion (because
it is just a quick "knock up" at the moment and I have never used VB.Net
before) but it does work very well. It needs a bit of tidying up of course
and I think it needs a bit of code adding to take account of empty TextBoxes
but it works reliably and it works whatever the font or font size in use.

    Private Sub Button1_Click(ByVal sender As System.Object, ByVal e As
System.EventArgs) Handles Button1.Click
        Dim s1 As String, p1 As Integer, p2 As Integer, n As Integer
        s1 = "Now is the time for all good men to come to the aid of the
country."
        TextBox1.Text = s1
        p1 = TextBox1.GetFirstCharIndexFromLine(n)
        Do
            n = n + 1
            p2 = TextBox1.GetFirstCharIndexFromLine(n)
            If p2 = -1 Then
                Console.WriteLine(Mid(s1, p1 + 1))
            Else
                Console.WriteLine(Mid(s1, p1 + 1, p2 - p1))
            End If
            p1 = p2
        Loop Until p2 = -1
    End Sub

Mike

I'm such a dope! Duh.

--
 
J

James Hahn

I've got no beef with using the API - I never commented on that at all.

But when a suggestion was posted that the API was the way to go, without any
clues as to how to actually do it, and OP indicated he was unfamiliar with
the API, I suggested that it was probably not worth the effort in learning
to use the API, and offered an alternative for which there was a complete,
functioning example.

When a subsequent suggestion was made that the example didn't work I pointed
out what was probably wrong with the testing setup. Only then was the API
solution actually produced.

It seems there are some here more interested in arguing amongst themselves
than in providing solutions to the problems being posed.

It works OK here for any text of 3 or 4 lines, which is what OP wanted it
for. It can go wrong after that in some cases. You might like to replace
the new rectangle in the MeasureCharacterRanges argument list with
tb.ClientRectangle, but that's only for neatness. However you should use
fonts with an integer font size, and larger fonts are more reliable than
smaller ones. If it continues to give trouble, try adding options to the
StringFormat - I think the one to do with trimming spaces might be
relevant.
The method already takes account of the formatting factors that you have
mentioned.

Seems like a lot of extra work to inaccurately duplicate what can
accurately be done with a couple of api calls. What's your beef with
using the API?
 
T

Tom Shelton

I've got no beef with using the API - I never commented on that at all.

That's cool... As it turns out, this can be accurately done without API calls
at all :)
 
M

Mike Williams

I've got no beef with using the API - I never commented on that at
all. But when a suggestion was posted that the API was the way to
go, without any clues as to how to actually do it . . .

You are a liar, James. I made the suggestion to use the API and I made it
within hours of the OP posting his original question, almost two days before
your own first post, and I DID give some clues on how to achieve it, so you
are lying when you say I did not. I specifically suggested that he should
use the SendMessage API (clue number one) to send an EM_GETLINECOUNT message
(clue number two) and follow that with the appropriate number of EM_GETLINE
messages (clue number three).
and OP indicated he was unfamiliar with the API, I suggested that
it was probably not worth the effort in learning to use the API, and
offered an alternative for which there was a complete, functioning
example.

Again, you are a liar James. The above statement of yours is okay until you
reach the word 'functioning', and that's when the lie begins. The code you
offered does not work. It happens to come up with the correct solution in a
few specific cases, but in many other cases it fails completely, as I have
amply demonstrated in other replies in this thread. It even fails
dramatically in the very specific circumstances in which you later said it
was at its most reliable! Code such as that, which works in such an
unreliable fashion and which sometimes by chance produces the correct
solution but which often does not, is in fact far worse than code which
simply never works at all. And yet you have called it a 'complete
functioning example' and you are continuing to do so, even after it has been
clearly shown that it does not work. That is a lie, James. Making incorrect
statements is not always a lie of course, sometimes it is just a mistake,
but in this case it has already been clearly demonstrated that the code you
offered does not work and yet you are refusing to accept that fact and you
are continuing to tell people, including the OP, that it does work. That is
an outright lie, James.
When a subsequent suggestion was made that the example
didn't work I pointed out what was probably wrong with
the testing setup.

There was nothing wrong with the testing setup! There was in fact something
wrong with the code! You really are sticking your head deeply into the sand
James. Why are you doing that? I told you that it was unreliable and that
although it might work with the specific textbox setting and text string
contained in the example it would fail to work with many other strings and
with many other textbox settings. You then suggested that people might like
to make all sorts of different modifications to the code in an attempt to
get it working, but you failed to make such modifications yourself and we
still have not seen from you an example of that code that actually works. As
it stands, the code simply does not work, and so I again suggested that the
OP might like to go with the method that I had originally suggested (the
SendMessage method) which /does/ work.
It seems there are some here more interested in arguing
amongst themselves than in providing solutions to the
problems being posed.

The only person to whom that statement clearly applies is YOURSELF! There
have been two solutions posted here which actually work, one an API
SendMessage solution posted by Tom Shelton and the other a VB.Net
GetFirstCharIndexFromLine function solution posted by myself. It is YOU who
have shown more interest in arguing than in posting solutions, and it is YOU
who have not yet posted a solution that works. Just give it up, James. Get
your head out of the sand.

Mike
 

Ask a Question

Want to reply to this thread or ask your own question?

You'll need to choose a username for the site, which only take a couple of moments. After that, you can post your question and our members will help you out.

Ask a Question

Top