views:

544

answers:

1

I am trying to fit a WPF RichTextBox to exactly accommodate a grid of characters in a particular monospace font. I am currently using FormattedText to determine the width and height of my RichTextBox, but the measurements it is providing me with are too small--specifically two characters in width too small.

Is there a better way to perform this task? This does not seem to be an appropriate way to determine the size of my control.

RichTextBox rtb;
rtb = new RichTextBox();
FontFamily fontFamily = new FontFamily("Consolas");
double fontSize = 16;
char standardizationCharacter = 'X';
String standardizationLine = "";
for(long loop = 0; loop < columns; loop ++) {
    standardizationLine += standardizationCharacter;
}
standardizationLine += Environment.NewLine;
String standardizationString = "";
for(long loop = 0; loop < rows; loop ++) {
    standardizationString += standardizationLine;
}
Typeface typeface = new Typeface(fontFamily, FontStyles.Normal, FontWeights.Normal, FontStretches.Normal);
FormattedText formattedText = new FormattedText(standardizationString, CultureInfo.CurrentCulture, FlowDirection.LeftToRight, typeface, fontSize, Brushes.Black);
rtb.Width = formattedText.Width;
rtb.Height = formattedText.Height;
+1  A: 

Assuming you have a very basic RichTextBox with just the standard FlowDocument inside it then you have to take into account all of the extra layout RichTextBox uses.

The Document.PagePadding property defaults to {5,0,5,0}. So you will have to add 10 to the width.

Additionally the RichTextBox has a BorderThickness that defaults to {1, 1, 1, 1}. So you will have to add 2 to the width.

So the code you want could look like:

var pagePadding = rtb.Document.PagePadding;
var borderThickness = rtb.BorderThickness;
rtb.Width = formattedText.Width
    + pagePadding.Left + pagePadding.Right 
    + borderThickness.Left + borderThickness.Right;

If you do that you will still be off by 1 character, but that's a bit misleading. The RichTextBox will wrap even if the text is just a tiny bit to wide not just a whole character width. If you add 2 to the width it works out. I can't figure out exactly where that extra 2 is coming from. I've never tried to get the width to be so exact. I have run into the PagePadding and BorderThickness issues before, but I just can't find the extra 2. It might just be a constant you are stuck with, but I doubt it. It's got to be somewhere.

A small tip on producing the StandardizationLine you can do

string StandardizationLine = new string('X', columns);

to clean up the loop.

Mike Two
Wonderful answer. To answer your earlier question, I am writing to the RichTextBox by catching OnPreviewKeyDown and OnPreviewTextInput and manipulating the strings inside Runs manually.
oxeb
There appears to be some issues with double.NaN (Auto) with this answer, but I think the BorderThickness \ PagePadding pages explain how to deal with this. I am not quite sure yet.
oxeb
@oxeb - Where are you getting the `double.NaN` problem? It doesn't surprise me too much. I did test this but only on a project with just a simple `RichTextBox` in VS2010. It is possible that some of the defaults are different. I should have stated that you might need to add error checking. Hope it's helping.
Mike Two
It is helping! I got it all working using a combination of this technique for the width and letting it size itself with AUTO for the height and then freezing it by basically doing Height = ActualHeight with a callback.
oxeb