The Shed is going Social! Join us on FaceBook and Twitter and chime in on the conversation.
|
 |
|
Dev Shed Forums
> Programming Languages - More
> .Net Development
|
How can I get the rectangle of the text in a Textbox?
Discuss How can I get the rectangle of the text in a Textbox? in the .Net Development forum on Dev Shed. How can I get the rectangle of the text in a Textbox? .NET development forum discussing all .NET derivatives including C#, VB.NET, ASP.NET, ADO.NET and more. Learn the ins and outs of using the .NET framework.
|
|
 |
|
|
|
|
|

Dev Shed Forums Sponsor:
|
|
|

March 13th, 2009, 12:36 PM
|
|
Contributing User
|
|
Join Date: Nov 2005
Location: Northwest Florida
Posts: 208
  
Time spent in forums: 3 Days 22 h 8 m 28 sec
Reputation Power: 10
|
|
|
Is it possible to make an autosizing textbox?
I have a textbox that starts off a certain size. But, if the user types in enough text to require scrolling in the textbox, then I want to resize the textbox to contain all the text so that scrolling isn't necessary.
For most controls, I would simply use the Control.DisplayRectangle.Size property, but when used for a textbox, this is just the size of the textbox itself, regardless of whether the text extends beyond the bounds of the textbox or not.
I have tried using Graphics.MeasureString to get the size I need, and I get close but not quite what I need for professional results. And I know the textbox already knows the size I'm looking for, otherwise, it couldn't produce proper scrolling behavior when the text goes beyond the bounds of the textbox.
Any ideas?
--webdunce
Last edited by WebDunce : March 15th, 2009 at 07:25 PM.
Reason: better title
|

March 13th, 2009, 02:57 PM
|
|
Contributing User
|
|
Join Date: Mar 2009
Location: Lodz, Poland
Posts: 31
Time spent in forums: 11 h 19 m 57 sec
Reputation Power: 5
|
|
|
A possible solution and spooky idea
I can only suggest using that Graphics.MeasureString method with some additional margin. I scratched this code specially for you, so please tell me what you think.
Uploaded the class, along with runnable example:
speedyshare.com/181911114.html
An idea, if it's worth anything. When you make a class based on TextBox, you can override (and access?) some hidden properties. One such property is AutoSize, but this one is not relevant in this case. If you find a relevant member for solution, then create a class derived from Textbox (like AutosizeTextbox : TextBox).
C# Code:
Original
- C# Code |
|
|
|
/// <summary> /// Use this method only once, per textbox. Created event-handler will keep /// track of text changes, and autosize textbox automaticaly. /// The +6 makes a margin of error. You can customize the number if needed. /// </summary> public static void KeepAutoresizingThis(TextBox textbox) { Graphics graphics = textbox.CreateGraphics(); Size originalSize = textbox.Size; textbox.TextChanged += new EventHandler(delegate { int suggestedWidth = (int)graphics.MeasureString(textbox.Text, textbox.Font).Width + 6; if (suggestedWidth < originalSize.Width) textbox.Size = originalSize; else textbox.Width = suggestedWidth; }); }
Last edited by ArekBulski : March 13th, 2009 at 03:49 PM.
Reason: a note of overriding
|

March 13th, 2009, 03:57 PM
|
|
Contributing User
|
|
Join Date: Nov 2005
Location: Northwest Florida
Posts: 208
  
Time spent in forums: 3 Days 22 h 8 m 28 sec
Reputation Power: 10
|
|
thanks so much for the reply.
I am so sorry. I neglected to mention that the textbox will be of fixed width. So, I need to resize the height. I am currently using Graphics.MeasureString and I do add a bottom padding because Graphics.MeasureString seems to be getting a different height than I need (sometimes a bit smaller, sometimes a bit taller...depending on the actual text).
currently I'm using a method that returns a Size object (from which i get the Height property)
Code:
Size GetTextSize(TextBox tb)
{
// initialize some variables
Graphics g = tb.CreateGraphics();
SizeF size = g.MeasureString(tb.Text, tb.Font, tb.Width - 6); // the -6 should account for the diff between the textbox.clientrectangle.width and the textbox.width
int bottomPadding = 20;
int desiredHeight = (int)size.Height + 7 + bottomPadding; // the +7 should account for the diff between the textbox.clientrectangle.height and textbox.height
int heightOfSingleLineOfText = tb.Font.Height;
// if there are any characters at all...
if (tb.TextLength > 0)
{
// and if the last char is a newline...
if (tb.Text[tb.TextLength - 1] == '\n')
{
// add an extra line
desiredHeight += heightOfSingleLineOfText;
}
}
// cycle through the characters
for (int i = 0; i < tb.TextLength; i++)
{
// if the char is a newline
// and is preceded by a newline...
if (tb.Text[i] == '\n' && tb.Text[i - 1] == '\n')
{
// add an extra "line"
desiredHeight += heightOfSingleLineOfText;
}
}
size.Height = desiredHeight;
return new Size((int)size.Width, (int)size.Height);
}
but this is just not as nice as it could be if i could access the same rectangle that the textbox itself has already calculated and painted the text in.
thanks.
Last edited by WebDunce : March 13th, 2009 at 04:04 PM.
|

March 13th, 2009, 04:08 PM
|
|
Contributing User
|
|
Join Date: Nov 2005
Location: Northwest Florida
Posts: 208
  
Time spent in forums: 3 Days 22 h 8 m 28 sec
Reputation Power: 10
|
|
|
Also, I definitely thought about deriving a custom textbox control to access the protected properties and methods but i couldn't seem to find a property or method that seemed to do what I need
there might be one....i just couldn't find it.
Thanks again. All suggestions and ideas are definitely appreciated.
|

March 13th, 2009, 04:25 PM
|
|
Contributing User
|
|
Join Date: Nov 2005
Location: Northwest Florida
Posts: 208
  
Time spent in forums: 3 Days 22 h 8 m 28 sec
Reputation Power: 10
|
|
I have found a workaround....
RichTextBox has a ContentsResized event that provides the rectangle I am seeking.
However, I must use a textbox because it is already a heavily customized textbox and the functionality that I added to the textbox I have no intention of spending the time to so customize a RichTextBox object.
But, I can add a RichTextBox to the Form's class (but not add it to the form's controls, see, so it is not visible).
Anytime the textbox's text changes, i will update the richtextbox and anytime the richtextbox fires the ContentsChanged event, i will use that to update the size of the textbox...it works...i tried it already.
Code:
// the form was designed in the designer so the
// textbox1 object was created in the designer and
// it's code is not shown here
public partial class Form1 : Form
{
RichTextBox rtb = new RichTextBox();
public Form1()
{
InitializeComponent();
rtb.Size = textBox1.Size;
textBox1.TextChanged += new EventHandler(textBox1_TextChanged);
rtb.ContentsResized += new ContentsResizedEventHandler(rtb_ContentsResized);
}
void rtb_ContentsResized(object sender, ContentsResizedEventArgs e)
{
textBox1.Height = e.NewRectangle.Height + 7; // +7 accounts for some diff between the textbox's clientsize and actual size.
}
private void textBox1_TextChanged(object sender, EventArgs e)
{
rtb.Text = this.textBox1.Text;
}
|

March 13th, 2009, 05:20 PM
|
|
Contributing User
|
|
Join Date: Mar 2009
Location: Lodz, Poland
Posts: 31
Time spent in forums: 11 h 19 m 57 sec
Reputation Power: 5
|
|
|
Excellent solution, but incomplete?
I am impressed. You seem to be very efficient with finding solutions.  However I wasn't able to make your code working, without adding few lines. Also it needs to change RichTextBox.ScrollBars, or more text will cause over-grow of that e.NewRectangle. Here is my code, based on your excellent solution:
Code:
public partial class Form1 : Form
{
RichTextBox rtb = new RichTextBox();
public Form1()
{
InitializeComponent();
}
private void Form1_Load(object sender, EventArgs e)
{
rtb.Parent = this; /// needed to add this!
rtb.Visible = false; /// needed as well!
rtb.Size = textBox1.Size;
rtb.ScrollBars = RichTextBoxScrollBars.None; /// also this is important!
textBox1.TextChanged += new EventHandler(textBox1_TextChanged);
rtb.ContentsResized += new ContentsResizedEventHandler(rtb_ContentsResized);
}
void rtb_ContentsResized(object sender, ContentsResizedEventArgs e)
{
textBox1.Height = e.NewRectangle.Height + 7;
}
void textBox1_TextChanged(object sender, EventArgs e)
{
rtb.Text = textBox1.Text;
}
}
Would you want some help with putting this into a custom control? So you can add this custom-textbox from Designer Toolbox, without re-writing this code for every new textbox. 
|

March 13th, 2009, 06:58 PM
|
|
Contributing User
|
|
Join Date: Nov 2005
Location: Northwest Florida
Posts: 208
  
Time spent in forums: 3 Days 22 h 8 m 28 sec
Reputation Power: 10
|
|
|
Hey ArekBulski,
Thanks for your code. Your improvements to my code are very good and I will use them.
|

March 13th, 2009, 07:16 PM
|
|
Contributing User
|
|
Join Date: Mar 2009
Location: Lodz, Poland
Posts: 31
Time spent in forums: 11 h 19 m 57 sec
Reputation Power: 5
|
|
Quote: | Originally Posted by WebDunce Thanks for your code. Your improvements to my code are very good and I will use them. |
Ah, thanks! Me feels appreciated...
So what is your project all about? I am really curious. Besides I think I got too much spare time, so why not to spend some time here, working on some more solutions for you? 
|

March 13th, 2009, 07:23 PM
|
|
Contributing User
|
|
Join Date: Nov 2005
Location: Northwest Florida
Posts: 208
  
Time spent in forums: 3 Days 22 h 8 m 28 sec
Reputation Power: 10
|
|
(anyone thinking of using this code should first read post #13)
also, here is my attempt at a custom control. It seems to work good. You may improve it....
Code:
public partial class AutoSizingTextBox : UserControl
{
RichTextBox rtb = new RichTextBox();
TextBox tb = new TextBox();
[Browsable(true)]
public new int Height
{
get { return this.tb.Height; }
set { this.tb.Height = value; }
}
[Browsable(true)]
public new int Width
{
get { return this.tb.Width; }
set { this.tb.Width = value; }
}
public AutoSizingTextBox()
{
InitializeComponent();
this.Size = this.tb.Size;
this.rtb.Location = new Point(0, 0);
this.rtb.ScrollBars = RichTextBoxScrollBars.None;
this.rtb.Size = tb.Size;
this.rtb.ContentsResized += new ContentsResizedEventHandler(rtb_ContentsResized);
this.Controls.Add(rtb);
this.tb.Size = this.Size;
this.tb.Anchor = AnchorStyles.Left | AnchorStyles.Top | AnchorStyles.Right | AnchorStyles.Bottom;
this.tb.Location = new Point(0, 0);
this.tb.Multiline = true;
this.tb.TextChanged += new EventHandler(tb_TextChanged);
this.tb.SizeChanged += new EventHandler(tb_SizeChanged);
this.Controls.Add(tb);
this.tb.BringToFront();
}
void tb_SizeChanged(object sender, EventArgs e)
{
if (this.rtb.Width != this.tb.Width)
{
this.rtb.Width = this.tb.Width;
this.rtb.Height = this.tb.Height - 8;
}
this.Size = this.tb.Size;
}
void tb_TextChanged(object sender, EventArgs e)
{
this.rtb.Text = this.tb.Text;
}
void rtb_ContentsResized(object sender, ContentsResizedEventArgs e)
{
this.rtb.Height = e.NewRectangle.Height;
this.tb.Height = this.rtb.Height + 8;
}
}
Last edited by WebDunce : March 18th, 2009 at 10:35 PM.
|

March 13th, 2009, 07:28 PM
|
|
Contributing User
|
|
Join Date: Nov 2005
Location: Northwest Florida
Posts: 208
  
Time spent in forums: 3 Days 22 h 8 m 28 sec
Reputation Power: 10
|
|
Quote: | Originally Posted by ArekBulski Ah, thanks! Me feels appreciated...
So what is your project all about? I am really curious. Besides I think I got too much spare time, so why not to spend some time here, working on some more solutions for you?  |
Several years ago, I designed a website for a friend of mine. I built the site in such a way that he cannot easily edit it with Dreamweaver or Frontpage or a Flash editor.
I am trying to create an editor that will let my friend edit the website. An autosizing textbox will be an important part of the editor program (which I have spent years working on...  )
|

March 13th, 2009, 08:26 PM
|
|
Contributing User
|
|
Join Date: Mar 2009
Location: Lodz, Poland
Posts: 31
Time spent in forums: 11 h 19 m 57 sec
Reputation Power: 5
|
|
Quote: | Originally Posted by WebDunce I am trying to create an editor that will let my friend edit the website. An autosizing textbox will be an important part of the editor program (which I have spent years working on...  ) |
 I am even more amazed now. Wish you good luck, and of course I will be here, trying to advise when you need it...
Quote: | also, here is my attempt at a custom control. It seems to work good. You may improve it.... |
As for this class... it really works! I tried to make a class derived from TextBox, instead from UserControl. It is smaller, but I won't hide that it does not work.  wtf
C# Code:
Original
- C# Code |
|
|
|
public class AutoSizingTextBox : TextBox { RichTextBox rtb = new RichTextBox(); public AutoSizingTextBox() { this.Multiline = true; this.TextChanged += new EventHandler(AutoSizingTextBox_TextChanged); this.SizeChanged += new EventHandler(AutoSizingTextBox_SizeChanged); //this.FindForm().Controls.Add(this); rtb.Location = new Point(0, 0); rtb.Parent = this.Parent; rtb.Visible = false; rtb.ScrollBars = RichTextBoxScrollBars.None; rtb.Size = this.Size; rtb.ContentsResized += new ContentsResizedEventHandler(rtb_ContentsResized); //this.FindForm().Controls.Add(rtb); } void AutoSizingTextBox_SizeChanged(object sender, EventArgs e) { rtb.Size = this.Size; } void AutoSizingTextBox_TextChanged(object sender, EventArgs e) { rtb.Text = this.Text; } void rtb_ContentsResized(object sender, ContentsResizedEventArgs e) { rtb.Height = e.NewRectangle.Height; this.Height = e.NewRectangle.Height + 8; } }
|

March 16th, 2009, 09:53 PM
|
|
Contributing User
|
|
Join Date: Nov 2005
Location: Northwest Florida
Posts: 208
  
Time spent in forums: 3 Days 22 h 8 m 28 sec
Reputation Power: 10
|
|
just as a note to myself and anyone else who may be trying to create an autosizing textbox...a google search has turned up a bit of code that uses windows messages and centers around the EM_GETLINECOUNT message.
i haven't had a chance to really look into this but it looks promising...
Click here for the code example.
|

March 17th, 2009, 04:41 AM
|
|
Contributing User
|
|
Join Date: Nov 2005
Location: Northwest Florida
Posts: 208
  
Time spent in forums: 3 Days 22 h 8 m 28 sec
Reputation Power: 10
|
|
so i rewrote my AutosizeableTextbox based on the code i referenced in the above post that i found in a google search. it uses the SendMessage() method and is much more efficient than the original usercontrol i made by combining a textbox and a richtextbox. You have to link to the user32.dll and use the EM_GETLINECOUNT message
c# Code:
Original
- c# Code |
|
|
|
using System; using System.Collections.Generic; using System.Text; using System.Windows.Forms; using System.Drawing; using System.ComponentModel; using System.Runtime.InteropServices; namespace AutoSizeableTextBox { public class AutosizeableTextbox : TextBox { // link to the SendMessage function in the user32.dll [DllImport("user32.dll", CharSet = CharSet.Auto, SetLastError = false)] static extern IntPtr SendMessage(IntPtr hWnd, Int32 Msg, IntPtr wParam, IntPtr lParam); // set up some variables const int TEXTBOX_PADDING = 7; int minWidth = 10; // during instantiation, this is set to the width of a double-u int minHeight = 20; AutoSizeMode autosizeMode = AutoSizeMode.GrowOnly; bool autosize = true; // set up some useful properties public int MinimumWidth { get { return this.minWidth; } } public int MinimumHeight { get { return this.minHeight; } } [Browsable(true), Category("Autosize Properties")] public AutoSizeMode AutoSizeMode2 // see note by AutoSize2 { get { return autosizeMode; } set { autosizeMode = value; ResizeMe(); } } [Browsable(true), Category("Autosize Properties")] public bool AutoSize2 // when i called it AutoSize, whether i used "new" or "override," it would always reset itself to false at runtime. { get { return autosize; } set { autosize = value; ResizeMe(); } } // constructor public AutosizeableTextbox() { this.minWidth = TextRenderer.MeasureText("W", this.Font).Width; this.minHeight = (int)(this.FontHeight + TEXTBOX_PADDING); //this.MinimumSize = new Size(this.MinimumSize.Width, minHeight); this.FontChanged += new EventHandler(AutosizeableTextbox_FontChanged); this.TextChanged += new EventHandler(AutosizingTextbox_TextChanged); this.SizeChanged += new EventHandler(AutosizeableTextbox_SizeChanged); } void AutosizeableTextbox_FontChanged(object sender, EventArgs e) { this.minWidth = TextRenderer.MeasureText("W", this.Font).Width; this.minHeight = (int)(this.FontHeight + TEXTBOX_PADDING); ResizeMe(); } // public methods public void ForceResize() { ResizeMe(); } public void ForceResize(int minWidth, int minHeight, int width, int height) { this.MinimumSize = new Size(minWidth, minHeight); this.Size = new Size(width, height); ResizeMe(); } // event handlers void AutosizeableTextbox_SizeChanged(object sender, EventArgs e) { ResizeMe(); } void AutosizingTextbox_TextChanged(object sender, EventArgs e) { ResizeMe(); } // resize method void ResizeMe() { if (this.MinimumSize.Height < this.minHeight) this.MinimumSize = new Size(this.MinimumSize.Width, this.minHeight); if (this.MinimumSize.Width < this.minWidth) this.MinimumSize = new Size(this.minWidth, this.MinimumSize.Height); if (autosize) { if (this.Multiline) ResizeWithMultilineOn(); else ResizeWithMultilineOff(); } } // resize when multiline is true void ResizeWithMultilineOn() { double lineHeight = this.FontHeight; int lineCount = this.GetLineCount(); int newHeight = (int)(lineCount * lineHeight) + TEXTBOX_PADDING; if (newHeight < this.Height) { if (autosizeMode == AutoSizeMode.GrowAndShrink) this.Height = newHeight; } else { this.Height = newHeight; } } // resize when multiline is false void ResizeWithMultilineOff() { if (this.TextLength > 0) { int newWidth = TextRenderer.MeasureText(this.Text, this.Font).Width + this.minWidth; if (newWidth < this.Width) { if (autosizeMode == AutoSizeMode.GrowAndShrink) { this.Width = newWidth; } } else { this.Width = newWidth; } } else { if (autosizeMode == AutoSizeMode.GrowAndShrink) { this.Width = this.minWidth; // width of a double-u } } } // gets the number of lines in the textbox int GetLineCount() { const Int32 EM_GETLINECOUNT = 186; return (int)SendMessage(this.Handle, EM_GETLINECOUNT, IntPtr.Zero, IntPtr.Zero); } } }
Then you have to multiply the number of lines by the height of the textbox's fontheight and add a little extra for padding etc, but it works just fine.
click here for a demo project using the autosizing textbox (the link will not work after 7 days of no downloads...so some may find the link broken in the future)
Last edited by WebDunce : March 18th, 2009 at 08:32 AM.
|

March 18th, 2009, 09:43 AM
|
|
Contributing User
|
|
Join Date: Mar 2009
Location: Lodz, Poland
Posts: 31
Time spent in forums: 11 h 19 m 57 sec
Reputation Power: 5
|
|
I tested WebDunce's code and it really works! Congrats!
Thanks for uploading the whole solution too!
Best regards,
Arek Bulski.
|

January 12th, 2012, 08:05 PM
|
|
Registered User
|
|
Join Date: Jan 2012
Posts: 1
Time spent in forums: 7 m 4 sec
Reputation Power: 0
|
|
Very Nice! Thanks!
I registered with Dev Shed just so I could give kudos to WebDunce. I swapped your control in place of a standard textbox and voila: not a hitch. You may be a dunce at web, but not at desktop app development! Thanks very much for contributing this!
|
Developer Shed Advertisers and Affiliates
| Thread Tools |
Search this Thread |
|
|
|
| Display Modes |
Rate This Thread |
Linear Mode
|
|
Posting Rules
|
You may not post new threads
You may not post replies
You may not post attachments
You may not edit your posts
HTML code is Off
|
|
|
|
|