开发者

How to Implement AutoSize

开发者 https://www.devze.com 2022-12-30 20:20 出处:网络
I\'m trying to figure out a good way to auto-size a Rectangle that has text drawn inside of it. I basically want the size to have a ratio of width/height and then \"grow\" according to that ratio to f

I'm trying to figure out a good way to auto-size a Rectangle that has text drawn inside of it. I basically want the size to have a ratio of width/height and then "grow" according to that ratio to fit the text. I've looked at Graphics.MeasureString but I don't think it does what I'm looking for (maybe it does and I'm just using it wrong).

I don't want to specify a specific width of the rectangle to be drawn. Instead I want to say find the smallest width/height to fit this text given a minimum width but the found rectangle must have some specific ratio of width and height.

This doesn't have to be specific to C#, any idea for 开发者_C百科solving this problem I'm sure can be mapped to C#.

Thanks!


I believe you can use Graphics.MeasureString. This is what I have used in my GUI code to draw rectangles around text. You hand it the text and the font you want to use, it returns to you a rectangle (technically a SizeF object - width and height). Then you can adjust this rectangle by the ratio you want:

Graphics g = CreateGraphics();
String s = "Hello, World!";
SizeF sizeF = g.MeasureString(s, new Font("Arial", 8));

// Now I have a rectangle to adjust.
float myRatio = 2F;
SizeF adjustedSizeF = new SizeF(sizeF.Width * myRatio, sizeF.Height * myRatio);
RectangleF rectangle = new RectangleF(new PointF(0, 0), adjustedSizeF);

Am I understanding your question correctly?


You should use TextRenderer.MeasureText, all controls use TextRenderer to draw text in .NET 2.0 and up.

There is no unambiguous solution to your question, there are many possible ways to fit text in a Rectangle. A wide one that displays just one line is just as valid as a narrow one that displays many lines. You'll have to constrain one of the dimensions. It is a realistic requirement, this rectangle is shown inside some other control and that control has a certain ClientSize. You'll need to decide how you want to lay it out.


On the back of my comment about the System.Windows.Forms.Label, maybe you could have a look at the code driving the painting of a Label? If you use Reflector this should get you part of the way.

There seems to be some methods on there like GetPreferredSizeCore() for example that probably have what you want which I'm sure could be made generic enough given a little work.


I've found my own solution. The following code determines the best rectangle (matching the ratio) to fit the text. It uses divide and conquer to find the closest rectangle (by decrementing the width by some "step"). This algorithm uses a min-width that is always met and I'm sure this could be modified to include a max width. Thoughts?

private Size GetPreferredSize(String text, Font font, StringFormat format)
{

    Graphics graphics = this.CreateGraphics();

    if (format == null)
    {
        format = new StringFormat();
    }

    SizeF textSize = SizeF.Empty;

    // The minimum width allowed for the rectangle.
    double minWidth = 100;

    // The ratio for the height compared to the width.
    double heightRatio = 0.61803399; // Gloden ratio to make it look pretty :)

    // The amount to in/decrement for width.
    double step = 100;

    // The new width to be set.
    double newWidth = minWidth;

    // Find the largest width that the text fits into.
    while (true)
    {
        textSize = graphics.MeasureString(text, font, (int)Math.Round(newWidth), format);

        if (textSize.Height <= newWidth * heightRatio)
        {
            break;
        }

        newWidth += step;
    }

    step /= 2;

    // Continuously divide the step to adjust the rectangle.
    while (true)
    {

        // Ensure step.
        if (step < 1)
        {
            break;
        }

        // Ensure minimum width.
        if (newWidth - step < minWidth)
        {
            break;
        }

        // Try to subract the step from the width.
        while (true)
        {

            // Measure the text.
            textSize = graphics.MeasureString(text, font, (int)Math.Round(newWidth - step), format);

            // If the text height is going to be less than the new height, decrease the new width.
            // Otherwise, break to the next lowest step.
            if (textSize.Height < (newWidth - step) * heightRatio)
            {
                newWidth -= step;
            }
            else
            {
                break;
            }

        }

        step /= 2;
    }

    double width = newWidth;
    double height = width * heightRatio;

    return new Size((int)Math.Ceiling(width), (int)Math.Ceiling(height));
}
0

精彩评论

暂无评论...
验证码 换一张
取 消