9

I am working with a RichTextBox in C#. It exists on a TabPage. When the TabPage is selected, I aim to populate the RichTextBox, and scroll to the end. I have tried the slight variations on solutions for this common question, the main one being along the lines of:

MyRichTextBox.Select(MyRichTextBox.Text.Length, 0);  
MyRichTextBox.ScrollToCaret();  

or:

MyRichTextBox.SelectionStart = MyRichTextBox.Text.Length;  
MyRichTextBox.ScrollToCaret();  

This is producing inconsistent results, albeit in a predictable manner. It will alternate between scrolling to the bottom, and scrolling one line short of the bottom. Respectively illustrated (sorry for the links, new user so I can't post the images):
Successfully scrolled to bottom
Scrolled to one line short of the bottom
I am surprised to find nothing mentioning this behaviour through my searches, and have decided to ask if anyone here has encountered this, and/or has a solution in mind. If it comes down to it, I suppose I can go with something along the lines of itsmatt's answer.

Community
  • 1
  • 1
  • sounds almost lik an indexing issue but can't really be certain without seein the code that you have for the RTB – MethodMan Dec 16 '11 at 13:52
  • The code I have posted here is essentially all that matters. I populate the RichTextBox with some text, and then try to scroll to the bottom using that code. There's no further interaction with the control. – art.vandelay.31415 Dec 16 '11 at 14:11
  • ok let me ask you this real quick.. what line are you wanting to scroll to i.e what word.. find the line by using IndexOf I will post an example below. – MethodMan Dec 16 '11 at 14:15

4 Answers4

30

I did some further experimentation with ScrollToCaret and it just does not end up in the same position every time. Since my goal is limited to only scrolling all the way to the bottom, it was then a good candidate for sending the WM_VSCROLL message (277, or 0x115) to the control, with wParam of SB_PAGEBOTTOM (7). This consistently scrolls all the way to the very bottom exactly like I needed:

[DllImport("user32.dll", CharSet = CharSet.Auto)]
private static extern int SendMessage(IntPtr hWnd, int wMsg, IntPtr wParam, IntPtr lParam);
private const int WM_VSCROLL = 277;
private const int SB_PAGEBOTTOM = 7;

public static void ScrollToBottom(RichTextBox MyRichTextBox)
{
    SendMessage(MyRichTextBox.Handle, WM_VSCROLL, (IntPtr)SB_PAGEBOTTOM, IntPtr.Zero);
}
  • If you put the conversion from pagebottom as "new System.IntPtr(SB_BOTTOM)", then the automatic translation to VB.NET will work, too. – Stefan Steiger Apr 15 '15 at 09:06
1

Change this to fit your working code..

String gotoCaret = "Something on this line.";
int position = textBox.Text.IndexOf(gotoCaret);
MyRichTextBox.SelectionStart = position;
MyRichTextBox.ScrollToCaret();
MethodMan
  • 18,625
  • 6
  • 34
  • 52
  • Hmmm, it's possible this would work for someone. I'm not sure that it would make a difference on the inconsistency I noted, since the issue seems to be with the ScrollToCaret. What I was saying in my question is that I can tell it `MyRichTextBox.ScrollToCaret();` under the same circumstances each time, and achieve different outcomes. Anyhow, the nature of my RichTextBox content makes searching for a particular string unreliable, as it contains text which may repeat. – art.vandelay.31415 Dec 16 '11 at 14:28
  • ooh.. that's not good if the text repeats however you could create some for each code that would loop thru and count for how many times the word your looking for exist.. if it exist once.. then MyRichTextBox.ScrollToCaret else you would have to go to that index position of where the last instance of the repeated word or character was found.. does this make sense.. – MethodMan Dec 16 '11 at 14:31
  • Yeah, in fact if I wanted to try that route, I could just go for LastIndexOf and that would solve the repeating problem of course. :) Or I could also consider prepending the text. Then I don't need to scroll at all! haha – art.vandelay.31415 Dec 16 '11 at 15:09
  • hope that helps in regards to having another approach.. good luck glad I could potentially help – MethodMan Dec 16 '11 at 15:15
0

I have the same problem, I guess a RTB is almost quite managed by Windows Messages so it sounds a bit like a rabbit warren. I don t know, therefore, the reason for the alternating output (but it has slightly a bug taste). I am concerned with this RTB.Scrolltocaret flickering output but in a VB program. Compliments for your drastic solution: It works perfectly.

Should anyone encounter this anomaly in that programming environment, here's the VB code

Imports System.Runtime.InteropServices
Public Class Form
<DllImport("user32.dll",CharSet:=CharSet.Auto)> _
Public Shared Function SendMessage( _
ByVal hWnd As IntPtr, _
ByVal wMsg As Integer, _
ByVal wParam As IntPtr, _
ByVal lParam As IntPtr) As Integer
End Function
Const WM_SCROLL = 277
Const SB_PAGEBOTTOM = 7
Sub ScrollToBottom(ByVal RTBName As RichTextBox)
   SendMessage(RTBName.Handle, _
               WM_SCROLL, _
               SB_PAGEBOTTOM, _
               IntPtr.Zero)
End Sub 'then call ScrollToBottom instead of ScrollToCaret
Bento
  • 223
  • 1
  • 12
  • Sorry but I m writing by a phone so in the economical version of the page I didn t see the code button (but I noticed 4 space bars are equivalent) – Bento Dec 27 '14 at 15:30
0

I have encountered the same bug (now 7+ years old), of ScrollToCaret() jumping alternatively between the last line and almost the last line. Another solution which avoids using unmanaged code is to call ScrollToCaret() twice.

RichBox.Select(TheLocationYouWantToScrollTo, 0);
RichBox.ScrollToCaret();
RichBox.ScrollToCaret();

This approach can sometimes produce a little screen flicker (not bad, but not super smooth) because it is scrolling to one line and then the other. You might try to solve the slight flicker this way, but it won't work:

RichBox.SuspendLayout(); // I won't actually suspend this layout
RichBox.Select(TheLocationYouWantToScrollTo, 0);
RichBox.ScrollToCaret();
RichBox.ScrollToCaret();
RichBox.ResumeLayout();

You can also reduce flicker by making sure the new Location is on a new line:

RichBox.Select(TheLocationYouWantToScrollTo, 0)
if (RichBox.Transcription.GetFirstCharIndexOfCurrentLine() != ThePriorCharIndexOfCurrentLine)
{
   RichBox.ScrollToCaret();
   RichBox.ScrollToCaret(); 
}

This reduces flicker by only scrolling when we are at a new line.

Dan
  • 99
  • 5