RichTextBox:如何确定文本是否超出控件边界?

时间:2009-12-13 14:11:55

标签: c# .net richtextbox scrollbar sendmessage

我扩展了RichTextBox控件,以实现本机RichEdit类中提供的大部分缺少的功能。

我遇到了一个问题,如果将控件设置为包裹到窗口或包装到打印机,则会出现水平滚动条,即使控件调整大小时也不应该。

将wordwrap循环到none并返回似乎可以解决问题,但在打包到打印机时可能相对较慢(即:在每个Resize事件上调用太慢)。

这是我的自动换行代码:

   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;
                }
        }
    }

最初我认为问题可能与我正在发布图形句柄的事实有关,但是当我包装到控件并且不需要句柄时也会出现问题。

添加屏幕截图:

正确行为:

alt text http://www.charltonfamily.net/temp/RTB_EM_SETTARGETDEVICE/WrapToPrinter_Correct_Horizontal_Scrollbar.png

行为不正确(稍微调整表单 非常 后):

alt text http://www.charltonfamily.net/temp/RTB_EM_SETTARGETDEVICE/WrapToPrinter_Inappropriate_Horizontal_Scrollbar.png

换页窗口/无换行代码来自http://msdn.microsoft.com/en-us/library/bb774282(VS.85).aspx

的评论

调用:: SendMessage(hwnd,EM_SETTARGETDEVICE,NULL,0)将文本换行到窗口,:: SendMessage(hwnd,EM_SETTARGETDEVICE,NULL,1)将完全禁用自动换行。我不确定其他地方是否有记录。

我的p / invoke:

    [DllImport("user32.dll")]
    public static extern int SendMessage(IntPtr hWnd, int msg, int wParam, int lParam);

相关常数:

    public const int WM_USER = 0x400;
    public const int EM_SETTARGETDEVICE = (WM_USER + 72);

修改

我一直在研究这个,我相信.NET RichTextBox控件可能在调整控件大小时发送一个不正确的值的SetScrollRange()。这是有道理的,因为它不一定知道EM_SETTARGETDEVICE消息。

我可能会在调整大小后执行SetScrollRange()或类似的东西,但我的问题是我不知道正确的值是什么,或者我怎么能去解决这个问题。

我注意到的另一件事是,当问题出现时,我可以将控件的大小调整为实际包装文本的大小。此时滚动条回到功能状态,我可以调整大小,直到自动换行循环到无,然后回来。

编辑(另请参阅下面的非功能性答案中的更多细节)

看起来EM_GETRECT不是我真正想要的,因为当控件大小改变时它的大小会发生变化。这是MSDN描述:

EM_GETRECT消息

  

获取编辑控件的格式化矩形。格式化矩形是控件绘制文本的限制矩形。限制矩形与编辑控制窗口的大小无关。您可以将此消息发送到编辑控件或丰富的编辑控件。

我最初的理解是,这是整个文本的矩形,从中我可以确定客户端窗口是否小于格式化矩形,因此应该显示滚动条。

看起来这个格式化矩形的真正目的是使文本显示在小于编辑控件的区域(即:边距)。

新问题:

那么,是否有一个我认为EM_GETRECT会给我的矩形?一个矩形,它会告诉我文本的宽度(包括屏幕上运行的部分)是多少?例如,如果我有一个400px的控件和800px长的文本行,我想得到800px的值,所以我可以将它与控制宽度进行比较并显示/不显示滚动条。

实际上我并不关心非控制文本的长度,因为我关心知道 IF 文本超出了控件的范围。

感谢目前为止提供的所有帮助。

3 个答案:

答案 0 :(得分:2)

我没有尝试过你的代码,没有P / Invoke声明就不容易运行。虽然您的SendMessage的LPARAM参数声明看起来不对,但它应该是IntPtr。为WPARAM传递0也不应该编译,也不确定你做了什么。

确保检查SendMessage()的返回值,如果RichEdit对您的参数不满意,则返回IntPtr.Zero。在SDK文档中未提及为设备上下文句柄传递0作为可接受的值,您可能需要为屏幕传递HDC。很容易从Control.CreateGraphics()获得。

我也不清楚为什么你不想要出现水平滚动条。当你切换到打印机WYSIWYG模式时,我希望你能看到一个。

答案 1 :(得分:0)

您是否尝试过the EM_SHOWSCROLLBAR Message

或者,你可以试试 调整大小之前的WM_SETREDRAW(0)和完成之后的WM_SETREDRAW(1)。

答案 2 :(得分:0)

我有一些工作,但这不是我真正想要的。

我真正想要的是让RichTextBox做我想做的事。相反,我让RichTextBox按照自己的意愿行事,然后尝试修复它。这是一个讨厌的解决方案,有时导致滚动条闪烁。

这就是我正在做的事情:

SCROLLINFO scrollinfo = new SCROLLINFO();
scrollinfo.cbSize = Marshal.SizeOf(scrollinfo);
scrollinfo.fMask = ApiConstants.SIF_ALL;
bool flag1 = User32.GetScrollInfo(_RichTextBox.Handle, ApiConstants.SB_HORZ, ref scrollinfo);

Logging.LogMessage("Resize - ScrollInfo: Max: " + scrollinfo.nMax + " Min: " + scrollinfo.nMin + " Page: " + scrollinfo.nPage + " Pos: " + scrollinfo.nPos + " TrackPos: " + scrollinfo.nTrackPos + " || RichtTextBox.RightMargin == " + _RichTextBox.RightMargin + " || RichTextBox.WordWrap == " + WordWrap + " / " + _RichTextBox.WordWrap + " Size: " + Size + " ClientRectangle: " + _RichTextBox.ClientSize);

switch (WordWrap)
{
    case WordWrap.WrapToControl:
    {
        if (scrollinfo.nMax > _RichTextBox.ClientSize.Width)
        {
            User32.ShowScrollBar(_RichTextBox.Handle, ApiConstants.SB_HORZ, false);
        }
        break;
    }
    case WordWrap.WrapToPrintDocument:
    {
        if (scrollinfo.nMax > _PrintableWidth)
        {
            User32.ShowScrollBar(_RichTextBox.Handle, ApiConstants.SB_HORZ, false);
        }
        break;
    }
}

WordWrap.WrapToControl条件中的逻辑被破坏了,我不知道如何修复它。问题是当处于错误状态时,最大滚动量大于ClientSize。但是,当文本实际在屏幕上运行时(考虑左缩进),最大滚动也大于ClientSize。

我想我可以通过使用EM_GETRECT消息来解决这个问题,但我仍然需要做更多的测试。 EM_GETRECT解决方案可能也适用于WrapToPrintDocument条件,但是如果它为什么不能为RichTextBox执行此操作呢?真的希望我能找到RichTextBox从哪里得到它的SCROLLINFO参数,因为那时我可以让RichTextBox控件做我想要的而不需要第二个“修复”消息。

注意:我不需要显示滚动条的情况,因为RichTextBox在隐藏它之后处理该部分(叹息

修改

看起来EM_GETRECT不是我真正想要的,因为当控件大小改变时它的大小会发生变化。这是MSDN描述:

EM_GETRECT消息 获取编辑控件的格式化矩形。格式化矩形是控件绘制文本的限制矩形。限制矩形与编辑控制窗口的大小无关。您可以将此消息发送到编辑控件或丰富的编辑控件。

我最初的理解是,这是整个文本的矩形,从中我可以确定客户端窗口是否小于格式化矩形,因此应该显示滚动条。

看起来这个格式化矩形的真正目的是使文本显示在小于编辑控件的区域(即:边距)。

新问题:

那么,是否有一个我认为EM_GETRECT会给我的矩形?一个矩形,它会告诉我文本的宽度(包括屏幕上运行的部分)是多少?例如,如果我有一个400px的控件和800px长的文本行,我想得到800px的值,所以我可以将它与控制宽度进行比较并显示/不显示滚动条。

感谢目前为止提供的所有帮助。