none
Measure rich text strings RRS feed

  • Question

  • I want to be able to create a richtextbox like control that will resize itself to fit the rendered richtext in the control. The problem is that I don't see any methods for measuring the size of rendered richtext. The Graphics.MeasureString method will only measure plain text strings with a certain font while richtext can contain several fonts and different sizes so a straight MeasureString isn't going to cut it (I think). Is there a way to do this? I've seen several articles on printing richtext that use EM_FORMATRANGE but it appears that all you get back from that is how many characters you can fit in the specified area. I want to know how big of an area I need to fit all my text.
    Monday, May 21, 2007 8:04 PM

Answers

  • I managed to use a combination of the ContentsResized event and the EM_FORMATRANGE method. I have the ContentsResized method set the height of the control (without changing width) and then call another method that uses the EM_FORMATRANGE to see if I can half the width without cutting off any characters. If I can then it tries cutting in half again until it gets somewhere close (between 100 and 200% of the needed width) to the mininum width that will still fit all the characters.

    It works, but it does feel awfully "hacky". It's a shame there isn't a Graphics.DrawRichText method and MeasureRichText method.

    Wednesday, May 23, 2007 7:41 PM
  • Okay, I think I've found a better way. I can use GetPositionFromCharIndex! Can't believe I didn't see this before . Here's the code if it helps anybody else:

     

    Code Snippet

    private int CalculateWidth()

    {

    Point p = GetPositionFromCharIndex(this.TextLength);

    for (int i = 0; i < this.Lines.Length; i++)

    {

    Point lp = GetPositionFromCharIndex(this.Lines[i].Length);

    if (lp.X > p.X)

    {

    p = lp;

    }

    }

    return p.X + 1;

    }

     

    It works for multiline RichText as well by looking at the position at the end of each line and returning the largest X position. The +1 at the end is because if you set the width of a RichTextBox to exactly the maximum width, it'll try and wrap the text.
    Wednesday, February 6, 2008 6:49 PM

All replies

  • You could do a binary search on the EM_FORMRANGE result.
    Monday, May 21, 2007 10:12 PM
    Moderator
  •  nobugz wrote:
    You could do a binary search on the EM_FORMRANGE result.

     

    I could, but surely there's a better solution than that?

    Tuesday, May 22, 2007 12:34 PM
  • Please let us know when you find one.
    Tuesday, May 22, 2007 1:26 PM
    Moderator
  • I managed to use a combination of the ContentsResized event and the EM_FORMATRANGE method. I have the ContentsResized method set the height of the control (without changing width) and then call another method that uses the EM_FORMATRANGE to see if I can half the width without cutting off any characters. If I can then it tries cutting in half again until it gets somewhere close (between 100 and 200% of the needed width) to the mininum width that will still fit all the characters.

    It works, but it does feel awfully "hacky". It's a shame there isn't a Graphics.DrawRichText method and MeasureRichText method.

    Wednesday, May 23, 2007 7:41 PM
  • Hi,


    There's alot of posts that says you can use SendMessage with EM_FORMATRANGE and pass a zero on the high param to measure the text only, and the resulting rectangle will be returned in low param, inside the formatrange structure, in the property rc (the type is RECT). But after so many tries, the formatrange structure is not changing at all.

    So I devise another plan, and this one works. I put the cursor at the end of a line in the richtextbox (using selectionstart), and call the GetCaretPos API, the result is a POINT that specify the x and y location of the caret inside the client area of the richtextbox, so we can use the x value as the width of the text.


    Wednesday, October 10, 2007 5:15 AM
  •  chr0nus wrote:
    Hi,
    So I devise another plan, and this one works. I put the cursor at the end of a line in the richtextbox (using selectionstart), and call the GetCaretPos API, the result is a POINT that specify the x and y location of the caret inside the client area of the richtextbox, so we can use the x value as the width of the text.

     

    I tried this method but for some reason whenever I do GetCaretPos the returned point is always 1,1. Anybody have any idea what the problem might be?

    Wednesday, February 6, 2008 4:07 PM
  • Okay, I think I've found a better way. I can use GetPositionFromCharIndex! Can't believe I didn't see this before . Here's the code if it helps anybody else:

     

    Code Snippet

    private int CalculateWidth()

    {

    Point p = GetPositionFromCharIndex(this.TextLength);

    for (int i = 0; i < this.Lines.Length; i++)

    {

    Point lp = GetPositionFromCharIndex(this.Lines[i].Length);

    if (lp.X > p.X)

    {

    p = lp;

    }

    }

    return p.X + 1;

    }

     

    It works for multiline RichText as well by looking at the position at the end of each line and returning the largest X position. The +1 at the end is because if you set the width of a RichTextBox to exactly the maximum width, it'll try and wrap the text.
    Wednesday, February 6, 2008 6:49 PM