|
Note: This is an unedited contribution. If this article is inappropriate,
needs attention or copies someone else's work without reference then please
Report This Article
Introduction This article describes how to set the height of a single-line textbox control. The TextBox control in C# (and VB.Net) ignores the height property and adjusts it to fit the font height. By calculating the exact font size needed, we can set the TextBox to a desired height. Background
I had a c# project that required a single-line text box with adjustable
height. I found many examples of how to adjust the width, but nothing
on how to dynamically change the height of a text box. I did not want
to use multi-line because I wanted to keep the auto-complete features of
the single-line text box. Using the code Single-line textbox height is set by the size of the font, not the
TextBox.Height property. This makes it difficult if you are looking for
an exact height. Luckily, the font property uses a float for the font
size (emSize). You can use fractions of fonts to fine-tune the textbox
height.
The calculation the textbox uses to determine its height is:
Height = ( Font Size * Font Line Spacing / Font Em Height ) + 7 - Font Size - It is easiest to measure font size in pixels so you do not have to factor in screen dpi.
- Font Line Spacing - The distance, in design units, between two consecutive lines of text.
- Font Em Height - height, in design units of the font's widest letter - typically the letter M.
Text
boxes have a 3-pixel lower and 4-pixel upper white space around the
font height. Therefore, the calculation increases the height by 7 pixels. We can reverse this calculation to obtain the font size needed for a desired height: Font Size = ( height - 7 ) * Font Em Height / Font Line Spacing This method will return a font object that will set the size of your text box: private Font GetFontForTextBoxHeight(int TextBoxHeight, Font OriginalFont)
{
float desiredheight = (float)TextBoxHeight;
Font fnt = new Font(OriginalFont.FontFamily,
OriginalFont.Size,
OriginalFont.Style,
GraphicsUnit.Pixel);
if (desiredheight < 8)
desiredheight = 8;
float FontEmSize = fnt.FontFamily.GetEmHeight(fnt.Style);
float FontLineSpacing = fnt.FontFamily.GetLineSpacing(fnt.Style);
float emSize = (desiredheight - 7) * FontEmSize / FontLineSpacing;
fnt = new Font(fnt.FontFamily, emSize, fnt.Style, GraphicsUnit.Pixel);
return fnt;
}
Whenever you have to set a textbox size, set the font property using the above method: YourTextBox.Font = GetFontForTextBoxHeight(DesiredHeight, YourTextBox.Font);
Using the Control The Adjustable Height Textbox is a user control that you can add to your form. It behaves the same as a regular TextBox except: - You can set the height in the design view via the Size_AdjustableHeight property.
- The control, if in single-line mode, will respond to docking and anchoring.
To use the control: - Right-click on your toolbox in Design view
- Click Browse. Navigate to AdjustableHeightTextbox.dll. Hit OK.
- Add the control to your form.
Enjoy! Points of Interest - I learned that the .Net TextBox code is basically a wrapper for the old MFC textbox control. That is why it is not very customizable.
- The GetFontForTextBoxHeight() routine, with some tweaking, could be used for a ComboBox control. The height calculations are close, but still off by a couple of pixels.
There is a lack of information on the inner workings of the textbox control (as with most of the standard .Net controls). I hope this article has saved you hours of searching. History2008.09.07 - Posted original article 2008.09.10 - Added a user control version of the textbox and a test project.
| You must Sign In to use this message board. |
|
| | Msgs 1 to 17 of 17 (Total in Forum: 17) (Refresh) | FirstPrevNext |
|
 |
|
|
The textbox control has an AutoSize property which can be disabled. However, this method will not change the font size - so you will get a great big textbox with an itty-bitty font.
It is a hidden property which will not show up in the IDE, but it will not throw an error if used in code. To set the height of a TextBox by disabling auto-sizing:
textBox1.AutoSize = false; textBox1.Height = 50;
This Visual Studio Magazine article[^], from which this comment was based, includes a great explanation of the back-end code and how you can access it in VS 2008.
|
| Sign In·View Thread·PermaLink | |
|
|
|
 |
|
|
I'll have to remember this.
There have been a couple of time where that's what I wanted to do with a TextBox, but it never occurred to me to set textBox1.AutoSize = false;
|
| Sign In·View Thread·PermaLink | |
|
|
|
 |
|
|
I've incorporated this technique in with your other ideas I've used in my TextBox control. You may (or may not) be interested:
private int default_Height = -1; // Generally: [this.Height = 20;] [System.ComponentModel.Category("Layout")] [System.ComponentModel.Description( "Set the TextBox.Height to a specific value " +"via disabling of the .AutoSize property." )] public int Size_ManualHeight { get {return this.Height;} set { if (value != this.Height) { if (default_Height <= 0) { default_Height = this.Height; }
if( (value > 0) && (value != default_Height) ) { if (value > default_Height) { this.AutoSize = false; this.Height = value; } else { this.Font = GetFontForTextBoxHeight( value, (this.BorderStyle != System.Windows.Forms.BorderStyle.Fixed3D), this.Font ); this.AutoSize = false; this.Height = value; } } else if (!this.AutoSize) { this.Font = GetFontForTextBoxHeight( default_Height, (this.BorderStyle != System.Windows.Forms.BorderStyle.Fixed3D), this.Font ); this.Height = default_Height; // Generally: [this.Height = 20;] this.AutoSize = true; } } } } [System.ComponentModel.Category("Layout")] [System.ComponentModel.Description( "Set the TextBox.Height to a specific value " +"via manipulation of its .Font properties." )] public int Size_AdjustedHeight { get {return this.Height;} set { if (value != this.Height) { if (default_Height <= 0) { default_Height = this.Height; }
this.Font = GetFontForTextBoxHeight( value, false, this.Font ); } } } private static System.Drawing.Font GetFontForTextBoxHeight( int TextBoxHeight, bool ExtraFontPoints, System.Drawing.Font OriginalFont ) { // ...
// emSize is the target font size. TextBoxes have a total of // 7 pixels above and below the FontHeight of the font. //float emSize = (desiredheight - 7) * FontEmSize / FontLineSpacing; float desiredoffset = 7f; if (ExtraFontPoints) { desiredoffset = 5f; } float emSize = (desiredheight - desiredoffset) * FontEmSize / FontLineSpacing;
// ... }
As part of incorporating this technique, I also made a slight mod to the [GetFontForTextBoxHeight()] method, as shown above. My thought is that if I'm using this technique of disabling of the .AutoSize property to explicitly set the textbox .Height *and* the textbox's .BorderStyle != System.Windows.Forms.BorderStyle.Fixed3D then I can squeeze in a couple more pixels of size for the text. My testing of this idea has worked fine.
edit: On these extra points for the fontsize:
The reason to call the [GetFontForTextBoxHeight()] method in the first place from the [Size_ManualHeight] set accessor when [(value >= default_Height)] is because otherwise the fontsize is too large for the height of the textbox (and the displayed value will probably be clipped). At the same time, if the value set to [Size_ManualHeight] is much less than 15, the text value quickly becomes unreadable. The idea of allowing for 2 extra pixels was a way to somewhat alleviate this. Though, the idea doesn't work out well if the textbox's .BorderStyle == System.Windows.Forms.BorderStyle.Fixed3D
The reason to pass the value 'false' in the call to the [GetFontForTextBoxHeight()] method which made from the [Size_AdjustedHeight] set accessor is, of course, because that logic does not override the [this.AutoSize] property.
|
| Sign In·View Thread·PermaLink | |
|
|
|
 |
|
|
Did you find this formula somewhere in MSDN or reverse-engineered the answer out of somewhere or is it just something you cooked up? I always thought that text box height should be relative to Dialog Base Units if it is used in a dialog. (i.e. it is exactly 8 or so Dialog Base Units) This article also says how you can get Dialog Base Units for non-system fonts: http://support.microsoft.com/kb/125681[^] Please ignore me if this is about a different type of text-box. 
I'm gonna hold my vote until I get an answer...
|
| Sign In·View Thread·PermaLink | 1.00/5 (1 vote) |
|
|
|
 |
|
|
sk8er_boy287 wrote: I'm gonna hold my vote until I get an answer...
That's certainly better than being an auto-Univoter!
|
| Sign In·View Thread·PermaLink | |
|
|
|
 |
|
|
Let me guess... like whomever uni-voted my question? 
"This method will return a font object that will set the size of your text box:"
What does that even mean? You can't set the size of anything using a font. 
Wow! I just realized this article is terribly titled and I misinterpreted the whole lot of it! It's about setting Font size, not TextBox size. Well, there goes my uni-vote of the day. 
|
| Sign In·View Thread·PermaLink | |
|
|
|
 |
|
|
sk8er_boy287 wrote: What does that even mean? You can't set the size of anything using a font. Hmmm
Try to adjust the height property of a textbox (with multi-line set to false). The height will not change. Now adjust the font size - the height of the textbox changes to accommodate the taller font. Now set it by fractions of pixels - the textbox height will change ever so slightly. The font calculations were to calculate just the right font size to make the textbox increase to the desired height.
The goal of the article is not about setting the font size, it is about changing the height of a cantankerous control that does not give us an obvious way to set the height.
|
| Sign In·View Thread·PermaLink | |
|
|
|
 |
|
|
I don't think you understand what I'm trying to say. The article is titled wrong. What you're doing is calculating font size based on given TextBox size, while the title says the exact opposite.
Also, your method might not always work. Try changing the display DPI setting to something significantly different than the default 96 (you may need to reboot), then see if it still works... The fact that you're using hard-coded constants is a throw-off. I've seen this kind of issues in lots of software, where they ignore the DPI setting and do this kind of wrong calculations...
You can always change the height of a control or any other window, for that matter, from code... I don't know much about C# properties, though, so I might be wrong.
|
| Sign In·View Thread·PermaLink | |
|
|
|
 |
|
|
Actually, DPI settings were taken into account in the calculations by switching the font to GraphicsUnit.Pixel. The Em values remain the same, independent of DPI settings. Where I would have gotten into trouble with DPI settings is if I had retained the GraphicsUnit.Point, where a calculation from point-to-pixel (or inch, or world, or furlongs, for that matter ) would factor in the DPI settings.
I did try it after reading your comment. It does still work, even at 200% DPI, which btw, is quite fugly.
sk8er_boy287 wrote: You can always change the height of a control or any other window, for that matter, from code... I don't know much about C# properties, though, so I might be wrong.
Give it a try in C#.
modified on Wednesday, September 10, 2008 1:07 PM
|
| Sign In·View Thread·PermaLink | |
|
|
|
 |
|
|
sk8er_boy287 wrote: Let me guess... like whomever uni-voted my question?
Your guess is incorrect. Not all 1-votes are "auto univotes." Some are deserved.
|
| Sign In·View Thread·PermaLink | |
|
|
|
 |
|
|
I've more than over-replied on this, so I won't bother again. If I'm wrong with all my mumbojumbo, just tell me and put me out of my suffering. 
|
| Sign In·View Thread·PermaLink | |
|
|
|
 |
|
|
The MSDN article How to Obtain Font Metrics[^] helped me come up with the FontEmSize/FontLineSpacing calculations. The rest was trial and error. Oh, and counting pixels because my calculation was off by 7 pixels. 
sk8er_boy287 wrote: This article also says how you can get Dialog Base Units for non-system fonts: http://support.microsoft.com/kb/125681[^]
I believe that the article is about how to calculate the size of a dialog box (MessageBox, etc.) based on the font. I do not think that these calculations are for textbox controls. If my assumption is incorrect (which has been known to occur from time to time ), please correct me.
modified on Tuesday, September 9, 2008 10:24 AM
|
| Sign In·View Thread·PermaLink | |
|
|
|
 |
|
|
 |
|
|
Would it be practical to have a container control which contains one single-line textbox and includes wrapper properties so that changes to most properties (e.g. Text) will affect the textbox within? That would allow the height of the container to be set independent from the font size, and would allow for top, bottom, or middle alignment. There could also be a 'measure font size' event that would allow the parent form to have the font size vary based upon height and width (e.g. use the largest font that is reasonable given both height and width constraints).
|
| Sign In·View Thread·PermaLink | |
|
|
|
 |
|
|
A suggestion:
What not change your article to be about a subclassed "adjustable-height TextBox?"
Your new class could inherit from the standard TextBox and expose a single new property (*), which in the properties window of the IDE would display the textbox instance's .Height value -- and when a different value is entered in the new property, the class would invoke the method you've shown us, something like this:
[System.ComponentModel.Category("Layout")] [System.ComponentModel.Description("Set the TextBox.Height to a specific value.")] public int Size_AdjustedHeight { get {return this.Height;} set { if (value != this.Height) { this.Font = GetFontForTextBoxHeight(value, this.Font); } } }
This would have the advantage of explicitly visually showing you in the developement environment what the result will be. And, of course, you could dynamically set this property's value in the code of your application thusly: [textbox.Size_AdjustedHeight = 24;].
edit: If you like this suggestion, it would probably be good to make the GetFontForTextBoxHeight() method static in the new class. My thought here is that if one uses several instances of this new class on a form, only one copy the "resize" code is needed in memory.
(*) Say, "AdjustedHeight" Or, to cause alphabetic placement in the properties list with "Size", "Size_AdjustedHeight"
EDIT: Of course, regardless of how one uses this method, it *does* modify the textbox's .Font.Size
In playing around with it, it seems that when the "Size_AdjustedHeight" value is set to 26 or higher, the target textbox's .Font looks bolded compared to a textbox which hasn't been so adjusted.
modified on Monday, September 8, 2008 10:21 AM
|
| Sign In·View Thread·PermaLink | |
|
|
|
 |
|
|
Supercat9 & Ilíon - great suggestions. I'm working on a user control now. I'll post an update when finished. Thanks for the input!
|
| Sign In·View Thread·PermaLink | |
|
|
|
 |
|
|
hayes.adrian wrote: Supercat9 & Ilíon - great suggestions. I'm working on a user control now. I'll post an update when finished. Thanks for the input!
Done! Enjoy!
Thanks again for your input. Great suggestions.
|
| Sign In·View Thread·PermaLink | |
|
|
|
 |
|
|
General News Question Answer Joke Rant Admin
|