여러 줄 텍스트 상자의 맨 아래로 자동 스크롤하는 방법


295

.Multiline 속성이 true로 설정된 텍스트 상자가 있습니다. 정기적으로 새로운 줄의 텍스트를 추가하고 있습니다. 새 줄이 추가 될 때마다 텍스트 상자가 가장 맨 아래 항목 (가장 최신 항목)으로 자동 스크롤되도록하고 싶습니다. 어떻게하면 되나요?


6
답을 찾기 위해 여기를 보았지만 찾을 수 없었습니다. 그래서 알아 냈을 때 미래의 사용자를 위해 여기에 올리거나 다른 누군가가 더 나은 접근 방식을 취할 것이라고 생각했습니다.
GWLlosa

2
VBA에서 동일한 작업을 수행해야했습니다.이 멋진 팬츠에는 새로운 .NET 메서드가 모두 없습니다. 향후 google-fu의 경우 다음과 같은 주문이 있습니다. TextBox1.Text = TextBox1.Text & "whatever"; TextBox1.SelStart = Len (TextBox1.Text); TextBox1.SetFocus; ... 그리고 .SetFocus는 이전에 포커스가 있었던 컨트롤로 돌아갑니다. TextBox1에 초점을 맞추지 않으면 내가 한 일에 상관없이 스크롤 막대가 업데이트되지 않습니다.
Gordon Broom

1
@GordonBroom Whelp 덕분에 이제 "코드 스 니펫" "개시"호출을 시작하겠습니다. 잘 했어. : D
시드니

답변:


425

정기적으로 새로운 줄의 텍스트를 추가하고 있습니다. 새 줄이 추가 될 때마다 텍스트 상자가 가장 맨 아래 항목 (가장 최신 항목)으로 자동 스크롤되도록하고 싶습니다.

를 사용 TextBox.AppendText(string text)하면 새로 추가 된 텍스트의 끝으로 자동 스크롤됩니다. 루프에서 호출하면 깜박이는 스크롤 막대를 피할 수 있습니다.

또한 .Text속성에 연결하는 것보다 훨씬 빠릅니다 . 그것은 당신이 그것을 얼마나 자주 부르는지에 달려 있습니다. 나는 단단한 루프로 테스트하고있었습니다.


텍스트 상자가 표시되기 전에 호출되거나 텍스트 상자가 표시되지 않는 경우 (예 : TabPanel의 다른 탭에서) 스크롤되지 않습니다. 자동 스크롤이 아닌 TextBox.AppendText ()를 참조하십시오 . 사용자가 텍스트 상자를 볼 수 없을 때 자동 스크롤이 필요한지 여부에 따라 중요하거나 중요하지 않을 수 있습니다.

이 경우 다른 답변의 대체 방법도 작동하지 않는 것 같습니다. 한 가지 방법은 VisibleChanged이벤트 에서 추가 스크롤을 수행하는 것입니다 .

textBox.VisibleChanged += (sender, e) =>
{
    if (textBox.Visible)
    {
        textBox.SelectionStart = textBox.TextLength;
        textBox.ScrollToCaret();
    }
};

내부적으로 AppendText다음과 같은 작업을 수행합니다.

textBox.Select(textBox.TextLength + 1, 0);
textBox.SelectedText = textToAppend;

그러나 수동으로 할 이유가 없습니다.

(직접 디 컴파일하면 더 효율적인 내부 메소드를 사용하고 사소한 특수한 경우가 있음을 알 수 있습니다.)


7
이 방법은 훨씬 빠르고 부드럽습니다. 스크롤 막대에는 '깜박임'이 없습니다 (빠른 연속으로 많은 전화를 걸 때 더 눈에)니다).
TallGuy

3
이것은 훨씬 더 나은 솔루션입니다.
Jeff

3
tb.Text += ....WndProc와
marshals

3
텍스트 영역도 초점을 맞출 필요가 있습니다. 처음으로 이것을 할 때 초점이 없기 때문에 스크롤되지 않았습니다.
Qwerty01

3
AppendText가 내 ReadOnly TextBox를 자동으로 스크롤하지 않았지만 TextBox.ScrollToEnd (); AppendText 호출 후 트릭을 수행했습니다.
Brandon Barkley

143

다음 코드 스 니펫을 사용할 수 있습니다.

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

자동으로 끝까지 스크롤됩니다.


5
답을 찾기 위해 여기를 보았지만 찾을 수 없었습니다. 그래서 알아 냈을 때 미래의 사용자를 위해 여기에 올리거나 다른 누군가가 더 나은 접근 방식을 취할 것이라고 생각했습니다.
GWLlosa

4
이것은 당시 최고의 답변 이었을지 모르지만 이제 Bob의 답변이 OP의 문제에 대한 더 나은 해결책이라고 생각합니다.
tomsv

38

.NET 4.0 에서 인터페이스가 변경된 것 같습니다 . 위의 모든 것을 달성 하는 다음과 같은 방법 이 있습니다. Tommy Engebretsen이 제안했듯이 TextChanged 이벤트 핸들러에 넣으면 자동으로 만들어집니다.

textBox1.ScrollToEnd();

21
이 메소드는 네임 스페이스 의 TextBoxBase클래스에 System.Windows.Controls.Primitives있습니다 ( PresentationFramework조립, WPF). 이 메서드는 존재하지 않으며 네임 스페이스 ( 어셈블리, WinForms) 에서 TextBox클래스를 상속 하는 WinForms에는 작동하지 않습니다 . TextBoxBaseSystem.Windows.FormsSystem.Windows.Forms
Bob

1
참고 ScrollToEnd()극단적으로 실적이 저조한 될 수 있습니다. 내 앱에서는 프로파일 링 시간의 50 % 이상을 차지했습니다.
ergohack

16

TextChanged 이벤트에 제안 된 코드를 추가하십시오.

private void textBox1_TextChanged(object sender, EventArgs e)
{
  textBox1.SelectionStart = textBox1.Text.Length;
  textBox1.ScrollToCaret();
}

10
textBox1.Focus()
textBox1.SelectionStart = textBox1.Text.Length;
textBox1.ScrollToCaret();

나를 위해 작동하지 않았습니다 (이유가 무엇이든 Windows 8.1).
그리고 여전히 .NET 2.0을 사용하고 있기 때문에 ScrollToEnd를 사용할 수 없습니다.

그러나 이것은 작동합니다.

public class Utils
{
    [System.Runtime.InteropServices.DllImport("user32.dll", CharSet = System.Runtime.InteropServices.CharSet.Auto)]
    private static extern int SendMessage(System.IntPtr hWnd, int wMsg, System.IntPtr wParam, System.IntPtr lParam);

    private const int WM_VSCROLL = 0x115;
    private const int SB_BOTTOM = 7;

    /// <summary>
    /// Scrolls the vertical scroll bar of a multi-line text box to the bottom.
    /// </summary>
    /// <param name="tb">The text box to scroll</param>
    public static void ScrollToBottom(System.Windows.Forms.TextBox tb)
    {
        if(System.Environment.OSVersion.Platform != System.PlatformID.Unix)
             SendMessage(tb.Handle, WM_VSCROLL, new System.IntPtr(SB_BOTTOM), System.IntPtr.Zero);
    }


}

VB.NET :

Public Class Utils
    <System.Runtime.InteropServices.DllImport("user32.dll", CharSet := System.Runtime.InteropServices.CharSet.Auto)> _
    Private Shared Function SendMessage(hWnd As System.IntPtr, wMsg As Integer, wParam As System.IntPtr, lParam As System.IntPtr) As Integer
    End Function

    Private Const WM_VSCROLL As Integer = &H115
    Private Const SB_BOTTOM As Integer = 7

    ''' <summary>
    ''' Scrolls the vertical scroll bar of a multi-line text box to the bottom.
    ''' </summary>
    ''' <param name="tb">The text box to scroll</param>
    Public Shared Sub ScrollToBottom(tb As System.Windows.Forms.TextBox)
        If System.Environment.OSVersion.Platform <> System.PlatformID.Unix Then
            SendMessage(tb.Handle, WM_VSCROLL, New System.IntPtr(SB_BOTTOM), System.IntPtr.Zero)
        End If
    End Sub


End Class

Windows 10에서 동일한 문제가 발생하면 해결 방법도 여기에서 잘 작동합니다.
Hannes

아무도 나를 위해 일하지 않았지만 이것. 신의 축복을 빕니다
Emirhan Özlen

9

새로 고침을 추가해야했습니다.

textBox1.SelectionStart = textBox1.Text.Length;
textBox1.ScrollToCaret();
textBox1.Refresh();

4

이 스레드에서 해결되지 않은 간단한 차이점을 발견했습니다.

ScrollToCarat()양식 Load()이벤트 의 일부로 모든 통화를 수행하면 작동하지 않습니다. 방금 ScrollToCarat()양식의 Activated()이벤트에 전화를 추가 했는데 정상적으로 작동합니다.

편집하다

양식의 Activated이벤트가 처음 실행될 때 (이후 활성화 시가 아님) 에만이 스크롤을 수행 하거나 양식이 활성화 마다 스크롤하는 것이 중요합니다 .

따라서 Activated()프로그램이로드 될 때 텍스트를 스크롤하기 위해 이벤트를 잡는 경우 이벤트 핸들러 자체의 이벤트를 구독 취소하면됩니다.

Activated -= new System.EventHandler(this.Form1_Activated);

양식이 활성화 될 때마다 수행해야 할 다른 작업이있는 경우 이벤트가 bool처음 Activated()발생할 때 a 를 true로 설정하여 후속 활성화를 스크롤하지는 않지만 필요한 다른 작업을 계속 수행 할 수 있습니다. 하다.

귀하의 경우에도 TextBox탭에이되지 않는 SelectedTab, ScrollToCarat()아무런 영향을주지 않습니다. 따라서 스크롤하는 동안 적어도 선택된 탭으로 만들어야합니다. 이 작업을 수행 할 때 양식이 깜박이면 코드를 a YourTab.SuspendLayout();및 랩으로 묶을 수 있습니다 YourTab.ResumeLayout(false);.

편집 종료

도움이 되었기를 바랍니다!


1

텍스트가 변경되면 텍스트 상자의 끝으로 스크롤되지만 여전히 사용자가 위로 스크롤 할 수 있습니다

outbox.SelectionStart = outbox.Text.Length;
outbox.ScrollToEnd();

Visual Studio Enterprise 2017에서 테스트


1

webforms 구현을 볼 것으로 예상되는 다른 사람은 Page Request Manager의 endRequest 이벤트 핸들러 ( https://stackoverflow.com/a/1388170/1830512 ) 를 사용하려고합니다 . 마스터 페이지의 콘텐츠 페이지에서 TextBox에 대해 수행 한 작업은 컨트롤에 변수를 사용하지 않았다는 사실을 무시하십시오.

var prm = Sys.WebForms.PageRequestManager.getInstance();

function EndRequestHandler() {
    if ($get('<%= ((TextBox)StatusWindow.FindControl("StatusTxtBox")).ClientID %>') != null) {
        $get('<%= ((TextBox)StatusWindow.FindControl("StatusTxtBox")).ClientID %>').scrollTop = 
        $get('<%= ((TextBox)StatusWindow.FindControl("StatusTxtBox")).ClientID %>').scrollHeight;
    }
}

prm.add_endRequest(EndRequestHandler);

0

이것은 단지 나를 위해 일했다 ...

txtSerialLogging-> 텍스트 = "";

txtSerialLogging-> AppendText (s);

위의 모든 경우를 시도했지만 문제는 내 경우 텍스트가 감소하고 증가하며 오랫동안 정적 상태를 유지할 수 있다는 것입니다. 정적은 정적 길이 (라인)를 의미하지만 내용이 다릅니다.

그래서 나는 길이 (선)가 몇 시간 동안 동일하게 유지되는 끝에 한 줄 점프 상황에 직면했습니다 ...


나는 Bob의 대답과 비슷하지만 특정 사례를 설명합니다. 그리고 Bob의 답변에 대해 언급 할 수 없습니다 ... stackoverflow rules에 갇혀 :(
TooGeeky

0

나는 이것을 위해 함수를 사용한다 :

private void Log (string s) {
    TB1.AppendText(Environment.NewLine + s);
    TB1.ScrollToCaret();
}
당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.