ฉันจะเลื่อนไปที่ด้านล่างของกล่องข้อความหลายบรรทัดโดยอัตโนมัติได้อย่างไร


295

ฉันมีกล่องข้อความที่มีคุณสมบัติ. Multiline ตั้งค่าเป็นจริง ในช่วงเวลาปกติฉันจะเพิ่มบรรทัดใหม่ให้กับข้อความ ฉันต้องการให้กล่องข้อความเลื่อนไปที่รายการล่างสุดโดยอัตโนมัติ (รายการที่ใหม่ที่สุด) ทุกครั้งที่มีการเพิ่มบรรทัดใหม่ ฉันจะทำสิ่งนี้ได้อย่างไร


6
มองหาคำตอบที่นี่ไม่พบมันดังนั้นเมื่อฉันคิดออกฉันคิดว่าฉันจะวางไว้ที่นี่สำหรับผู้ใช้ในอนาคตหรือถ้าคนอื่นอาจมีวิธีที่ดีกว่า
GWLlosa

2
ฉันต้องการทำสิ่งเดียวกันใน VBA ซึ่งไม่มีวิธีการ. NET แบบใหม่ทั้งหมด สำหรับ google-fu ในอนาคตนี่คือคาถา: TextBox1.Text = TextBox1.Text & "any" TextBox1.SelStart = Len (TextBox1.Text); TextBox1.SetFocus; ... จากนั้นให้ชุดโฟกัสกลับไปที่การควบคุมใด ๆ ที่เคยมีการโฟกัสมาก่อน โดยไม่ให้ความสำคัญกับ TextBox1 มันจะไม่มีวันอัพเดตแถบเลื่อนของมันไม่ว่าฉันจะทำอะไร
Gordon Broom

1
@GordonBroom Whelp ขอบคุณที่ฉันจะเริ่มเรียก "ตัวอย่างโค้ด" "incantations" ตอนนี้ การทำงานที่ดี. : D
Sidney

คำตอบ:


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 ตอนนี้ฉันรู้สึกโง่: D
Saeid Yazdani

3
textarea ยังต้องให้ความสำคัญเช่นกันครั้งแรกที่ฉันทำสิ่งนี้มันไม่ได้เลื่อนเพราะมันไม่มีจุดโฟกัส
Qwerty01

3
AppendText ไม่เลื่อนกล่องข้อความแบบอ่านอย่างเดียวของฉันโดยอัตโนมัติ แต่เพิ่ม 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.Primitivesnamespace ( PresentationFrameworkแอสเซมบลี WPF) วิธีนี้ไม่มีอยู่และจะไม่ทำงานใน WinForms ซึ่งTextBoxคลาสจะสืบทอดมาจากTextBoxBaseในSystem.Windows.Formsเนมสเปซ ( System.Windows.Formsชุดประกอบ, WinForms)
บ๊อบ

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()เหตุการณ์ของคุณถูกไล่ออกดังนั้นคุณจะไม่เลื่อนไปที่การเปิดใช้งานในภายหลัง แต่ยังสามารถทำสิ่งอื่น ๆ ทำ.

นอกจากนี้ถ้าคุณTextBoxอยู่บนแท็บที่ไม่ได้SelectedTab, ScrollToCarat()จะไม่มีผลกระทบ อย่างน้อยคุณต้องทำให้แท็บที่เลือกไว้ขณะเลื่อน คุณสามารถใส่รหัสในYourTab.SuspendLayout();และYourTab.ResumeLayout(false);จับคู่หากแบบฟอร์มของคุณกะพริบเมื่อคุณทำเช่นนี้

สิ้นสุดการแก้ไข

หวังว่านี่จะช่วยได้!


1

สิ่งนี้จะเลื่อนไปที่ส่วนท้ายของกล่องข้อความเมื่อมีการเปลี่ยนแปลงข้อความ แต่ยังอนุญาตให้ผู้ใช้เลื่อนขึ้น

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

ทดสอบใน Visual Studio Enterprise 2017


1

สำหรับผู้อื่นที่มาถึงที่นี่คาดว่าจะเห็นการใช้งานเว็บฟอร์มคุณต้องการใช้เครื่องมือจัดการเหตุการณ์ 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 หมายถึง static length (lines) แต่เนื้อหานั้นแตกต่างกัน

ดังนั้นฉันกำลังเผชิญหน้ากับสถานการณ์การกระโดดหนึ่งบรรทัดในตอนท้ายเมื่อความยาว (เส้น) ยังคงเหมือนเดิมในบางครั้ง ...


ฉันรู้ว่ามันคล้ายกับคำตอบของ Bob แต่อธิบายกรณีเฉพาะ และฉันไม่สามารถแสดงความคิดเห็นในคำตอบของ Bob ... ติดอยู่กับกฎ 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.