如何将RichTextBox控件滚动到给定的点,而不考虑插入符号的位置



在我的WinForms应用程序中,我有一个包含长文本的RichTextBox控件。无论选择插入符号位于何处,我都需要以编程方式将其滚动到给定的点(表示为字符索引(。我需要这样的方法:

//scroll the control so that the 3512th character is visible.
rtf.ScrollToPosition(3512);

我找到的所有类似问题的答案都使用ScrollToCaret()方法,如果你想滚动到插入符号位置,这很好。但我需要滚动到不同的位置,而不是插入符号,并且不更改插入符号的位置。我该怎么做?

谢谢。

您可以使用在TextBoxBase.ScrollToCaret方法中实现的相同方法来实现这一点。此方法基于使用由底层RichEdit控件实现的基于COM的文本对象模型。

以下定义了扩展方法ScrollToCharPosition。它使用ITextDocument和ITextRange接口的缩写定义。

public static class RTBExtensions
{
public static void ScrollToCharPosition(this RichTextBox rtb, Int32 charPosition)
{
const Int32 WM_USER = 0x400;
const Int32 EM_GETOLEINTERFACE = WM_USER + 60;
const Int32 tomStart = 32;
if (charPosition < 0 || charPosition > rtb.TextLength - 1)
{
throw new ArgumentOutOfRangeException(nameof(charPosition), $"{nameof(charPosition)} must be in the range of 0 to {rtb.TextLength - 1}.");
}
// retrieve the rtb's OLEINTERFACE and use the Interop Marshaller to cast it as an ITextDocument
// The control calls the AddRef method for the object before returning, so the calling application must call the Release method when it is done with the object.
ITextDocument doc = null;
SendMessage(new HandleRef(rtb, rtb.Handle), EM_GETOLEINTERFACE, IntPtr.Zero, ref doc);
ITextRange rng = null;
if (doc != null)
{
try
{
rng = (RTBExtensions.ITextRange)doc.Range(charPosition, charPosition);
rng.ScrollIntoView(tomStart);
}
finally
{
if (rng != null)
{
Marshal.ReleaseComObject(rng);
}
Marshal.ReleaseComObject(doc);
}
}
}
[DllImport("user32.dll", CharSet = CharSet.Auto)]
private extern static IntPtr SendMessage(HandleRef hWnd, Int32 msg, IntPtr wParam, ref ITextDocument lParam);
[ComImport, Guid("8CC497C0-A1DF-11CE-8098-00AA0047BE5D")]
private interface ITextDocument
{
[MethodImpl((short)0, MethodCodeType = MethodCodeType.Runtime)]
void _VtblGap1_17();
[return: MarshalAs(UnmanagedType.Interface)]
[MethodImpl(MethodImplOptions.InternalCall, MethodCodeType = MethodCodeType.Runtime), DispId(15)]
ITextRange Range([In] int cp1, [In] int cp2);
}
[ComImport, Guid("8CC497C2-A1DF-11CE-8098-00AA0047BE5D")]
private interface ITextRange
{
[MethodImpl((short)0, MethodCodeType = MethodCodeType.Runtime)]
void _VtblGap1_49();
[MethodImpl(MethodImplOptions.InternalCall, MethodCodeType = MethodCodeType.Runtime), DispId(0x242)]
void ScrollIntoView([In] int Value);
}
}

示例用法richtextbox1.ScrollToCharPosition(50)

您可以使用SendMessage向RichEdit控件发送WM_VSCROLL消息,在wParamLOWORD中指定SB_THUMBPOSITION,并在HIWORD中指定要滚动到的绝对垂直位置。

GetPositionFromCharIndex方法(属于TextBoxBase,因此它也适用于TextBox类(返回显示特定位置的字符的相对物理位置(如果字符位置在当前滚动位置上方,则该值可能为负值;如果字符位置低于当前滚动位置,则该数值为当前滚动位置和字符位置之间的差值,除非当前滚动位置为0(。


假设您的RichTextBox命名为richTextBox1:

  • 使用例如Regex.Match来确定单词或短语的位置;匹配的图案化的位置由Match的Index属性返回
  • GetPositionFromCharIndex(0)检查电流偏移
  • 将当前垂直位置定义的偏移量的绝对值与GetPositionFromCharIndex(matchPos)返回的值(以像素表示(相加,其中matchPos是要滚动到的字符/单词/模式的位置
  • 使用计算的位置调用SendMessage,并指定将拇指移动到此位置,将SB_THUMBPOSITION作为wParam的一部分
var matchPos = Regex.Match(richTextBox1.Text, @"some words").Index;
var pos0 = richTextBox1.GetPositionFromCharIndex(0).Y;
var pos = richTextBox1.GetPositionFromCharIndex(matchPos).Y + Math.Abs(pos0 - 1);
SendMessage(richTextBox1.Handle, WM_VSCROLL, pos << 16 | SB_THUMBPOSITION, 0);

本机方法声明:

[DllImport("user32.dll", CharSet = CharSet.Auto, SetLastError = true)]
internal static extern int SendMessage(IntPtr hWnd, uint uMsg, int wParam, int lParam);
private const uint WM_VSCROLL = 0x0115;
private const int SB_THUMBPOSITION = 4;

最新更新