views:

105

answers:

2

I have panel that I have customized. I use it to display text. But sometimes that text is too long and wraps to the next line. Is there some way I can auto resize the panel to show all the text?

I am using C# and Visual Studio 2008 and the compact framework.


Here is the code I am wanting to adjust the size for:
(Note: HintBox is my own class that inherits from panel. So I can modify it as needed.)

public void DataItemClicked(ShipmentData shipmentData)
{
    // Setup the HintBox
    if (_dataItemHintBox == null)
        _dataItemHintBox = HintBox.GetHintBox(ShipmentForm.AsAnObjectThatCanOwn(),
                                             _dataShipSelectedPoint,
                                             new Size(135, 50), shipmentData.LongDesc,
                                             Color.LightSteelBlue);


    _dataItemHintBox.Location = new Point(_dataShipSelectedPoint.X - 100,
                                          _dataShipSelectedPoint.Y - 50);
    _dataItemHintBox.MessageText = shipmentData.LongDesc;
    // It would be nice to set the size right here
    _dataItemHintBox.Size = _dataItemHintBox.MethodToResizeTheHeightToShowTheWholeString()
    _dataItemHintBox.Show();

}

I am going to give the answer to Will Marcouiller because his code example was the closest to what I needed (and looks like it will work). However, this is what I think I will use:

public static class CFMeasureString
{
    private struct Rect
    {
        public readonly int Left, Top, Right, Bottom;
        public Rect(Rectangle r)
        {
            this.Left = r.Left;
            this.Top = r.Top;
            this.Bottom = r.Bottom;
            this.Right = r.Right;
        }
    }

    [DllImport("coredll.dll")]
    static extern int DrawText(IntPtr hdc, string lpStr, int nCount, 
                               ref Rect lpRect, int wFormat);
    private const int DT_CALCRECT = 0x00000400;
    private const int DT_WORDBREAK = 0x00000010;
    private const int DT_EDITCONTROL = 0x00002000;

    static public Size MeasureString(this Graphics gr, string text, Rectangle rect, 
                                     bool textboxControl)
    {
        Rect bounds = new Rect(rect);
        IntPtr hdc = gr.GetHdc();
        int flags = DT_CALCRECT | DT_WORDBREAK;
        if (textboxControl) flags |= DT_EDITCONTROL;
        DrawText(hdc, text, text.Length, ref bounds, flags);
        gr.ReleaseHdc(hdc);
        return new Size(bounds.Right - bounds.Left, bounds.Bottom - bounds.Top + 
                        (textboxControl ? 6 : 0));
    }
}

This uses the os level call to draw text. By P-Invoking it I can get the functionality I need (multi line wrapping). Note that this method does not include any margins. Just the actual space taken up by the text.

I did not write this code. I got it from http://www.mobilepractices.com/2007/12/multi-line-graphicsmeasurestring.html. That blog post had my exact problem and this fix. (Though I did make a minor tweak to make it a extension method.)

+4  A: 

You could use the Graphics.MeasureString() method.

With a code sample of your text assignment onto your panel, I could perhaps provide a code sample using the MeasureString() method, if you need it.

I have no way to know whether the Graphics.MeasureString() method is part of the Compact Framework, as it is not said on the page I linked.

EDIT #1

Here's a link where I answered to another text-size related question, while I look for writing a sample for you. =)

EDIT #2

Here's another link related to your question. (Next edit is the sample code. =P)

EDIT #3

public void DataItemClicked(ShipmentData shipmentData) { 
    // Setup the HintBox 
    if (_dataItemHintBox == null) 
        _dataItemHintBox = HintBox.GetHintBox(ShipmentForm.AsAnObjectThatCanOwn(), 
                                             _dataShipSelectedPoint, 
                                             new Size(135, 50), shipmentData.LongDesc, 
                                             Color.LightSteelBlue); 

    // Beginning to measure the size of the string shipmentData.LongDesc here.

    // Assuming that the initial font size should be 30pt.
    Single fontSize = 30.0F;
    Font f = new Font("fontFamily", fontSize, FontStyle.Regular);

    // The Panel.CreateGraphics method provides the instance of Graphics object 
    // that shall be used to compare the string size against.
    using (Graphics g = _dataItemHintBox.CreateGraphics()) {
        while (g.MeasureString(shipmentData.LongDesc, f).Width > _dataItemHintBox.Size.Width - 5) {
            --fontSize;
            f = new Font("fontFamily", fontSize, FontStyle.Regular);
        }
    }

    // Font property inherited from Panel control.
    _dataItemHintBox.Font = f;

    // End of font resizing to fit the HintBox panel.

    _dataItemHintBox.Location = new Point(_dataShipSelectedPoint.X - 100, 
                                          _dataShipSelectedPoint.Y - 50); 
    _dataItemHintBox.MessageText = shipmentData.LongDesc; 
    // It would be nice to set the size right here 
    _dataItemHintBox.Size = _dataItemHintBox.MethodToResizeTheHeightToShowTheWholeString() 
    _dataItemHintBox.Show(); 
} 

Disclaimer: This code has not been tested and is off the top of my head. Some changes might be obligatory in order for you to test it. This provides a guideline to achieve what you seem to want to accomplish. There might be a better way to do this, but I know this one works. Well, the algorithm works, as you can see in my other answers.

Instead of the line:

SizeF fontSize = 30.0F;

You could as well do the following:

var fontSize = _dataItemHintBox.Font.Size;

Why is this?

Because Font.Size property is readonly. So, you need to create a new instance of the System.Drawing.Font class each time the Font.Size shall change.

In your comparison, instead of having the line:

while (g.MeasureString(shipmentData.LongDesc, f)...)

you could also have:

while (g.MeasureString(shipmentData.LongDesc, _dataItemHintBox.Font)...)

This would nullify the need for a second Font class instance, that is f.

Please feel free to post feedbacks as I could change my sample to fit your reality upon the feedbacks received, so that it better helps you. =)

I hope this helps! =)

Will Marcouiller
Graphics.MeasureString() is part of the compact framework. My assignment is simple. I have my panel and I set the text for it. I will add the code to my question. Since I don't want to resize in the Paint method, I am guessing I will have to make a Graphics instance somehow.
Vaccano
Use control.CreateGraphics() for that, inside a 'using' statement.
bbudge
Thanks for your continued attention on this, but I don't know that it is possible (at least with MeasureString) because you can't supply a width to the method in the compact framework. I would have to make my own word wrapping algorithm. That sounds complicated and bug prone.
Vaccano
You don't have to provide a width. The `Size (height and width)` is part of the `Font` your provide to the `MeasureString()` method. Then, in return, you got a `Size` class instance, then you may either the resulting `Height` or `Width`.
Will Marcouiller
+2  A: 

You can use whichever of the TextRenderer.MeasureText overloads is appropriate for you. Using this function, you can determine the actual rendered size of a string and adjust your panel's size accordingly.

If you're trying to measure inside the Paint event, then you could use the MeasureString function on your e.Graphics object, but resizing inside Paint is not wise. Using TextRenderer avoids your having to create a Graphics object with CreateGraphics() and disposing of it when you're finished.

EDIT

Since TextRenderer is not supported on the compact framework (I missed the tag the first time I saw the question), you'll have to use MeasureString() function on the Graphics object. Something like this:

public Size GetStringSize(string text)
{
    using(Graphics g = yourPanel.CreateGraphics())
    {
        return g.MeasureString(text, yourPanel.Font);
    }
}
Adam Robinson
Alas, TextRenderer is not part of the compact framework. But that is good to know if I end up having this issue on a normal .net problem.
Vaccano
Thanks for the example code. However, the second MeasureString that you posted is not part of the compact framework either. This link shows that only the first one you used is supported (http://msdn.microsoft.com/en-us/library/system.drawing.graphics.measurestring%28VS.90%29.aspx) (Indicated by the mobile device icon)
Vaccano
@Vaccano: Got it, updated.
Adam Robinson