2

I'm trying to set the width of a label ("label1") according to its content width. But each time I get the length of the string with the String.Length-Method, the width seems to be too small and my string does not fit into the label.

  string anystring = "ThisIsMyString";
  int i = anystring.Length;
  label1.Text = anystring;
  label1.Width = i + 10; //I add +10 to have some buffer

Is the problem that String.Length and Label.Width have different measure units? Are there any other ways to handle this? (I know that I could simply leave the part out where I set the label's width, but I'd like to know how to do it that way out of curiosity)

MaChaToc
  • 139
  • 1
  • 12
  • The length of a string is the number of characters where the width of a control is its size in pixel (per default). You may multiply number of character by a constant number of pixels however. – MakePeaceGreatAgain Dec 14 '16 at 12:47
  • 2
    `string.Length` is simply the number of characters... it isn't about width - to know the width in terms of some UI element, you need a lot of context - including the font, font size, etc. You probably want `Graphics.MeasureString`? – Marc Gravell Dec 14 '16 at 12:47
  • Set `Label.AutoSize` property to `true`. – Alexander Petrov Dec 14 '16 at 12:57
  • I get your point. I'll try both Graphics.MeasureString and Label.AutoSize Thanks – MaChaToc Dec 14 '16 at 13:00
  • @AlexanderPetrov with the AutoSize property it works fine. But I'd like to figure it out by setting the width of the label myself during the code. – MaChaToc Dec 14 '16 at 13:15

3 Answers3

4

The correct way to determine the width or the size of text, when displayed on-screen using Windows is to use Graphics.MeasureString:

Measures the specified string when drawn with the specified Font.

The reason for this is that the following things will impact the exact size you get back:

  • Obviously the text
  • The font family/type used
  • The font size used
  • Font styling (bold, italic, etc.)

Here is a very short example:

using (var f = new Font("Arial", 8.25f))
using (var g = Graphics.FromHwnd(IntPtr.Zero)) // Desktop
{
    SizeF size = g.MeasureString("Test", f);
}

Obviously you should use a Graphics object obtained from a control, and the font of the label (if you're going to use it for a label) to avoid constructing and using the objects above.

Lasse V. Karlsen
  • 380,855
  • 102
  • 628
  • 825
  • Ha! Well look at me reinventing the wheel. Never would've even *guessed* .NET has something native to do this. – sab669 Dec 14 '16 at 13:45
  • Right now I am trying to do it that way. But I'm not really sure how to do it. The example from the Microsoft side generates an event. I don't really know how to call on this event, where I need it. So this is my event: `private void MeasureStringMin(string str, Font stringFont , Label lbl, PaintEventArgs e)` – MaChaToc Dec 14 '16 at 13:45
  • @MaChaToc You need to attach an event handler for the `Paint` event on the `Label`. So if you go to the Design View for your application, click on a `Label` control then go to the Events tab on the Property sheet there will be a `Paint` entry. Double-click there and it'll automatically wire up the `Paint` event. Then you can basically use the code as seen in the example on @LasseV.Karlsen's link. – sab669 Dec 14 '16 at 13:53
  • @sab669 that was what I was missing – MaChaToc Dec 14 '16 at 14:04
  • @LasseV.Karlsen It worked thanks! In addition to your code I just had to set the autosize property to false and convert `size.Width` to a Int32. For anyone who might be interested my code inside `using (var g ...)` looked in the end like this: `label1.AutoSize = false; SizeF size = g.MeasureString(button1.Text, f); int i = Convert.ToInt32(size.Width); label1.Width = i; label1.Text = Convert.ToString(i);` – MaChaToc Dec 14 '16 at 14:09
2

Well, the short answer to your question:

Is the problem that String.Length and Label.Width have different measure units?

Is "yes".

See here for more information on the Width property, and here for more information on String.Length.

Basically: String.Length refers to the number of characters in the string. "ABC123" would return 6. Control.Width is a measurement in pixels. And since different fonts appear differently, it would be extremely cumbersome to try and figure out "N characters in X font is Y pixels". (EDIT: And that's assuming you're using a mono-width font. If not, it's even more complex as "iii" is visibly longer than "aaa".)

In regards to your follow-up:

Are there any other ways to handle this?

(EDIT: Changing this part after coming up with an idea in comments)

1) Write the string to an image file

2) Find a suitable library that can determine color from a coordinate or set of coordinates in an image

3) Loop N times over the rows of pixels your image where N = the height of the image

4) Look for lines containing black pixels. We don't want lines that are ALL white as that would just be the "buffer" above and below our text.

5) Iterate over each pixel in the current row and store the X coordinate of the first black pixel, then store the X coordinate of every black pixel after the first in a "temporary" variable

6) Store the difference between lastBlackPixel and firstBlackPixel in a variable

7) Compare the difference between those two points for every row in your image. If the current difference is greater than largestKnownDifference then update that variable with the new distance.

That should determine the width of the text. Or maybe iterate each column and store the position of the first black pixel and then the the position of the last column to contain a black pixel? I don't know, I haven't had my coffee yet. Lots of ways to skin a cat.

sab669
  • 3,984
  • 8
  • 38
  • 75
  • 1
    You can't even say "N characters in font X", since depending _which_ characters, the width may vary too. EG in most fonts, W is a lot wider than I – James Thorpe Dec 14 '16 at 12:51
  • True, true. It varies from font, size, user's individual display settings -- all kinds of things. – sab669 Dec 14 '16 at 12:53
  • @sab669 I am assigning it directly in the code, don't intend to change it during the program running. This was just some easy practice I tried to understand those methods better. – MaChaToc Dec 14 '16 at 12:58
  • @MaChaToc Got'chya. I'm sure there's a library out there that could write text to an image file, and then as I said use another library to determine the left-most and right-most pixels of the text that aren't the background color (presumably white) and then use those points to determine the width. That would probably be the most-reliable way to determine this sort of thing, but would still be fairly difficult. Could be a good / fun learning exercise, though! – sab669 Dec 14 '16 at 13:01
  • @sab669 What you say is interesting. But what do you mean with left-most and right-most pixels? – MaChaToc Dec 14 '16 at 13:12
  • @MaChaToc Well, an image is just a rectangle, right? A box of pixels, say 250x250 image. If the top-left corner is referring to `0,0` and the bottom right is `250,250`, then the dead center of the image would be at `125,125` -- 125 pixels on the X axis, 125 pixels on the Y axis. Since we only care about width, we only need the X axis (horizontal -- width). So the "left-most" would be the black pixel closest to `0` and the "right-most" would be the black pixel closest to `250` regardless of their `Y` coordinate, as we don't care about the *height* of the text. – sab669 Dec 14 '16 at 13:17
  • Glad to help! Fun little brain teaser to get the day started. But as I said, there are ultimately a lot of ways one could determine the width of a string -- this is just the first one that came to me on this sleep-deprived morning. You could take my solution(s) one step further and check the dimmensions of the image and determine whether it would be faster to iterate horizontally or vertically. Or maybe the image library you use has some really specific `GetFirstColorPosition` and `GetLastColorPosition` methods that take a `Color` as an argument. Who knows. – sab669 Dec 14 '16 at 13:26
2

You could take a look into Label's autosize property. Setting this to true will make the label to adjust its width according to string size. However, this might not get the desired result for very long texts.

If you want to be able to put long texts and not obtain very long labels, you can wrap the text by also setting its MaximumSize.

[EDIT] I think measuring can be done by getting the width after setting it autosized (a posteriori).

An alternative is to try to measure the text by using Graphics.MeasureString that uses the actual text and used font to provide the size. A fully working example can be found here (apriori).

Community
  • 1
  • 1
Alexei - check Codidact
  • 22,016
  • 16
  • 145
  • 164
  • Given OP states `(I know that I could simply leave the part out where I set the label's width, but I'd like to know how to do it that way out of curiosity)`, I think (s)he knows *how* to put text in a label and make it fit. They just want to know how one could programatically determine the width of a given string. **EDIT** did not realize `AutoSize` defaults to `false` if the label is created programatically. So this is actually very good to know! I thought it always defaulted to `true`. – sab669 Dec 14 '16 at 13:19