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

C# 如何在不滚动和丢失选择的情况下将文本附加到RichTextBox?,c#,.net,winforms,C#,.net,Winforms,我需要将文本附加到RichTextBox,并且需要在不使文本框滚动或丢失当前文本选择的情况下执行此操作,是否可能?这应该可以满足您的要求: Dim tempStart As Int32 Dim tempLength As Int32 tempStart = RichTextBox1.SelectionStart tempLength = RichTextBox1.SelectionLength RichTextBox1.Text += "dsf

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

这应该可以满足您的要求:

        Dim tempStart As Int32
    Dim tempLength As Int32

    tempStart = RichTextBox1.SelectionStart
    tempLength = RichTextBox1.SelectionLength

    RichTextBox1.Text += "dsfkljwerhsdlf"

    RichTextBox1.SelectionStart = tempStart
    RichTextBox1.SelectionLength = tempLength

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)


有趣。您是否尝试过。窗体上的DoubleBuffered属性?@Robert DoubleBuffering窗体不会自动对子控件进行双缓冲。虽然已经有了很多,但它应该是有效的。为什么它不适合你???我看不到你的代码。如果您需要帮助,请发布一个记录完整的问题。@AdamBruss好的,我复制了它。如果我输入足够的文本来生成一个滚动条,然后滚动到顶部并将光标放在第一行上,您附加的输出现在是黑色而不是红色。设置SelectionColor时,您的选择不在末尾,因此您为错误的选择着色。我将不得不使用变通方法。@AdamBruss“hack”将提供一个rtf字符串:
rtb.Select(rtb.TextLength,0);rtb.SelectedRtf=@“{\rtf\ansi{\colortbl;\red255\green0\blue0;}\cf1红色字符串\par}”;选择(rtb.TextLength,0);rtb.SelectionColor=颜色.黑色
    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();
            }
        }
    }