【问题标题】:How to append text to RichTextBox without scrolling and losing selection?如何在不滚动和丢失选择的情况下将文本附加到 RichTextBox?
【发布时间】:2011-09-26 16:38:12
【问题描述】:

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

【问题讨论】:

    标签: c# .net winforms


    【解决方案1】:

    当您使用文本和选择文本方法时,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();
    

    【讨论】:

    • 有趣。您是否尝试过表单上的 .DoubleBuffered 属性?
    • @Robert DoubleBuffering 表单不会自动 DoubleBuffer 子控件。虽然有很多creative ways to try it
    • @AdamBruss 它应该可以工作。为什么它对你不起作用???我没有看到你的代码。如果您需要帮助,请发布一个有据可查的问题。
    • @AdamBruss 好的,我复制了它。如果我输入足够的文本来生成滚动条,然后滚动到顶部并将光标放在第一行,那么您的附加输出现在是黑色而不是红色。当您设置 SelectionColor 时,您的选择不在最后,因此您为错误的选择着色。我将不得不尝试解决问题。
    • @AdamBruss “hack”将提供一个 rtf 字符串:rtb.Select(rtb.TextLength, 0); rtb.SelectedRtf = @"{\rtf\ansi{\colortbl;\red255\green0\blue0;}\cf1 Red String\par}";rtb.Select(rtb.TextLength, 0);rtb.SelectionColor = Color.Black;
    【解决方案2】:

    基于 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");
    

    【讨论】:

      【解决方案3】:

      这个解决方案几乎是正确的,除了它不能正确处理反向选择(插入符号位于选择的开头,而不是结尾 - 例如 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();
              }
          }
      }
      

      【讨论】:

        【解决方案4】:

        修改了 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();
                    }
                }
            }
        

        【讨论】:

          【解决方案5】:

          这应该做你想做的:

                  Dim tempStart As Int32
              Dim tempLength As Int32
          
              tempStart = RichTextBox1.SelectionStart
              tempLength = RichTextBox1.SelectionLength
          
              RichTextBox1.Text += "dsfkljwerhsdlf"
          
              RichTextBox1.SelectionStart = tempStart
              RichTextBox1.SelectionLength = tempLength
          

          【讨论】:

            猜你喜欢
            • 2021-10-21
            • 2014-06-18
            • 2012-08-17
            • 2023-04-06
            • 2018-08-16
            • 1970-01-01
            • 2012-11-13
            • 1970-01-01
            • 2021-09-29
            相关资源
            最近更新 更多