Let's say I have 200 pixels of space and I want to draw two strings into it:
- the first string should be left justified
- right second string should be right justified
- But not overlap if they do not both fit (then do whatever my String.Trimming options say)
Am I going to have to measure each and draw this manually, or does DrawString have some way to support what I'm trying to do without me reinventing the wheel?
Imagine that \l and \r were escapes that did this, then I could say
graphics.Drawstring("\lfirst\rsecond", ...);
and I'd wind up with something like
"first second"
At least that's what I'd like to have happen (I know \l and \r do not exist). Is there a way?
I've ignored your flags and instead I'm showing you (roughly) how you can align text. It's easy enough to pick out your text, split it up and draw it as two separate strings!
string text2 = "Use TextFormatFlags and Rectangle objects to"
+ " align text in a rectangle.";
using (Font font2 = new Font("Arial", 12, FontStyle.Bold, GraphicsUnit.Point))
{
Rectangle rect2 = new Rectangle(150, 10, 130, 140);
// Create a TextFormatFlags with word wrapping, horizontal center and
// vertical center specified.
TextFormatFlags flags = TextFormatFlags.HorizontalLeft |
TextFormatFlags.VerticalCenter | TextFormatFlags.WordBreak;
// Draw the text and the surrounding rectangle.
TextRenderer.DrawText(e.Graphics, text2, font2, rect2, Color.Blue, flags);
e.Graphics.DrawRectangle(Pens.Black, rect2);
}
In the long run here's what I wound up doing:
Call MeasureString on left string
Call MeasureString on right string
Draw left string, left-justified
If the sum of the width of the two strings is less than width of the available space, draw the right string right-justified.
Pretty straightforward, though I was hoping there was something in the framework that would have done the work for me.
Related
I have been unable to put together a bit of string rendering code that gives me everything I am aiming for. To this point, only trade-offs.
I am shooting for something that behaves the way Windows7 desktop shortcuts do, in that there is an image above text and that text can wrap once, with anything more truncated with an ellipse.
Such as:
I am using TextRenderer.DrawText and I have tried various combinations of TextFormatFlags, but I can only get either text that wraps indefinitely, or text that stops at a single line and has the ellipsis.
Some examples. The bounds provided to TextRenderer are, in all cases, within the bounds of the ClipRectangle of the surface in which I am drawing. The text it is attempting to render is "This is the rather long name of a group called #1.".
This code:
TextFormatFlags flags = TextFormatFlags.HorizontalCenter |
TextFormatFlags.NoPadding | TextFormatFlags.WordBreak |
TextFormatFlags.EndEllipsis;
Rectangle rect = new Rectangle(x, y, this.Width - 6, this.Height - y);
TextRenderer.DrawText(e.Graphics, _text, Font, rect, _textColor, flags);
...produces this:
While simply removing the TextFormatFlags.WordBreak flag produces:
How can I do this such as I get wrapping as far as is possible within the bounds of the Rectangle I pass, followed by an ending ellipse when truncation occurs?
You have to use a, cough, unintuitively named option to get both word breaks and the end-ellipsis. TextFormatFlags.TextBoxControl gives you what you are looking for.
This has some additional formatting behavior, none that you are likely to be upset about if you like the way the icon title looks, it uses the same winapi function. Key thing it does is not display a clipped line, like a multi-line TextBox doesn't, also giving you the ellipsis back.
I have strings formatted using the code below
String.Format("{0,-10} {1,10}", "Kills:", kills);
String.Format("{0,-10} {1,10}", "Points:", points);
String.Format("{0,-10} {1,10}", "$:", currency);
From what I understood, the first part of the strings should be left justified with a 10 space buffer and then the integer variables should be printed right justified with a 10 space buffer.
However when attempt to draw the strings using SpriteBatch.DrawString, nothing aligns properly.
The left aligned side prints properly, but the right aligned side centres on a certain point, for example if kills = 50 and points = 5002, the 50 will be centered over the 00 in the 5002...
What is going on?
Quite simply, I suspect you're not using a monospaced font. When different characters have different widths, you can't use spaces to line things up. (Your sample works when using Console.WriteLine for example, as the console has a fixed width font by default.)
You'll either have to use a monospaced font, or you'll have to draw the strings separately - draw each string to fit the relevant area. I don't know anything about XNA, but I'd expect you to either have to measure the width of the string before you draw it (so you can subtract it from the right-hand edge, for example) or specify some sort of layout value which indicates "right-align this string with a particular point".
Most likely you draw the text with a proportional font. Bear in mind that characters don't have the same width, so you cannot align texts with spaces.
As I do not have reply privileges, (or some such thing, as there is no reply button for answers), but I would like to contribute, I will post this answer.
Jon's answer mentioned measuring the string, this is possible by spriteFont.MeasureString(string s);. This returns a Vector2, the X portion of which is the width of the rendered text. (Y is height, which could be helpful for other things) This allows you to use a font other than a monospace font.
Here is an example of using MeasureString:
I'm not really sure what the question is asking, but if you wanted a single line of text similar to "Kills:50 Points:5002" but width two different spritebatch calls you could do the following (note I typed this directly into stackoverflow, so there may be minor syntax errors):
float killStringWidth = spriteFont.MeasureString(killString).X;
spriteBatch.DrawString(spriteFont, killString, new Vector2(0,0), Color.White );
spriteBatch.DrawString(spriteFont, pointString, new Vector2(killStringWidth + 10, 0), Color.White );
I want to draw a string as an axis label. When I draw the string with following code, I can read it "from the left". The base line of the text is at the left side.
StringFormat format = CustomGraphics.StringFormat(ContentAlignment.MiddleCenter);
format.FormatFlags |= StringFormatFlags.DirectionVertical;
e.Graphics.DrawString(this.yAxis.Title.Text, this.yAxis.Title.Font,
textBrush, e.Bounds, format);
format.FormatFlags &= ~StringFormatFlags.DirectionVertical;
I want to draw vertical but turn the orientation by 180 degrees. How can I control this? Is there another method that I should use?
Use Graphics.RotateTransform() to get the string rotated the way you want it. You'll need TranslateTransform() and MeasureText() to get the start-point right.
How do I rotate a label in C#? contains a long and powerful paint method, based originally on http://www.codeproject.com/KB/miscctrl/customtext.aspx
Is there any technique to calculate the height of text in AcroField?
In other words, I have a template PDF with a body section that I want to paste my long text into and I want to get the height of the text. If it overflows, insert a new page for rest of the text.
This will give height and width:
Vector curBaseline = renderInfo.GetBaseline().GetStartPoint();
Vector topRight = renderInfo.GetAscentLine().GetEndPoint();
iTextSharp.text.Rectangle rect = new iTextSharp.text.Rectangle(
curBaseline[Vector.I1], curBaseline[Vector.I2],
topRight[Vector.I1], topRight[Vector.I2]
);
Single curFontSize = rect.Height;
Just set your field to a font size of zero. This will automatically size the font so that the given text will fit into your field... within certain limits. I don't think it'll shrink below 6 points.
Another alternative would be to use a ColumnText and call myColText.go(true). This will "simulate" layout, letting you know what goes where without actually drawing anything to the PDF. Just whip up a columnText with the same dimensions, font&font-size as your field, and your results should be the same.
In fact, I believe iText's text field rendering code uses ColumnText internally. Yep, have a look at the source for TextField.getAppearance().
Note that the bounding box of your field isn't going to match the box the text is laid out into... you have to account for borer style & thickness. That's why I suggest you look at the source.
The LinearGradientBrush in .net (or even in GDI+ as a whole?) seems to have a severe bug: Sometimes, it introduces artifacts. (See here or here - essentially, the first line of a linear gradient is drawn in the endcolor, i.e. a gradient from White to Black will start with a Black line and then with the proper White to Black gradient)
I wonder if anyone found a working workaround for this? This is a really annoying bug :-(
Here is a picture of the Artifacts, note that there are 2 LinearGradientBrushes:
alt text http://img142.imageshack.us/img142/7711/gradientartifactmm6.jpg
I have noticed this as well when using gradient brushes. The only effective workaround I have is to always create the gradient brush rectangle 1 pixel bigger on all edges than the area that is going to be painted with it. That protects you against the issue on all four edges. The downside is that the colors used at the edges are a fraction off those you specify, but this is better than the drawing artifact problem!
You can use the nice Inflate(int i) method on a rectangle to get the bigger version.
I would finesse Phil's answer above (this is really a comment but I don't have that privilege). The behaviour I see is contrary to the documentation, which says:
The starting line is perpendicular to the orientation line and passes through one of the corners of the rectangle. All points on the starting line are the starting color. Then ending line is perpendicular to the orientation line and passes through one of the corners of the rectangle. All points on the ending line are the ending color.
Namely you get a single pixel wrap-around in some cases. As far as I can tell (by experimentation) I only get the problem when the width or height of the rectangle is odd. So to work around the bug I find it is adequate to increase the LinearGradientBrush rectangle by 1 pixel if and only if the dimension (before expansion) is an odd number. In other words, always round the brush rectangle up the the next even number of pixels in both width and height.
So to fill a rectangle r I use something like:
Rectangle gradientRect = r;
if (r.Width % 2 == 1)
{
gradientRect.Width += 1;
}
if (r.Height % 2 == 1)
{
gradientRect.Height += 1;
}
var lgb = new LinearGradientBrush(gradientRect, startCol, endCol, angle);
graphics.FillRectangle(lgb, r);
Insane but true.
At least with WPF you could try to use GradientStops to get 100% correct colors right at the edges, even when overpainting.
I experienced artifacts too in my C++ code. What solved the problem is setting a non-default SmoothingMode for the Graphics object. Please note that all non-default smoothing modes use coordinate system, which is bound to the center of a pixel. Thus, you have to correctly convert your rectangle from GDI to GDI+ coordinates:
Gdiplus::RectF brushRect;
graphics.SetSmoothingMode( Gdiplus::SmoothingModeHighQuality );
brushRect.X = rect.left - (Gdiplus::REAL)0.5;
brushRect.Y = rect.top - (Gdiplus::REAL)0.5;
brushRect.Width = (Gdiplus::REAL)( rect.right - rect.left );
brushRect.Height = (Gdiplus::REAL)( rect.bottom - rect.top );
It seems like LinearGradientBrush works correctly only in high-quality modes.