I have extended the RichTextBox control to implement much of the missing functionality provided in the native RichEdit class.
I'm running into an issue where if the control is set to wrap to the window or to wrap to printer the horizontal scrollbar appears even though it shouldn't when the control is resized.
Cycling the wordwrap to none and back seems to resolve the problem but can be relatively slow when wrapping to a printer (ie: much too slow to call on every Resize event).
Here's my wordwrap code:
private void ChangeWordWrap(WordWrap wordWrap)
{
switch (wordWrap)
{
case WordWrap.NoWrap:
{
User32.SendMessage(_RichTextBox.Handle, ApiConstants.EM_SETTARGETDEVICE, 0, 1);
break;
}
case WordWrap.WrapToPrintDocument:
{
using (Graphics g = PrintDocument.PrinterSettings.CreateMeasurementGraphics(PrintDocument.DefaultPageSettings))
{
int lParam = ConvertEx.HundredthInchToTwips((PrintDocument.DefaultPageSettings.Bounds.Width - PrintDocument.DefaultPageSettings.Margins.Left - PrintDocument.DefaultPageSettings.Margins.Right));
IntPtr wParam = g.GetHdc();
User32.SendMessage(_RichTextBox.Handle, ApiConstants.EM_SETTARGETDEVICE, wParam, lParam);
g.ReleaseHdc();
}
break;
}
case WordWrap.WrapToControl:
{
User32.SendMessage(_RichTextBox.Handle, ApiConstants.EM_SETTARGETDEVICE, 0, 0);
break;
}
}
}
Originially I thought the problem might be related to the fact I'm releasing the graphics handle but the issue also occurs when I'm wrapping to the control and no handle is needed.
Adding screenshots:
Correct behavior:
Incorrect behavior (after resizing the form VERY slightly):
The wrap to window/no wrap code came from a comment at http://msdn.microsoft.com/en-us/library/bb774282%28VS.85%29.aspx
*Calling ::SendMessage(hwnd, EM_SETTARGETDEVICE, NULL, 0) will wrap text to the window, and ::SendMessage(hwnd, EM_SETTARGETDEVICE, NULL, 1) will disable word-wrap entirely. I'm not sure this is documented anywhere else.*
My p/invoke:
[DllImport("user32.dll")]
public static extern int SendMessage(IntPtr hWnd, int msg, int wParam, int lParam);
Related Constants:
public const int WM_USER = 0x400;
public const int EM_SETTARGETDEVICE = (WM_USER + 72);
Edit:
I've been researching this more and I believe the .NET RichTextBox control is probably sending a SetScrollRange() with incorrect values when the control is resized. Which would make sense since it doesn't necessarily know about the EM_SETTARGETDEVICE message.
I could probably execute SetScrollRange() or something similiar after a resize but my problem is that I have no idea what the correct values would be or how I could go about figuring that out.
Another thing I've noticed is that when the problem is occuring I can resize the control to a size that would actually wrap the text. At this point the scrollbar goes back to a functional state and I can resize until the word wrap is cycled to none and back.
Edit: (Also some more detail in my non-functional answer below)
It looks like EM_GETRECT is not what I actually want since it's size changes when the control size changes. Here's the MSDN description:
EM_GETRECT Message Gets the formatting rectangle of an edit control. The formatting rectangle is the limiting rectangle into which the control draws the text. The limiting rectangle is independent of the size of the edit-control window. You can send this message to either an edit control or a rich edit control.
My initial understanding was that this was the rectangle for the entire text and from it I would be able to determine if the client window was smaller that the formatting rectangle and thus a scrollbar should be shown.
Looks like the real purpose of this formatting rectangle is to make the text display in an area smaller than the Edit Control (ie: margins).
New question:
So, is there a rectangle that is what I thought EM_GETRECT would give me? A rectangle that will tell me how wide the text (including the part that runs off the screen) is? For instance if I had a 400px control and a line of text that was 800px long I want to get the 800px value so I can compare it to control width and show/not show the scrollbar.
Actually I don't care about the length of the off-control text so much as I care about know IF text extends beyond the bounds of the control.
Thanks for all the help so far.