複数行のテキストボックスの下部まで自動的にスクロールするにはどうすればよいですか?


295

.Multilineプロパティをtrueに設定したテキストボックスがあります。定期的に、新しい行のテキストを追加しています。新しい行が追加されるたびに、テキストボックスを一番下のエントリ(最新のエントリ)まで自動的にスクロールしたいと思います。どうすればこれを達成できますか?


6
答えをここで探したが見つからなかったので、それを見つけたとき、将来のユーザーのために、または誰か他の人がもっと良いアプローチをしているのなら、ここに置いておくと思った。
GWLlosa 2009年

2
私はVBAで同じことをする必要がありましたが、VBAでは、これらすべてのファンシーパンツの新しい.NETメソッドがありません。将来のgoogle-fuの場合、これがその呪文です。TextBox1.Text= TextBox1.Text& "whatever"; TextBox1.SelStart = Len(TextBox1.Text); TextBox1.SetFocus; ...次に、.SetFocusは、以前にフォーカスがあったコントロールに戻ります。TextBox1にフォーカスを移さなければ、私が何をしてもスクロールバーは更新されません。
Gordon Broom、2014年

1
@GordonBroom Whelp、そのおかげで、今から「コードスニペット」「呪文」の呼び出しを開始します。よくできました。:D
シドニー

回答:


425

定期的に、新しい行のテキストを追加しています。新しい行が追加されるたびに、テキストボックスを一番下のエントリ(最新のエントリ)まで自動的にスクロールしたいと思います。

を使用するTextBox.AppendText(string text)と、新しく追加されたテキストの最後まで自動的にスクロールします。ループで呼び出す場合、ちらつきのスクロールバーを回避します。

また、.Textプロパティに連結するよりも桁違いに高速です。それはあなたがそれを呼ぶ頻度に依存するかもしれませんが; 私はタイトなループでテストしていました。


これは、テキストボックスが表示される前に呼び出された場合、またはテキストボックスが表示されない場合(TabPanelの別のタブなど)はスクロールしません。自動スクロールしないTextBox.AppendText()を参照してください。これは、ユーザーがテキストボックスを表示できないときに自動スクロールが必要かどうかによって、重要な場合とそうでない場合があります。

この場合、他の回答からの代替方法も機能しないようです。それを回避する1つの方法は、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 2013

3
これははるかに優れたソリューションです。
ジェフ

3
自分がして、それをしようとして食べていたtb.Text += ....D:とのWndProcとマーシャルは今、私は愚かな感じ
Saeid Yazdani

3
textareaもフォーカスする必要があります。これを初めて行ったとき、フォーカスがないためスクロールしませんでした。
Qwerty01 2014年

3
AppendTextは自動的にReadOnly TextBoxをスクロールしませんでしたが、TextBox.ScrollToEnd();を追加しました。AppendText呼び出しがトリックを行った後。
Brandon Barkley 2017

143

次のコードスニペットを使用できます。

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

自動的に最後までスクロールします。


5
答えをここで探したが見つからなかったので、それを見つけたとき、将来のユーザーのために、または誰か他の人がもっと良いアプローチをしているのなら、ここに置いておくと思った。
GWLlosa 2009年

4
これが当時の最良の答えだったかもしれませんが、今はボブの答えがOPの問題に対するより良い解決策だと思います。
tomsv 2013

38

インターフェースは.NET 4.0で変更されたようです。上記のすべてを実現する以下の方法があります。Tommy Engebretsenが示唆したように、それをTextChangedイベントハンドラーに入れると、自動的に行われます。

textBox1.ScrollToEnd();

21
このメソッドは名前空間(アセンブリ、WPF)のTextBoxBaseクラスにあることに注意してください。このメソッドは存在せず、クラスが名前空間(アセンブリ、WinForms)から継承するWinFormsでは機能しません。System.Windows.Controls.PrimitivesPresentationFrameworkTextBoxTextBoxBaseSystem.Windows.FormsSystem.Windows.Forms
ボブ

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、

どれも私のために働いていませんでしたが、これは。神はあなたの魂を祝福します
エミルハンオズレン

9

更新を追加する必要がありました:

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

4

このスレッドでは対処されていない簡単な違いを見つけました。

ScrollToCarat()フォームのLoad()イベントの一部としてすべての呼び出しを行っている場合、機能しません。ScrollToCarat()フォームのActivated()イベントに呼び出しを追加したところ、問題なく動作しました。

編集する

このスクロールは、フォームのActivatedイベントが最初に発生したとき(以降のアクティベーションではなく)にのみ行うか、フォームがアクティブになるたびにスクロールすることが重要です。

したがってActivated()、プログラムが読み込まれたときにイベントをトラップしてテキストをスクロールするだけの場合は、イベントハンドラ自体の内部でイベントのサブスクライブを解除できます。

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

フォームがアクティブ化されるたびに実行する必要がある他のことがある場合はboolActivated()イベントが最初に発生したときにa をtrueに設定して、後続のアクティブ化をスクロールしないで、必要な他のことを実行できます。行う。

また、あなたの場合TextBoxではない、タブ上にあることはSelectedTabScrollToCarat()効果がありません。したがって、スクロールしている間は、少なくとも選択したタブにする必要があります。これを行うとフォームがちらつく場合は、コードをYourTab.SuspendLayout();YourTab.ResumeLayout(false);ペアでラップできます。

編集の終わり

お役に立てれば!


1

これは、テキストが変更されたときにテキストボックスの最後までスクロールしますが、ユーザーは上にスクロールできます

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

Visual Studio Enterprise 2017でテスト済み


1

Webフォームの実装を期待してここに着陸する他の人は、ページリクエストマネージャーのendRequestイベントハンドラー(https://stackoverflow.com/a/1388170/1830512)を使用します。マスターページのコンテンツページのテキストボックスに対して行った操作は次のとおりです。コントロールに変数を使用しなかったという事実は無視してください。

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-> Text = "";

txtSerialLogging-> AppendText(s);

上記のすべてのケースを試しましたが、問題は私の場合、テキストが減少したり、増加したり、長時間静止したままになる可能性があることです。staticは、静的な長さ(行)を意味しますが、内容が異なります。

それで、長さ(行)が同じままである最後の行ジャンプの状況に直面していました...


ボブの答えに似ていますが、特定のケースを説明しています。そして、ボブの答えにコメントすることはできません... stackoverflowルールでスタック:(
TooGeeky

0

私はこれに関数を使用します:

private void Log (string s) {
    TB1.AppendText(Environment.NewLine + s);
    TB1.ScrollToCaret();
}
弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.