2

我扩展了 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;
                }
        }
    }

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

添加截图:

正确行为:

替代文字 http://www.charltonfamily.net/temp/RTB_EM_SETTARGETDEVICE/WrapToPrinter_Correct_Horizo​​ntal_Scrollbar.png

不正确的行为(在非常轻微地调整表单大小之后):

替代文字 http://www.charltonfamily.net/temp/RTB_EM_SETTARGETDEVICE/WrapToPrinter_Inappropriate_Horizo​​ntal_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 会给我的矩形?一个矩形会告诉我文本(包括超出屏幕的部分)有多宽?例如,如果我有一个 400 像素的控件和一行 800 像素长的文本,我想获得 800 像素的值,以便我可以将它与控件宽度进行比较并显示/不显示滚动条。

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

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

4

3 回答 3

2

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

请务必检查 SendMessage() 的返回值,如果 RichEdit 对您的参数不满意,它会返回 IntPtr.Zero。SDK 文档中没有提到为设备上下文句柄传递 0 作为可接受的值,您可能需要为屏幕传递 HDC。从 Control.CreateGraphics() 很容易得到。

我也不清楚你为什么不希望出现水平滚动条。我希望您在切换到打印机所见即所得模式时会看到一个。

于 2009-12-13T15:03:56.300 回答
0

您是否尝试过 EM_SHOWSCROLLBAR 消息

或者,您可以在调整大小之前尝试 WM_SETREDRAW (0),在完成后尝试 WM_SETREDRAW (1)。

于 2009-12-15T07:21:29.983 回答
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 会给我的矩形?一个矩形会告诉我文本(包括超出屏幕的部分)有多宽?例如,如果我有一个 400 像素的控件和一行 800 像素长的文本,我想获得 800 像素的值,以便我可以将它与控件宽度进行比较并显示/不显示滚动条。

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

于 2009-12-15T19:33:51.310 回答