1

I would like to determine what the best method is for determining the number of displayed (visible due to scrolling) lines on a multiline winforms TextBox control.

The current method I am using is grossly inaccurate (as are other methods I have tried):

var size = TextRenderer.MeasureText(
    textBox.Text, textBox.Font, textBox.ClientSize, TextFormatFlags.TextBoxControl);
int lines = textBox.Lines.Length;
int lineHeight = (size.Height / lines);

// Value assigned to 'lines' does not reflect number of lines displayed:
int lines = (textBox.Height / lineHeight);

The method to calculate number of lines visible must take into account things such as the scroll bars of the text box, and nuances of text box display including lines that would only be partially visible are not displayed.

Any solution will be greatly appreciated!

Update:

Tried the following calculation as suggested, but still remain with inaccurate result:

int lines = (textBox.Height / textBox.Font.Height);

I've made a simple test program, here is a screenshot, both examples above produce similar results:

screenshot of TextBox height calculation results

The calculated number of lines using the either method often does not reflect number of actual lines displayed as the height is increased or decreased.

Lemonseed
  • 1,644
  • 1
  • 15
  • 29
  • Instead of your lengthy calculation, try `textBox.Font.Height` property as line height. – weaknespase Mar 13 '16 at 19:00
  • Thank you for the suggestion; results updated in question above; still remain with inaccurate calculation of number of lines. – Lemonseed Mar 13 '16 at 19:08
  • It is not straight-forward, TextBox uses an unspecified margin. You'll have to probe, use TextBox.GetCharacterIndexFromPoint and GetLineIndexFromCharacterIndex.. Heck of an XY problem btw. – Hans Passant Mar 13 '16 at 19:15
  • Haha, thank you. Yes I had feared I was asking an XY problem; maybe so. Real problem is trying to get scroll position. Currently using GetScrollRange() and GetScrollPos() from user32, but return in terms of lines. No way to know how many lines are displayed to complete the calculation. – Lemonseed Mar 13 '16 at 19:20
  • `Font.Height` property is float, while GDI functions draw text only on pixel boundary, try rounding it. Borders are included in height of control, you need to get size of borders and subtract them. `SystemInformation.BorderSize` and `SystemInformation.Border3DSize` will help with that. – weaknespase Mar 13 '16 at 19:21
  • 1
    http://stackoverflow.com/questions/1773400/winforms-richtextbox-how-can-i-scroll-the-caret-to-the-middle-of-the-richtextbo it works for textbox too – M.kazem Akhgary Mar 13 '16 at 19:48
  • You can send `EM_GETRECT` message to the text box and then divide height of result rectangle to font height of the text box. The result tells you how many line can be show in visible area of `TextBox`. – Reza Aghaei Mar 13 '16 at 23:50
  • I think people forget about the space between the lines. – JohnSaps Mar 14 '16 at 14:22

3 Answers3

3

You can send EM_GETRECT message to the text box and then divide height of result RECT to Font.Height of the text box.
The result tells you how many line can be show in visible area of TextBox.

const int EM_GETRECT = 0xB2;

[StructLayout(LayoutKind.Sequential)]
struct RECT
{
    public int Left, Top, Right, Bottom;
}

[DllImport("user32.dll", CharSet = CharSet.Auto)]
static extern IntPtr SendMessage(IntPtr hWnd, int Msg, IntPtr wParam, ref RECT lParam);

And the usage:

var rect = new RECT();
SendMessage(this.textBox1.Handle, EM_GETRECT, IntPtr.Zero, ref rect);
var count = (rect.Bottom - rect.Top)/this.textBox1.Font.Height;
Reza Aghaei
  • 120,393
  • 18
  • 203
  • 398
3

Use MeasureText to determine the height of a line and ClientSize.Height for the height within the textbox, which is available for drawing the text:

        int lineHeight = TextRenderer.MeasureText("X", this.textbox1.Font).Height;
        double linesPerPage = 1.0*this.textbox1.ClientSize.Height / lineHeight;

This works in the same way for a RichTextBox.

MiB_Coder
  • 865
  • 1
  • 7
  • 20
0

Instead of measuring the height of the lines you can do the following:

Lets say h is the height of the "visible" textBox, and H is the height of the whole text box. Lets call l the number of visible lines, and L the number of lines on total.

We can calculate the ratio h/H. I reckon that the same ratio would hold for the number of lines as well. So h/H should be equal to l/L. You have h H L and you need l.

Eminem
  • 870
  • 5
  • 20
  • Thank-you; yes this *should* work, but using `float H = TextRenderer.MeasureText(textBox.Text, textBox.Font, textBox.ClientSize, TextFormatFlags.TextBoxControl).Height; float h = textBox.Height; float L = textBox.Lines.Length; var l = (h*L/H);` still results in inaccurate number of lines. – Lemonseed Mar 13 '16 at 19:31