Làm cách nào để tự động cuộn xuống dưới cùng của hộp văn bản nhiều dòng?


295

Tôi có một hộp văn bản với thuộc tính .Multiline được đặt thành true. Theo định kỳ, tôi đang thêm các dòng văn bản mới vào nó. Tôi muốn hộp văn bản tự động cuộn đến mục dưới cùng (mục mới nhất) bất cứ khi nào một dòng mới được thêm vào. Làm thế nào để tôi thực hiện điều này?


6
Tìm kiếm câu trả lời ở đây, không thể tìm thấy nó, vì vậy khi tôi tìm ra nó, tôi đã hình dung rằng tôi sẽ đưa nó lên đây cho người dùng trong tương lai, hoặc nếu có ai đó có cách tiếp cận tốt hơn.
GWLlosa

2
Tôi cần phải làm điều tương tự trong VBA, vốn không có tất cả các phương thức .NET mới lạ mắt này. Đối với google-fu trong tương lai, đây là câu thần chú: TextBox1.Text = TextBox1.Text & "anything"; TextBox1.SelStart = Len (TextBox1.Text); TextBox1.SetF Focus; ... và sau đó một. SetF Focus trở lại bất kỳ điều khiển nào có tiêu điểm trước đó. Nếu không tập trung vào TextBox1, nó sẽ không bao giờ cập nhật thanh cuộn của nó cho dù tôi có làm gì đi nữa.
Chổi Gordon

1
@GordonBroom Whelp, nhờ đó tôi sẽ bắt đầu gọi "đoạn mã" "bùa chú" ngay bây giờ. Làm tốt lắm. : D
Sidney

Câu trả lời:


425

Theo định kỳ, tôi đang thêm các dòng văn bản mới vào nó. Tôi muốn hộp văn bản tự động cuộn đến mục dưới cùng (mục mới nhất) bất cứ khi nào một dòng mới được thêm vào.

Nếu bạn sử dụng TextBox.AppendText(string text), nó sẽ tự động cuộn đến cuối văn bản vừa được thêm vào. Nó tránh thanh cuộn nhấp nháy nếu bạn gọi nó trong một vòng lặp.

Nó cũng xảy ra là một trật tự cường độ nhanh hơn so với ghép vào .Texttài sản. Mặc dù điều đó có thể phụ thuộc vào tần suất bạn gọi nó; Tôi đã thử nghiệm với một vòng lặp chặt chẽ.


Điều này sẽ không cuộn nếu nó được gọi trước khi hộp văn bản được hiển thị hoặc nếu hộp văn bản khác không hiển thị (ví dụ: trong một tab khác của TabPanel). Xem TextBox.AppendText () không tự động ghi . Điều này có thể hoặc không quan trọng, tùy thuộc vào việc bạn có yêu cầu tự động ghi khi người dùng không thể nhìn thấy hộp văn bản.

Có vẻ như phương pháp thay thế từ các câu trả lời khác cũng không hoạt động trong trường hợp này. Một cách khác là thực hiện cuộn bổ sung vào VisibleChangedsự kiện:

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

Trong nội bộ, AppendTextlàm một cái gì đó như thế này:

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

Nhưng không nên có lý do để làm điều đó bằng tay.

(Nếu bạn tự dịch ngược nó, bạn sẽ thấy rằng nó sử dụng một số phương thức nội bộ có thể hiệu quả hơn và có vẻ như là một trường hợp đặc biệt nhỏ.)


7
Phương pháp này là nhiều nhanh hơn và mượt mà hơn. Không có 'nhấp nháy' của thanh cuộn (điều này đáng chú ý hơn khi thực hiện nhiều cuộc gọi liên tiếp).
TallGuy

3
Đây là một giải pháp tốt hơn nhiều .
Jeff

3
Đang ăn chính mình đang cố gắng làm điều đó với tb.Text += ....WndProc và soái ca Bây giờ tôi cảm thấy thật ngu ngốc: D
Saeid Yazdani

3
Các textarea cũng cần phải được tập trung, lần đầu tiên tôi làm điều này nó không cuộn vì nó không có trọng tâm.
Qwerty01

3
AppendText không tự động cuộn TextBox ReadOnly của tôi mà thêm TextBox.ScrollToEnd (); sau cuộc gọi AppendText đã thực hiện thủ thuật.
Brandon Barkley

143

Bạn có thể sử dụng đoạn mã sau:

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

Nó sẽ tự động cuộn đến cuối.


5
Tìm kiếm câu trả lời ở đây, không thể tìm thấy nó, vì vậy khi tôi tìm ra nó, tôi đã hình dung rằng tôi sẽ đưa nó lên đây cho người dùng trong tương lai, hoặc nếu có ai đó có cách tiếp cận tốt hơn.
GWLlosa

4
Đây có thể là câu trả lời tốt nhất vào thời điểm đó, nhưng bây giờ tôi nghĩ câu trả lời của Bob là một giải pháp tốt hơn cho vấn đề của OP.
tomsv

38

Có vẻ như giao diện đã thay đổi trong .NET 4.0. Có phương pháp sau đây đạt được tất cả các điều trên. Như Tommy Engebretsen đã đề xuất, việc đưa nó vào một trình xử lý sự kiện TextChanged sẽ khiến nó tự động.

textBox1.ScrollToEnd();

21
Lưu ý rằng phương thức đó nằm trong TextBoxBaselớp trong System.Windows.Controls.Primitiveskhông gian tên ( PresentationFrameworkassembly, WPF). Phương pháp này không tồn tại và sẽ không làm việc trong WinForms, mà TextBoxkế thừa lớp từ TextBoxBasetrong System.Windows.Formskhông gian tên ( System.Windows.Formslắp ráp, WinForms).
Bob

1
Lưu ý rằng ScrollToEnd()có thể được thực hiện cực kỳ kém. Trong ứng dụng của tôi, nó chiếm hơn 50% thời gian định hình.
ergohack

16

Cố gắng thêm mã được đề xuất vào sự kiện 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();

không làm việc cho tôi (Windows 8.1, dù lý do là gì).
Và vì tôi vẫn dùng .NET 2.0, tôi không thể sử dụng ScrollToEnd.

Nhưng điều này hoạt động:

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

Có vấn đề tương tự với Windows 10, cách giải quyết của bạn cũng hoạt động tốt ở đây.
Hannes

Không ai làm việc cho tôi, nhưng điều này. Chúa phù hộ cho tâm hồn của bạn
Tiểu vương zlen

9

Tôi cần thêm một làm mới:

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

4

Tôi tìm thấy một sự khác biệt đơn giản chưa được giải quyết trong chủ đề này.

Nếu bạn đang thực hiện tất cả các ScrollToCarat()cuộc gọi như một phần của Load()sự kiện trong biểu mẫu của mình , thì nó không hoạt động. Tôi vừa thêm ScrollToCarat()cuộc gọi của mình vào Activated()sự kiện của mẫu đơn và nó hoạt động tốt.

Biên tập

Điều quan trọng là chỉ thực hiện cuộn này khi Activatedsự kiện của biểu mẫu lần đầu tiên được kích hoạt (không phải trong các lần kích hoạt tiếp theo) hoặc nó sẽ cuộn mỗi khi biểu mẫu của bạn được kích hoạt, đó là điều bạn có thể không muốn.

Vì vậy, nếu bạn chỉ bẫy Activated()sự kiện để cuộn văn bản khi chương trình của bạn tải, thì bạn chỉ có thể hủy đăng ký sự kiện bên trong trình xử lý sự kiện, do đó:

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

Nếu bạn có những việc khác bạn cần làm mỗi khi biểu mẫu của bạn được kích hoạt, bạn có thể đặt thành boolđúng khi lần đầu tiên Activated()sự kiện của bạn được kích hoạt, vì vậy bạn không cuộn vào các lần kích hoạt tiếp theo, nhưng vẫn có thể thực hiện những việc khác bạn cần làm

Ngoài ra, nếu bạn TextBoxở trên một tab không phải là SelectedTab, ScrollToCarat()sẽ không có hiệu lực. Vì vậy, bạn cần ít nhất làm cho nó trở thành tab đã chọn trong khi bạn cuộn. Bạn có thể gói mã trong một YourTab.SuspendLayout();YourTab.ResumeLayout(false);cặp nếu biểu mẫu của bạn nhấp nháy khi bạn làm điều này.

Kết thúc chỉnh sửa

Hi vọng điêu nay co ich!


1

Điều này sẽ cuộn đến cuối hộp văn bản khi văn bản được thay đổi, nhưng vẫn cho phép người dùng cuộn lên

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

đã thử nghiệm trên Visual Studio Enterprise 2017


1

Đối với bất kỳ ai khác hạ cánh ở đây mong muốn thấy triển khai biểu mẫu web, bạn muốn sử dụng trình xử lý sự kiện endRequest của Trình quản lý yêu cầu trang ( https://stackoverflow.com/a/1388170/1830512 ). Đây là những gì tôi đã làm cho TextBox của mình trong Trang nội dung từ Trang chính, vui lòng bỏ qua thực tế là tôi đã không sử dụng biến cho điều khiển:

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

Điều này chỉ làm việc cho tôi ...

txtSerialLogging-> Text = "";

txtSerialLogging-> AppendText (s);

Tôi đã thử tất cả các trường hợp trên, nhưng vấn đề là trong trường hợp văn bản của tôi có thể giảm, tăng và cũng có thể giữ tĩnh trong một thời gian dài. phương tiện tĩnh, chiều dài tĩnh (dòng) nhưng nội dung là khác nhau.

Vì vậy, tôi đã phải đối mặt với tình huống nhảy một dòng ở cuối khi độ dài (đường) vẫn giữ nguyên trong một số lần ...


Tôi biết, nó tương tự như câu trả lời của Bob, nhưng giải thích một trường hợp cụ thể. VÀ tôi không thể nhận xét về câu trả lời của Bob ... Bị mắc kẹt với các quy tắc stackoverflow :(
TooGeeky

0

Tôi sử dụng một chức năng cho việc này:

private void Log (string s) {
    TB1.AppendText(Environment.NewLine + s);
    TB1.ScrollToCaret();
}
Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.