How to use MeasureString in C# to set table column width? - c#

I have a pretty quick (and I'm hoping basic) question. I'm modifying some C# code for my company's website. The code draws a table for me in fixed columns, the data for which is pulled from a database. The height of the each column of the table is fixed (currently), and I need to change it so if the string is a certain length, and therefore wraps, the second line of text is viewable (instead of hidden by the next row).
From my research, it seems like I can use MeasureString (since I know the font and string) to see if the string is longer/wider than my set table column, and change the height of the row if this is so. However, I'm very new to C# programming (and haven't done much programming overall in years, besides web stuff), so I'm not sure how to get all of this implemented. I have the logic in place, and I know how to change the height, I just need to know how to get an actual number I can use logic against using the MeasureString method (and how to instantiate any variables and functions I might need to use that method).

I believe you need to use this overload for MeasureString(string,font,int):
The width parameter specifies the maximum value of the width component
of the returned SizeF structure (Width). If the width parameter is
less than the actual width of the string, the returned Width component
is truncated to a value representing the maximum number of characters
that will fit within the specified width. To accommodate the entire
string, the returned Height component is adjusted to a value that
allows displaying the string with character wrap.
-- From Above Linked MSDN Page (Emphasis mine)
// Measure string (you'll need to instansiate your own graphics object,
// since you wont have PaintEventArgs)
SizeF stringSize = new SizeF();
stringSize = e.Graphics.MeasureString(measureString, stringFont, stringWidth);
int cellHeight = stringSize.Height;

You can either use e.Graphics.MeasureString() or TextRenderer.MeasureText()
Differences and advantages of each of them are describe here:
TextRenderer.MeasureText and Graphics.MeasureString mismatch in size
There you will also find usage examples, which I would skip here to avoid duplication.

MSDN gives an example where you calculate this by registering an event handler to the OnPaint method of your control (in instantiated controls), or by overriding the OnPaint method (in inherited controls), or by overriding the OnPaint method of your form (not best practice since you probably don't want to do this for EVERY form repaint). The OnPaint method will give you access to a graphics object so you can call the MeasureString method.
Consider the following:
public partial class Form1 : Form
{
public Form1()
{
InitializeComponent();
label1.Paint += new PaintEventHandler(label1_Paint);
}
void label1_Paint(object sender, PaintEventArgs e)
{
SizeF size = e.Graphics.MeasureString(label1.Text, label1.Font);
this.label1.Width = (int)size.Width;
this.label1.Height = (int)size.Height;
}
}

Related

Controlling how, when, and if child controls are drawn (.NET)

I am writing an application in .NET that has a plugin interface. That plugin interface provides several ways to draw information (controls) onto the surface of the application window. While there are several reasons why I am doing this, the main reason is to provide custom colorization to text, either through the use of a graphic or directly manipulating the color of the text based on the background color. I do this through the use of a "text mask" which is a black and white bitmap that works as an "alpha" map to let the Paint method know where to apply the texture/color changes.
The plugin developer has the option of using regular text (such as with a label), mask text (which is drawn to the mask rather than as a regular control), OR letting the user decide. To go along with this, I have provided a modified label class that can either be drawn "normally' (when the text mask is not set for the control), or to the text mask when the User OR Developer decides (depending on what the plugin developer wishes to offer to the user). Here is the class's code so that you understand how this is being done:
public class MaskingLabel : Label
{
private static readonly SolidBrush maskBrush = new SolidBrush(Color.White);
public Bitmap Mask { get; set; }
public MaskingLabel() : base() { }
protected override void OnPaint(PaintEventArgs e)
{
if (Mask == null)
base.OnPaint(e);
else
{
Graphics g = Graphics.FromImage(Mask);
g.DrawString(Text, Font, maskBrush, Location);
}
}
}
The problem I am running into is that this approach requires that I handle controls in a very specific order so that the form is drawn correctly. I need to find the most efficient approach to get the tasks listed below done in the order given. I have thought of three possibilities discussed further down. For reference, this is the order in which tasks must be done:
All "MaskingLabel" controls that have the bitmap object set to the mask must be drawn first so that the mask is created before the next step.
The mask is applied to the background picture.
The resulting Bitmap is drawn in a way similar to the way a background would be drawn (except that it is modified first).
The rest of the controls are drawn as normal.
Is there a way for me to insure this happens without separating the controls manually? My first guess is no. As such, I have a few guesses below about how I should go about this. I was hoping someone with more in depth knowledge of GDI+ could offer some insight.
One idea that has occurred to me is to draw the masked controls during the OnPaintBackground method. However, I don't want to waste time by painting the controls twice. This means I would need to filter out which controls are drawn during the main Paint method which effectively leads us to option 2 (FAIK):
I can manually filter out the controls which draw to the mask so that they don't get added to the control. My question here though is would they get drawn at all? Can I manually force them to invoke the OnPaint method?
If doing that wouldn't work, then perhaps I can create a separate derived panel control to serve as a "backdrop" child control that acts as the background picture which can be forced to be drawn first?
EDIT (With Part of the answer):
I realized after posting this that I already have part of the solution built into my project. Still, I think it is a legitimate question to ask, so if anyone can add insight beyond what I have done in my description below, it is welcome.
Specifically, my project has only two controls that are added to the "root" form: a bar that goes to the top (docked at the top when it is shown), and a transparent panel that occupies the rest of the space (with a dock style set to fill). So my solution would be to add the mask controls to the main form and add all the rest to the panel. This only leaves one remaining issue to be resolved: How do I make sure that the panel and the bar are drawn last? (As part of step 4 in the first list?)

Measure size of unicode string to calculate row height [duplicate]

Calling TextRenderer.MeasureText as follows:
TextRenderer.MeasureText(myControl.Text, myControl.Font);
and comparing the result to the size of the control to check if text fits. The results are sometimes incorrect. Have observed the following two issues:
Often when a Label is set to AutoSize, TextRenderer will report a width that is 1 pixel wider than the auto-sized width of the Control.
False negative where TextRenderer reports a width smaller than the control's but the text is still cut off. This occurred with "EstaciĆ³n de trabajo" -- not sure if the accent could somehow affect the width calculation?
Is there any way to improve the accuracy of the MeasureText method? Should I be calling one of the overrides that accepts a device context and/or format flags?
I know it's probably no actual anymore. Yet for future readers here is a simple yet accurate method of measuring text in a control:
Graphics g=Graphics.FromHwnd(YOUR CONTROL HERE.Handle);
SizeF s=g.MeasureString("YOUR STRING HERE", Font, NULL, NULL, STRING LENGTH HERE, 1)
Is there any way to improve the accuracy of the MeasureText method? Should I be calling one of the overrides that accepts a device context and/or format flags?
You have answered your question by yourself. Actually MeasureText based on Win32 DrawTextEx, and this function cannot work without valid device context. So when you call MeasureText override without hdc, it internally create desktop compatible hdc to do measurement.
Of course measurement depends on additional TextFormatFlags. Also keep in mind that Label painting (and measurement) depends on UseCompatibleTextRendering.
So general conclusion you should use MeasureText for your own code, for example when you then call DrawText with exactly same parameters, in all other cases size returned by MeasureText cannot be treated as precise.
If you need to get expected Label size, you should use GetPreferredSize method.
Check out the TextFormatFlags parameter to this function:
TextRenderer::MeasureText(String, Font, Size, TextFormatFlags)
http://msdn.microsoft.com/en-us/library/8wafk2kt.aspx
"The Size, in pixels, of text drawn on a single line with the specified font. You can manipulate how the text is drawn by using one of the DrawText overloads that takes a TextFormatFlags parameter. For example, the default behavior of the TextRenderer is to add padding to the bounding rectangle of the drawn text to accommodate overhanging glyphs. If you need to draw a line of text without these extra spaces you should use the versions of DrawText and MeasureText that take a Size and TextFormatFlags parameter. For an example, see MeasureText(IDeviceContext, String, Font, Size, TextFormatFlags)."
hth
I don't know if I have a perfect solution but I ran into this when I was doing WinForms a few years back. The way I ended up compensating was by adjusting the returned measurement by a percentage. I cannot recall what I used (maybe 5% or 105?), but I do recall that I ended up using a constant percentage across the app and always rounded up.
I haven't got enough points to comment yet, so I've had to put this as an answer:
Perhaps ClearType affects measurement accuracy, because although a character has a known width calculated from its glyph, its rendering and position are adjusted to place axial lines on whole pixels.
Just a thought.

Setting Width of Textbox according to maxlenth

I am using winforms application and i want to set that width of textbox which will show characters till max length,in short something like width = maxlength.Any predefined property is there? or we have to calculate it manually?
//I am looking for this type of logic
private void Form1_Load(object sender, EventArgs e)
{
//sample project
textBox2.Width = textBox2.MaxLength;
textBox3.Width = textBox3.MaxLength;
textBox4.Width = textBox4.MaxLength;
}
You have a Unit Mismatch: Width is in Pixels, MaxLength in characters.
So you need to measure the Font using e.g. Graphics.MeasureString.. Works best for fixed Fonts like Consolas.
You can measure the font e.g. like this, using 'x' as a medium width letter:
using (Graphics G = textBox2.CreateGraphics())
textBox2.Width = (int) (textBox2.MaxLength *
G.MeasureString("x", textBox2.Font).Width);
There are other Font measurement methods like TextRenderer.MeasureText you could use; also both methods have options to fine tune the measurement. The default above will include some leeway.
If you use a fixed Font the width will be OK, if you don't you'll need to decide whether you'd rather be on the safe side (use "W" or "M" to measure) or not. "X" is a likely compromise. Or you could adapt dynamically in the e.g. the TextChanged event..
Use the Anchor property to define how a control is automatically resized as its parent control is resized
Anchoring a control to its parent control ensures that the anchored edges remain in the same position relative to the edges of the parent control when the parent control is resized.
Try this:
textbox1.MaxLength = 0//The default value is 0, which indicates no limit;
Refer this msdn link for more info:
msdn link for textbox maxlength

Set overflow in a powerpoint shape

So, I have a shape I'm programatically generating, when it has a small amount of text, it looks like this:
If I add a huge amount of text however, it flows out of the shape, like so:
What I want to do is to hide the overflow and to force the text to start from the top of the shape (currently the text starts from a position higher than the top of the shape)
I haven't found much information about this so far, here is the code I'm using for the text inside the shape:
var shape = slide.Shapes.AddShape(MsoAutoShapeType.msoShapeRectangle, left, top, width, height);
var textRange2 = shape.TextFrame.TextRange.InsertAfter(description);
textRange2.Font.Size = 10;
shape.TextFrame.TextRange.Paragraphs().ParagraphFormat.Alignment = PpParagraphAlignment.ppAlignLeft;
shape.TextFrame.TextRange.Paragraphs().Font.Name = "Consolas";
shape.TextFrame.TextRange.Paragraphs().Font.Color.RGB = foregroundColor;
One last thing, I know I could just limit the string, but this would impose problems for the user. I want him to be able to resize the shape manually if there is too much text, so that's a no-go. Basically, I just want the equivalent of the css overflow:hidden rule.
One option for some users may be to use the following:
shape.TextFrame.AutoSize = PpAutoSize.ppAutoSizeShapeToFitText;
This will resize the shape to fit the text, there should also be an option to resize TEXT to fit the shape instead (resizing of fonts), I can't seem to find the function however.
Thanks guys
So, apparently
shape.TextFrame.AutoSize
accepts an enumerable PpAutoSize which has PpAutoSize.ppAutoSizeShapeToFitText; that can be used
whereas
shape.TextFrame2.AutoSize
accepts an enumerable MsoAutoSize which has MsoAutoSize.msoAutoSizeTextToFitShape;
So basically, if you change the textframe you're using to TextFrame2 instead of TextFrame, you can have the text resize to fit the shape automagically.
shape.TextFrame2.AutoSize = MsoAutoSize.msoAutoSizeTextToFitShape;
In Powerpoint directly- there is an option to do this. Based on the placement in the menu, I would guess it is in the shape.TextFrame.AutoSize Property - maybe the "mixed" Option?
The PowerPoint object model is a huge mess - so it might be some other strane Property...

C# Printing Inconsistent

I have a form on which I have a number of textboxes. I wish to print the text from these textboxes in the locations they are on the form. It is printing at the moment using the code below. However, the text prints differently on different printers (on some it prints just right, on some too high, etc). It is being printed on a pre-printed form with spaces for the text so it needs to be fairly exact. What am I missing to make it print the same on every printer?
public void printDocument_PrintPage(object sender, PrintPageEventArgs e)
{
Panel curPanel = this.FormPanel;
Graphics g = (Graphics)e.Graphics;
Pen aPen = new Pen(Brushes.Black, 1);
// Cycle through each control. Determine if it's a checkbox or a textbox and draw the information inside
// in the correct position on the form
int xLocation, yLocation;
for (int j = 0; j < curPanel.Controls.Count; j++)
{
// Check if its a TextBox type by comparing to the type of one of the textboxes
if (curPanel.Controls[j] is TextBox)
{
// Unbox the Textbox
TextBox theText = (TextBox)curPanel.Controls[j];
// Draw the textbox string at the position of the textbox on the form, scaled to the print page
xLocation = theText.Bounds.Left;
yLocation = theText.Bounds.Top;
g.DrawString(theText.Text, theText.Font, Brushes.Black, xLocation, yLocation);
}
}
}
The problem is that you ignoring how the text is aligned inside the control. Default alignment is roughly equal to StringFormat.Alignment = StringAlignment.Center, it can be changed for buttons and check boxes with their TextAlign property. You'll need to use the DrawString() overload that takes a Rectangle and a StringFormat. Note that TextBox is tricky, you might still be off by a few pixels.
Take a look at Control.DrawToBitmap() for a completely different approach.
I'm wondering if maybe the problem is discrepencies in how different printers pull in the paper. The text is off by a maximum of half an inch between printers. I was hoping this wasn't the case because if so I will just have to tailor my application to the client's particular printer (not ideal). Has anyone else run into this situation?
This is most likely a combination of two things:
You need to explicitly set up the page margins/boundaries. Various printers will have default margin and page size settings. Use a PageSetupDialog to help you out. If you want consistent printing, you can make the margins constant, but page size should be the responsibility of the user (and then check to make sure your margins actually fit on the page!).
The text needs to be placed on the page in relation to the page boundaries. I know your comment says that it will be, but it doesn't look like that it is actually implemented in your code. Setting the OriginAtMargins (on your PrintDocument control) to true helps immensely with this.

Categories