Comment faire défiler un contrôle RichTextBox vers un point donné quelle que soit la position du curseur
Dans mon application WinForms, j'ai un contrôle RichTextBox contenant un long texte. J'ai besoin de le faire défiler par programme jusqu'à un point donné (exprimé sous forme d'index de caractères), quel que soit l'emplacement du curseur de sélection. J'ai besoin d'une méthode comme celle-ci:
//scroll the control so that the 3512th character is visible.
rtf.ScrollToPosition(3512);
Toutes les réponses à des questions similaires que j'ai trouvées utilisent la ScrollToCaret()
méthode, ce qui est bien si vous voulez faire défiler jusqu'à la position du curseur. Mais j'ai besoin de faire défiler vers une position différente plutôt que le curseur, et sans changer la position du curseur. Comment puis-je faire cela?
Merci.
Réponses
Vous pouvez utiliser SendMessage pour envoyer un WM_VSCROLLmessage au contrôle RichEdit, en spécifiant SB_THUMBPOSITION
dans le LOWORD
de wParam
et la position verticale absolue vers laquelle faire défiler le HIWORD
.
La méthode GetPositionFromCharIndex (appartient à TextBoxBase , elle s'applique donc également à la classe TextBox) renvoie la position physique relative où un caractère dans une position spécifique est affiché (la valeur peut être négative si la position du caractère est au-dessus de la position de défilement actuelle et la différence entre la position de défilement actuelle et la position du caractère si elle est en dessous - sauf si la position de défilement actuelle est 0
).
Supposons que votre RichTextBox est nommé richTextBox1
:
- Utilisez, par exemple, Regex.Match pour déterminer la position d'un mot ou d'une phrase; la position du motif correspondant est renvoyée par la propriété Index de Match.
- Vérifiez le décalage actuel avec
GetPositionFromCharIndex(0)
- Ajoutez la valeur absolue du décalage défini par la position verticale actuelle à la valeur - exprimée en pixels - renvoyée par
GetPositionFromCharIndex(matchPos)
, oùmatchPos
est la position d'un caractère / mot / motif vers lequel faire défiler. - Appelez en
SendMessage
utilisant la position calculée et en spécifiant de déplacer le pouce vers cette position en passantSB_THUMBPOSITION
dans le cadre de 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);
Déclaration des méthodes natives:
[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;
Vous pouvez utiliser la même méthodologie implémentée dans la méthode TextBoxBase.ScrollToCaret pour ce faire. Cette méthodologie est basée sur l'utilisation du modèle d'objet texte basé sur COM implémenté par le contrôle RichEdit sous-jacent.
Ce qui suit définit la méthode d'extension ScrollToCharPosition
. Il utilise des définitions abrégées des interfaces ITextDocument et 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);
}
}
Exemple d'utilisation :richtextbox1.ScrollToCharPosition(50)