【发布时间】:2011-09-26 16:38:12
【问题描述】:
我需要将文本附加到 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();
【讨论】:
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;
基于 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
【讨论】: