So scrollen Sie ein RichTextBox-Steuerelement unabhängig von der Caret-Position zu einem bestimmten Punkt
In meiner WinForms-App habe ich ein RichTextBox-Steuerelement, das einen langen Text enthält. Ich muss es programmgesteuert zu einem bestimmten Punkt scrollen (ausgedrückt als Zeichenindex), unabhängig davon, wo sich das Auswahl-Caret befindet. Ich brauche eine Methode wie diese:
//scroll the control so that the 3512th character is visible.
rtf.ScrollToPosition(3512);
Alle Antworten auf ähnliche Fragen, die ich gefunden habe, verwenden die ScrollToCaret()
Methode. Dies ist in Ordnung, wenn Sie zur Caret-Position scrollen möchten. Aber ich muss zu einer anderen Position als zum Caret scrollen und ohne die Caret-Position zu ändern. Wie mache ich das?
Dankeschön.
Antworten
Sie können mit Sendmessage eine senden WM_VSCROLLNachricht an der Steuerung von RichEdit, unter Angabe SB_THUMBPOSITION
der LOWORD
von wParam
und die absoluten vertikalen Position zu blättern , um in dem HIWORD
.
Die GetPositionFromCharIndex- Methode (gehört zu TextBoxBase , gilt also auch für die TextBox-Klasse) gibt die relative physische Position zurück, an der ein Zeichen an einer bestimmten Position angezeigt wird (der Wert kann negativ sein, wenn die Zeichenposition über der aktuellen Bildlaufposition liegt und diese ist die Differenz zwischen der aktuellen Bildlaufposition und der Zeichenposition, wenn sie darunter liegt - es sei denn, die aktuelle Bildlaufposition ist 0
).
Angenommen, Ihre RichTextBox heißt richTextBox1
:
- Verwenden Sie z. B. Regex.Match , um die Position eines Wortes oder einer Phrase zu bestimmen. Die Position des übereinstimmenden Musters wird von der Index-Eigenschaft des Matchs zurückgegeben.
- Überprüfen Sie den Stromversatz mit
GetPositionFromCharIndex(0)
- Addieren Sie den absoluten Wert des Versatzes, der durch die aktuelle vertikale Position definiert ist, zu dem Wert - ausgedrückt in Pixel -, der von zurückgegeben wird
GetPositionFromCharIndex(matchPos)
. DabeimatchPos
handelt es sich um die Position eines Zeichens / Wortes / Musters, zu dem gescrollt werden soll. - Rufen Sie an,
SendMessage
indem Sie die berechnete Position verwenden und angeben, dass der DaumenSB_THUMBPOSITION
als Teil von wParam an diese Position bewegt werden soll.
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);
Deklaration nativer Methoden:
[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;
Sie können dieselbe Methode verwenden, die in der TextBoxBase.ScrollToCaret-Methode implementiert ist , um dies zu erreichen. Diese Methode basiert auf der Verwendung des COM - basierten Text - Objektmodell durch die darunter liegende RichEdit Steuerung implementiert.
Im Folgenden wird die Erweiterungsmethode definiert ScrollToCharPosition
. Es werden abgekürzte Definitionen der Schnittstellen ITextDocument und ITextRange verwendet.
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);
}
}
Anwendungsbeispiel :richtextbox1.ScrollToCharPosition(50)