19

In a C# form, I have a panel anchored all sides, and inside, a textbox, anchored top/left/right.

When text gets loaded into the textbox, i want it to auto expand itself vertically so that I don't need to scroll the textbox (scroll the panel at most, if there is more text that doesn't fit the panel). is there any way to do this with a textbox? (i'm not constrained to use this control so if there's another control that fits the description, feel free to mention it)

Masoud
  • 8,020
  • 12
  • 62
  • 123
Andrei S
  • 6,486
  • 5
  • 37
  • 54

6 Answers6

34

The current selected answer does NOT handle lines with no spaces such as "jjjjjjjjjjjjjjjjjjjj"x1000 (think about what would happen if someone pasted a URL)

This code solves that problem:

private void txtBody_TextChanged(object sender, EventArgs e)
{
    // amount of padding to add
    const int padding = 3;
    // get number of lines (first line is 0, so add 1)
    int numLines = this.txtBody.GetLineFromCharIndex(this.txtBody.TextLength) + 1;
    // get border thickness
    int border = this.txtBody.Height - this.txtBody.ClientSize.Height;
    // set height (height of one line * number of lines + spacing)
    this.txtBody.Height = this.txtBody.Font.Height * numLines + padding + border;
}
David Sherret
  • 101,669
  • 28
  • 188
  • 178
  • The best answer. Minor tweak: GetLineFromCharIndex() does not count trailing crlf, so check for that and add +1 if needed. – amonroejj Nov 19 '19 at 21:37
  • @amonroejj That's strange. I just tried this out and it's not necessary for me. I'm using .NET Framework 4.7.2. – David Sherret Nov 28 '19 at 04:18
  • is this for .Net Core? – Mike W Sep 18 '20 at 19:42
  • This answer handles empty lines etc well, but truncates descenders on the final line of text. I changed padding to 6, which works for my application, but is a bit of a frig really although it seems to work for font sizes 7pt to 48pt. – Skyfish Jan 09 '23 at 13:14
26

I'll assume this is a multi-line text box and that you'll allow it to grow vertically. This code worked well:

    private void textBox1_TextChanged(object sender, EventArgs e) {
        Size sz = new Size(textBox1.ClientSize.Width, int.MaxValue);
        TextFormatFlags flags = TextFormatFlags.WordBreak;
        int padding = 3;
        int borders = textBox1.Height - textBox1.ClientSize.Height;
        sz = TextRenderer.MeasureText(textBox1.Text, textBox1.Font, sz, flags);
        int h = sz.Height + borders + padding;
        if (textBox1.Top + h > this.ClientSize.Height - 10) {
            h = this.ClientSize.Height - 10 - textBox1.Top;
        }
        textBox1.Height = h;
    }

You ought to do something reasonable when the text box is empty, like setting the MinimumSize property.

Hans Passant
  • 922,412
  • 146
  • 1,693
  • 2,536
3

I'd suggest using Graphics.MeasureString.

First you create a Graphics object, then call MeasureString on it, passing the string and the textbox's font.

Example

string text = "TestingTesting\nTestingTesting\nTestingTesting\nTestingTesting\n";

// Create the graphics object.
using (Graphics g = textBox.CreateGraphics()) {        
    // Set the control's size to the string's size.
    textBox.Size = g.MeasureString(text, textBox.Font).ToSize(); 
    textBox.Text = text;
}

You could also limit it to the vertical axis by setting only the textBox.Size.Height property and using the MeasureString overload which also accepts int width.

Edit

As SLaks pointed out, another option is using TextRenderer.MeasureString. This way there's no need to create a Graphics object.

textBox.Size = TextRenderer.MeasureString(text, textBox.Font).ToSize(); 

Here you could limit to vertical resizing using Hans' technique, passing an extra Size parameter to MeasureString with int.MaxValue height.

GeReV
  • 3,195
  • 7
  • 32
  • 44
2

You can use a Label, and set AutoSize to true.

SLaks
  • 868,454
  • 176
  • 1,908
  • 1,964
0

You could anchor it to the bottom, that will ensure that the textbox is resized vertically when ever the form to which it belongs is resized. Also, a textbox that changes its size might not be an elegant thing since it might disrupt the way that other components are displayed. Why don't you give it a maximum size instead of having it resized?

npinti
  • 51,780
  • 5
  • 72
  • 96
0

Try this approach:

aspx.cs code

protected int GetRows(object value) {
        if (value == null || string.IsNullOrWhiteSpace(value.ToString()))
            return 1;

        var contentTrimmed = value.ToString().Replace('\t', ' ').Replace('\r', ' ').Replace('\n', ' ').Trim();

        var length = (decimal)contentTrimmed.Length;
        if (length == 0)
            return 1;

        int res = 0;
        decimal maxLength = 56;
        using (System.Drawing.Graphics graphics = System.Drawing.Graphics.FromImage(new Bitmap(1, 1)))
        {
             SizeF sizeRef = graphics.MeasureString("W", new Font("Segoe UI", 13, FontStyle.Regular, GraphicsUnit.Pixel));
             maxLength = maxLength * (decimal)sizeRef.Width;

             SizeF size = graphics.MeasureString(contentTrimmed, new Font("Segoe UI", 13, FontStyle.Regular, GraphicsUnit.Pixel));
             length = (decimal)size.Width;
        }

        res = (int)Math.Round(length / (decimal)maxLength, MidpointRounding.AwayFromZero);
        if (res == 0)
            return 1;

        return res;
 }

aspx code

<asp:TextBox ID="txtValue" TextMode="MultiLine" Text='<%# Eval("Value") %>' runat="server" MaxLength="500" Width="700px" Rows='<%# GetRows(Eval ("Value")) %>' ></asp:TextBox>
Oblivious Sage
  • 3,326
  • 6
  • 37
  • 58