Measure String inside RichTextBox Control
Asked Answered
H

6

12

Can somebody please explain how I would go about measuring the string inside a richtextbox control so that the I can automatically resize the richtextbox control according to its content?

Thank you

Edit:

I've thought about it, and since the below answer won't work if there are different fonts in the RichTextBox Control, what if, I could get the upper-left coords of the richtextbox control and then get the bottom-right coords of the very last line of text inside the rtb. That would essentially give me the Width and Height of the string inside the RichTextBox Control. Is this possible? Or is this a bad idea to do it this way?

Harkins answered 2/3, 2010 at 16:55 Comment(0)
H
7

Put the following code in the ContentsResized event:

Private Sub rtb_ContentsResized(ByVal sender As Object, ByVal e As System.Windows.Forms.ContentsResizedEventArgs) Handles txtQuestion.ContentsResized
        Dim h = e.NewRectangle.Height, w = e.NewRectangle.Width
        h = Math.Max(h, sender.Font.Height)
        h = Math.Min(h, Me.ClientSize.Height - 10 - sender.Top)
        h += sender.Height - sender.ClientSize.Height + 1
        sender.Height = h
End Sub
Horseweed answered 29/4, 2011 at 5:49 Comment(2)
ContentsResized and e.NewRectangle did the trick for me. The other usual suspects e.g. AutoSize, GetPreferredSize don't seem to work properly for RichTextBox.Voiceless
This doesn't work. The height ends up too short, and even if I modify this to set sender.Width = w, the width doesn't change at all.Stoned
S
5

Try calling GetPreferredSize(Size.Empty). It is defined in the Control class, and if overriden property by the RichTextBoxControl, ought to give you what you are looking for.

If you pass something other than Size.Empty into the method, then it will use that value as a maximum constraint. Using Size.Empty means that the potential size is unbounded.

Stoned answered 2/3, 2010 at 18:35 Comment(2)
you can even call it with the width GetPreferredSize(new Size(width, 0)) and you can get the height from the return Size of that callStaurolite
This does not work. The height and width returned by GetPerferredSize are both too short.Stoned
C
3

Assuming that someone is typing into the control, you could use an event to fire every time a character is entered (increment counter) and decrement when it is deleted. This would give you a true count.

Edit:

Have you tried this to adjust the height?

richTextBox1.Height = (int)(1.5 * richTextBox1.Font.Height) + richTextBox1.GetLineFromCharIndex(richTextBox1.Text.Length + 1) * richTextBox1.Font.Height + 1 + richTextBox1.Margin.Vertical;

richTextBox1.SelectionStart = 0;

richTextBox1.SelectionStart = richTextBox1.Text.Length;

Or you can do this using Width:

Graphics g = Graphics.FromHwnd(richTextBox1.Handle);

SizeF f = g.MeasureString(richTextBox1.Text, richTextBox1.Font);
richTextBox1.Width = (int)(f.Width)+5;
Caridadcarie answered 2/3, 2010 at 17:19 Comment(4)
This is currently what I am doing, although you can't account for font styles/sizes this way, and eventually the rtb won't be able to keep up and the text would overflowHarkins
No offense, but this produces very ugly results. There has to be a way to do this without looking like the RichTextBox is about to explode through my monitor.Harkins
Hmm, it seemed to work fine here but I am not applying any font stylesCaridadcarie
This might work for simple TextBoxes, but it does not work for RichTextBoxes that can have bold words, multiple fonts used, and multiple font sizes.Stoned
T
0

You can measure a string by calling TextRenderer.MeasureText.

However, if the text contains multiple fonts, this will not work.

EDIT: You're looking for the GetPositionFromCharIndex method.
Note that if there are multiple lines, you should take the max of the X coordinates of the last character on each line.

Tuppeny answered 2/3, 2010 at 16:57 Comment(2)
Thanks. I don't quite understand that page. Why does it draw text? Can it just send me back a width and height value, so I can reset the Size of the RichTextBox to the new size?Harkins
TextRenderer.MeasureText doesn't draw anything; it simply returns the size of the text in pixels.Tuppeny
P
0

I found a solution for the Rich text box height issues.. i have modified it a for general use..

Create following structs in your application....

[StructLayout(LayoutKind.Sequential)]
    public struct RECT {
        public Int32 left;
        public Int32 top;
        public Int32 right;
        public Int32 bottom;
    }

    [StructLayout(LayoutKind.Sequential)]
    public struct SCROLLBARINFO {
        public Int32 cbSize;
        public RECT rcScrollBar;
        public Int32 dxyLineButton;
        public Int32 xyThumbTop;
        public Int32 xyThumbBottom;
        public Int32 reserved;
        [MarshalAs(UnmanagedType.ByValArray, SizeConst = 6)]
        public Int32[] rgstate;
    }

Create following private variables in your class for form (where ever you need to calculate rich text height)

private UInt32 SB_VERT = 1;
        private UInt32 OBJID_VSCROLL = 0xFFFFFFFB;

        [DllImport("user32.dll")]
        private static extern
            Int32 GetScrollRange(IntPtr hWnd, UInt32 nBar, out Int32 lpMinPos, out Int32 lpMaxPos);

        [DllImport("user32.dll")]
        private static extern
            Int32 GetScrollBarInfo(IntPtr hWnd, UInt32 idObject, ref SCROLLBARINFO psbi);

Add following method to your Class for form

private int CalculateRichTextHeight(string richText) {
            int height = 0;
            RichTextBox richTextBox = new RichTextBox();
            richTextBox.Rtf = richText;
            richTextBox.Height = this.Bounds.Height;
            richTextBox.Width = this.Bounds.Width;
            richTextBox.WordWrap = false;
            int nHeight = 0;
            int nMin = 0, nMax = 0;

            SCROLLBARINFO psbi = new SCROLLBARINFO();
            psbi.cbSize = Marshal.SizeOf(psbi);

            richTextBox.Height = 10;
            richTextBox.ScrollBars = RichTextBoxScrollBars.Vertical;

            int nResult = GetScrollBarInfo(richTextBox.Handle, OBJID_VSCROLL, ref psbi);
            if (psbi.rgstate[0] == 0) {
                GetScrollRange(richTextBox.Handle, SB_VERT, out nMin, out nMax);
                height = (nMax - nMin);
            }

            return height;
        }

You may need to modify above method to make it work as per your requirement... Make sure to send Rtf string as parameter to method not normal text and also make sure to assign available width and height to the Richtextbox variable in the method...

You can play with WordWrap depending on your requirement...

Paten answered 23/2, 2012 at 14:38 Comment(1)
This doesn't work. The height always ends up too short.Stoned
E
0

Add on to bathineni's great answer:

Background: I needed to measure RTF output height for rendering onto paper and because I have custom dynamic page headers/footers I needed to control paging).

(RichTextBox.GetLineFromCharIndex let me down because of complex RTF; including lines & multi column Tables with wordwrap).

Anyhow all was working fine, until someone else used my app with the dreaded windows "Make text and other items larger or smaller" (DPI settings.) - in short now measuring bigger sized fonts it screwed up the page length calculations. (the printer still rendered the text and columns correctly - only the page lengths were now all wrong.)

Only factoring DPI difference failed as in short bigger text didn't fit properly into source RTF tx and cellx values.

Anyhow, in case others are doing similar crazy things bit of trial and error came up with the following (eventually very few) mods to the bathineni CalculateRichTextHeight method:

RichTextBox richTextBox = new RichTextBox();     // same as original
int dpix = richTextBox.CreateGraphics().DpiX;    // get dpi
richTextBox.WordWrap = true;                     // I needed this, you many not
  // ... set size etc - same as original answer
richTextBox.Scale(new System.Drawing.SizeF(dpix / 96, dpix / 96));  // scale RTB
  // ...
  // 96? my original calculations based on windows default 96dpi settings.

Seems the otherwise obscure Control.Scale(sizef) is actually useful for something after all.

Note: if converting results to actual printed lines, (in my case all my \pard's were "\sl-240\slmult0" which comes out to 16 (pix?) per line) also remember to re-factor the divisor. i.e. in my case:

lines = height / (int)(16 * (dpix / 96))
Exegesis answered 17/10, 2014 at 7:0 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.