web-dev-qa-db-ja.com

複数行のテキストボックスの一番下までで自動的にスクロールする方法は?

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

261
GWLlosa

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

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;

しかしそれを手動で行う理由はないはずです。

(自分で逆コンパイルした場合、おそらくもっと効率的な内部メソッドが使用され、マイナーな特別なケースと思われるものがあります。)

373
Bob

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

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

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

140
GWLlosa

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

textBox1.ScrollToEnd();
39
JohnDRoach

提案されたコードをTextChangedイベントに追加してみてください。

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

私はリフレッシュを追加する必要がありました:

textBox1.SelectionStart = textBox1.Text.Length;
textBox1.ScrollToCaret();
textBox1.Refresh();
8
h4nd
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
8
Stefan Steiger

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

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

編集

フォームのActivatedイベントが初めて起動されたときにのみ(これ以降のアクティブ化ではなく)スクロールすることが重要です。そうしないと、フォームがアクティブ化されるたびにスクロールします。

したがって、プログラムのロード時にテキストをスクロールするためにActivated()イベントをトラップするだけの場合は、イベントハンドラ自体の内部でイベントの購読を中止するだけで済みます。

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

フォームがアクティブ化されるたびに他にやらなければならないことがある場合は、Activated()イベントが最初に発生したときにboolをtrueに設定することができます。する必要があります。

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

編集の終わり

お役に立てれば!

3
Pete

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

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

visual Studio Enterprise 2017でテスト済み

1
Eric Shreve

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);
1
G.P. Greenleaf

これは私のために働きました...

txtSerialLogging-> Text = "";

txtSerialLogging-> AppendText(s);

私は上記のすべてのケースを試しましたが、問題は私のケースではテキストが減少し、増加し、そしてまた長い間静的なままでいることです。静的な意味、静的な長さ(行)が内容が異なります。

それで、私は長さ(ライン)が何度か同じままであるときに最後に1ラインジャンプ状況に直面していました...

0
TooGeeky

これには関数を使います。

private void Log (string s) {
    TB1.AppendText(Environment.NewLine + s);
    TB1.ScrollToCaret();
}
0
DMike92