无论插入符位置如何,如何将 RichTextBox 控件滚动到给定点
How to scroll a RichTextBox control to a given point regardless of caret position
在我的 WinForms 应用程序中,我有一个包含长文本的 RichTextBox 控件。我需要以编程方式将其滚动到给定点(表示为字符索引),而不管选择插入符号位于何处。我需要这样的方法:
//scroll the control so that the 3512th character is visible.
rtf.ScrollToPosition(3512);
我找到的所有类似问题的答案都使用 ScrollToCaret()
方法,如果您想滚动到插入位置,这很好。但我需要滚动到不同的位置而不是插入符号,并且不改变插入符号的位置。我该怎么做?
谢谢。
您可以使用在由底层 RichEdit 控件实现的 TextBoxBase.ScrollToCaret Method to accomplish this. This methodology is based on using the COM based Text Object Model 中实现的相同方法。
下面定义扩展方法ScrollToCharPosition
。它使用 ITextDocument and 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)
您可以向 RichEdit 控件使用 SendMessage to send a WM_VSCROLL
消息,在 wParam
的 LOWORD
中指定 SB_THUMBPOSITION
并在 [= 中指定要滚动到的绝对垂直位置16=].
GetPositionFromCharIndex method (belongs to TextBoxBase,所以它也适用于 TextBox class) returns 相对 physical 位置显示特定位置(如果字符位置高于当前滚动位置,则该值可以为负,如果低于当前滚动位置,则为当前滚动位置与字符位置之间的差 - 除非当前滚动位置为 0
) .
假设您的 RichTextBox 名为 richTextBox1
:
- 例如,使用 Regex.Match 来确定单词或短语的位置;匹配图案的位置由匹配的索引 属性 返回。
- 用
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;
在我的 WinForms 应用程序中,我有一个包含长文本的 RichTextBox 控件。我需要以编程方式将其滚动到给定点(表示为字符索引),而不管选择插入符号位于何处。我需要这样的方法:
//scroll the control so that the 3512th character is visible.
rtf.ScrollToPosition(3512);
我找到的所有类似问题的答案都使用 ScrollToCaret()
方法,如果您想滚动到插入位置,这很好。但我需要滚动到不同的位置而不是插入符号,并且不改变插入符号的位置。我该怎么做?
谢谢。
您可以使用在由底层 RichEdit 控件实现的 TextBoxBase.ScrollToCaret Method to accomplish this. This methodology is based on using the COM based Text Object Model 中实现的相同方法。
下面定义扩展方法ScrollToCharPosition
。它使用 ITextDocument and 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)
您可以向 RichEdit 控件使用 SendMessage to send a WM_VSCROLL
消息,在 wParam
的 LOWORD
中指定 SB_THUMBPOSITION
并在 [= 中指定要滚动到的绝对垂直位置16=].
GetPositionFromCharIndex method (belongs to TextBoxBase,所以它也适用于 TextBox class) returns 相对 physical 位置显示特定位置(如果字符位置高于当前滚动位置,则该值可以为负,如果低于当前滚动位置,则为当前滚动位置与字符位置之间的差 - 除非当前滚动位置为 0
) .
假设您的 RichTextBox 名为 richTextBox1
:
- 例如,使用 Regex.Match 来确定单词或短语的位置;匹配图案的位置由匹配的索引 属性 返回。
- 用
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;