如何在不滚动和丢失选择的情况下将文本附加到RichTextBox

本文关键字:文本 情况下 RichTextBox 选择 滚动 | 更新日期: 2023-09-27 18:00:57

我需要将文本附加到RichTextBox,并且需要在不滚动文本框或丢失当前文本选择的情况下执行,这可能吗?

如何在不滚动和丢失选择的情况下将文本附加到RichTextBox

WinForms中的RichTextBox在您处理文本和选择文本方法时非常闪烁。

我有一个标准的替代品来关闭绘画和滚动,代码如下:

class RichTextBoxEx: RichTextBox
{
  [DllImport("user32.dll")]
  static extern IntPtr SendMessage(IntPtr hWnd, Int32 wMsg, Int32 wParam, ref Point lParam);
  [DllImport("user32.dll")]
  static extern IntPtr SendMessage(IntPtr hWnd, Int32 wMsg, Int32 wParam, IntPtr lParam);
  const int WM_USER = 0x400;
  const int WM_SETREDRAW = 0x000B;
  const int EM_GETEVENTMASK = WM_USER + 59;
  const int EM_SETEVENTMASK = WM_USER + 69;
  const int EM_GETSCROLLPOS = WM_USER + 221;
  const int EM_SETSCROLLPOS = WM_USER + 222;
  Point _ScrollPoint;
  bool _Painting = true;
  IntPtr _EventMask;
  int _SuspendIndex = 0;
  int _SuspendLength = 0;
  public void SuspendPainting()
  {
    if (_Painting)
    {
      _SuspendIndex = this.SelectionStart;
      _SuspendLength = this.SelectionLength;
      SendMessage(this.Handle, EM_GETSCROLLPOS, 0, ref _ScrollPoint);
      SendMessage(this.Handle, WM_SETREDRAW, 0, IntPtr.Zero);
      _EventMask = SendMessage(this.Handle, EM_GETEVENTMASK, 0, IntPtr.Zero);
      _Painting = false;
    }
  }
  public void ResumePainting()
  {
    if (!_Painting)
    {
      this.Select(_SuspendIndex, _SuspendLength);
      SendMessage(this.Handle, EM_SETSCROLLPOS, 0, ref _ScrollPoint);
      SendMessage(this.Handle, EM_SETEVENTMASK, 0, _EventMask);
      SendMessage(this.Handle, WM_SETREDRAW, 1, IntPtr.Zero);
      _Painting = true;
      this.Invalidate();
    }
  }
}

然后从我的表单中,我可以很高兴地拥有一个无闪烁的richtextbox控件:

richTextBoxEx1.SuspendPainting();
richTextBoxEx1.AppendText("Hey!");
richTextBoxEx1.ResumePainting();

基于LarsTech的文章这里有一些不错的东西:

using System;
using System.Runtime.InteropServices;
using System.Windows.Forms;
using System.Drawing;
namespace yournamespace
{
    class RichTextBoxEx : RichTextBox
    {
        [DllImport("user32.dll")]
        static extern IntPtr SendMessage(IntPtr hWnd, Int32 wMsg, Int32 wParam, ref Point lParam);
        [DllImport("user32.dll")]
        static extern IntPtr SendMessage(IntPtr hWnd, Int32 wMsg, Int32 wParam, IntPtr lParam);
        const int WM_USER = 0x400;
        const int WM_SETREDRAW = 0x000B;
        const int EM_GETEVENTMASK = WM_USER + 59;
        const int EM_SETEVENTMASK = WM_USER + 69;
        const int EM_GETSCROLLPOS = WM_USER + 221;
        const int EM_SETSCROLLPOS = WM_USER + 222;
        Point _ScrollPoint;
        bool _Painting = true;
        IntPtr _EventMask;
        int _SuspendIndex = 0;
        int _SuspendLength = 0;
        public bool Autoscroll = true;
        public void SuspendPainting()
        {
            if (_Painting)
            {
                _SuspendIndex = this.SelectionStart;
                _SuspendLength = this.SelectionLength;
                SendMessage(this.Handle, EM_GETSCROLLPOS, 0, ref _ScrollPoint);
                SendMessage(this.Handle, WM_SETREDRAW, 0, IntPtr.Zero);
                _EventMask = SendMessage(this.Handle, EM_GETEVENTMASK, 0, IntPtr.Zero);
                _Painting = false;
            }
        }
        public void ResumePainting()
        {
            if (!_Painting)
            {
                this.Select(_SuspendIndex, _SuspendLength);
                SendMessage(this.Handle, EM_SETSCROLLPOS, 0, ref _ScrollPoint);
                SendMessage(this.Handle, EM_SETEVENTMASK, 0, _EventMask);
                SendMessage(this.Handle, WM_SETREDRAW, 1, IntPtr.Zero);
                _Painting = true;
                this.Invalidate();
            }
        }
        new public void AppendText(string text)  // overwrites RichTextBox.AppendText
        {
            if (Autoscroll)
                base.AppendText(text);
            else
            {
                SuspendPainting();
                base.AppendText(text);
                ResumePainting();
            }
        }
    }
}

你可以这样使用它:

var textbox = new RichTextBoxEx();
textbox.Autoscroll = false;
textbox.AppendText("Hi");

这个解决方案几乎是到位的,只是它不能正确地处理反向选择(插入符号位于选择的开头,而不是末尾,例如SHIFT+LEFT或向上拖动鼠标来选择文本(。

这是一个改进的版本,增加了以下功能:

  • 如果插入符号位于文本的末尾,则它会保留在那里,并在需要时滚动
  • 如果原始选择从最后一个字符开始或结束,则任何附加的文本都将包含在新选择中

这意味着您可以将插入符号放在文本的末尾,并监视要添加的文本(想想日志文件监视(。这也意味着您可以按CTRL+A键选择所有文本,并将任何附加文本自动包含在您的选择中。

class RichTextBoxEx : RichTextBox
{
    [DllImport("user32.dll")]
    private static extern IntPtr SendMessage(IntPtr hWnd, Int32 wMsg, Int32 wParam, ref Point lParam);
    [DllImport("user32.dll")]
    private static extern IntPtr SendMessage(IntPtr hWnd, Int32 wMsg, Int32 wParam, IntPtr lParam);
    [DllImport("user32")]
    private static extern int GetCaretPos(out Point p);
    const int WM_USER = 0x400;
    const int WM_SETREDRAW = 0x000B;
    const int EM_GETEVENTMASK = WM_USER + 59;
    const int EM_SETEVENTMASK = WM_USER + 69;
    const int EM_GETSCROLLPOS = WM_USER + 221;
    const int EM_SETSCROLLPOS = WM_USER + 222;
    private Point oScrollPoint;
    private bool bPainting = true;
    private IntPtr oEventMask;
    private int iSuspendCaret;
    private int iSuspendIndex;
    private int iSuspendLength;
    private bool bWasAtEnd;
    public int CaretIndex
    {
        get
        {
            Point oCaret;
            GetCaretPos(out oCaret);
            return this.GetCharIndexFromPosition(oCaret);
        }
    }
    public void AppendTextWithoutScroll(string text)
    {
        this.SuspendPainting();
        this.AppendText(text);
        this.ResumePainting();
    }
    private void SuspendPainting()
    {
        if (this.bPainting)
        {
            this.iSuspendCaret = this.CaretIndex;
            this.iSuspendIndex = this.SelectionStart;
            this.iSuspendLength = this.SelectionLength;
            this.bWasAtEnd = this.iSuspendIndex + this.iSuspendLength == this.TextLength;
            SendMessage(this.Handle, EM_GETSCROLLPOS, 0, ref this.oScrollPoint);
            SendMessage(this.Handle, WM_SETREDRAW, 0, IntPtr.Zero);
            this.oEventMask = SendMessage(this.Handle, EM_GETEVENTMASK, 0, IntPtr.Zero);
            this.bPainting = false;
        }
    }
    private void ResumePainting()
    {
        if (!this.bPainting)
        {
            if (this.iSuspendLength == 0)
            {
                if (!bWasAtEnd)
                {
                    this.Select(this.iSuspendIndex, 0);
                }
            }
            else
            {
                // Original selection was to end of text
                if (bWasAtEnd)
                {
                    // Maintain end of selection at end of new text
                    this.iSuspendLength = this.TextLength - this.iSuspendIndex;
                }
                if (this.iSuspendCaret > this.iSuspendIndex)
                {
                    // Forward select (caret is at end)
                    this.Select(this.iSuspendIndex, this.iSuspendLength);
                }
                else
                {
                    // Reverse select (caret is at start)
                    this.Select(this.iSuspendIndex + this.iSuspendLength, -this.iSuspendLength);
                }
            }
            SendMessage(this.Handle, EM_SETSCROLLPOS, 0, ref this.oScrollPoint);
            SendMessage(this.Handle, EM_SETEVENTMASK, 0, this.oEventMask);
            SendMessage(this.Handle, WM_SETREDRAW, 1, IntPtr.Zero);
            this.bPainting = true;
            this.Invalidate();
        }
    }
}

修改了LarsTech代码,使其在插入符号不在RichTextBox的最后一个位置时自动停止自动滚动。还解决了输入彩色文本的问题。要继续滚动,请将插入符号放在最后一个位置(Ctrl END(

    class RichTextBoxEx : RichTextBox
    {
        [DllImport("user32.dll")]
        private static extern IntPtr SendMessage(IntPtr hWnd, Int32 wMsg, Int32 wParam, ref Point lParam);
        [DllImport("user32.dll")]
        private static extern IntPtr SendMessage(IntPtr hWnd, Int32 wMsg, Int32 wParam, IntPtr lParam);
        [DllImport("user32")]
        private static extern int GetCaretPos(out Point p);
        const int WM_USER = 0x400;
        const int WM_SETREDRAW = 0x000B;
        const int EM_GETEVENTMASK = WM_USER + 59;
        const int EM_SETEVENTMASK = WM_USER + 69;
        const int EM_GETSCROLLPOS = WM_USER + 221;
        const int EM_SETSCROLLPOS = WM_USER + 222;
        private Point oScrollPoint;
        private bool bPainting = true;
        private IntPtr oEventMask;
        private int iSuspendCaret;
        private int iSuspendIndex;
        private int iSuspendLength;
        private bool bWasAtEnd;
        private Color _selColor = Color.Black;
        public int CaretIndex
        {
            get
            {
                Point oCaret;
                GetCaretPos(out oCaret);
                return this.GetCharIndexFromPosition(oCaret);
            }
        }
        new public Color SelectionColor { get { return _selColor; } set { _selColor = value; } }
        new public void AppendText(string text)  // overwrites RichTextBox.AppendText
        {
            if (this.SelectionStart >= this.TextLength)
            {
                base.SelectionColor = _selColor;
                base.AppendText(text);
            }
            else
            {
                var selStart = this.SelectionStart;
                var selLength = this.SelectionLength;
                SuspendPainting();
                this.Select(this.TextLength, 0);
                base.SelectionColor = _selColor;
                base.AppendText(text);
                this.Select(selStart, selLength);
                ResumePainting();
            }
        }
        private void SuspendPainting()
        {
            if (this.bPainting)
            {
                this.iSuspendCaret = this.CaretIndex;
                this.iSuspendIndex = this.SelectionStart;
                this.iSuspendLength = this.SelectionLength;
                this.bWasAtEnd = this.iSuspendIndex + this.iSuspendLength == this.TextLength;
                SendMessage(this.Handle, EM_GETSCROLLPOS, 0, ref this.oScrollPoint);
                SendMessage(this.Handle, WM_SETREDRAW, 0, IntPtr.Zero);
                this.oEventMask = SendMessage(this.Handle, EM_GETEVENTMASK, 0, IntPtr.Zero);
                this.bPainting = false;
            }
        }
        private void ResumePainting()
        {
            if (!this.bPainting)
            {
                if (this.iSuspendLength == 0)
                {
                    if (!bWasAtEnd)
                    {
                        this.Select(this.iSuspendIndex, 0);
                    }
                }
                else
                {
                    // Original selection was to end of text
                    if (bWasAtEnd)
                    {
                        // Maintain end of selection at end of new text
                        this.iSuspendLength = this.TextLength - this.iSuspendIndex;
                    }
                    if (this.iSuspendCaret > this.iSuspendIndex)
                    {
                        // Forward select (caret is at end)
                        this.Select(this.iSuspendIndex, this.iSuspendLength);
                    }
                    else
                    {
                        // Reverse select (caret is at start)
                        this.Select(this.iSuspendIndex + this.iSuspendLength, -this.iSuspendLength);
                    }
                }
                SendMessage(this.Handle, EM_SETSCROLLPOS, 0, ref this.oScrollPoint);
                SendMessage(this.Handle, EM_SETEVENTMASK, 0, this.oEventMask);
                SendMessage(this.Handle, WM_SETREDRAW, 1, IntPtr.Zero);
                this.bPainting = true;
                this.Invalidate();
            }
        }
    }

这应该可以实现您想要的:

        Dim tempStart As Int32
    Dim tempLength As Int32
    tempStart = RichTextBox1.SelectionStart
    tempLength = RichTextBox1.SelectionLength
    RichTextBox1.Text += "dsfkljwerhsdlf"
    RichTextBox1.SelectionStart = tempStart
    RichTextBox1.SelectionLength = tempLength
相关文章: