我有一个将 .Multiline 属性设置为 true 的文本框。我会定期向其中添加新的文本行。我希望文本框在添加新行时自动滚动到最底部的条目(最新的条目)。我该如何做到这一点?
我会定期向其中添加新的文本行。我希望文本框在添加新行时自动滚动到最底部的条目(最新的条目)。
如果您使用 TextBox.AppendText(string text)
,它将自动滚动到新附加文本的末尾。如果您在循环中调用它,它可以避免闪烁的滚动条。
它也恰好比连接到 .Text
属性快一个数量级。尽管这可能取决于您调用它的频率;我正在用一个紧密的循环进行测试。
如果在显示文本框之前调用它,或者如果文本框不可见(例如在 TabPanel 的不同选项卡中),则不会滚动。请参阅TextBox.AppendText() not autoscrolling。这可能很重要,也可能不重要,具体取决于您在用户看不到文本框时是否需要自动滚动。
在这种情况下,其他答案的替代方法似乎也不起作用。一种解决方法是对 VisibleChanged
事件执行额外的滚动:
textBox.VisibleChanged += (sender, e) =>
{
if (textBox.Visible)
{
textBox.SelectionStart = textBox.TextLength;
textBox.ScrollToCaret();
}
};
在内部,AppendText
执行以下操作:
textBox.Select(textBox.TextLength + 1, 0);
textBox.SelectedText = textToAppend;
但是应该没有理由手动进行。
(如果你自己反编译它,你会发现它使用了一些可能更有效的内部方法,并且似乎是一个小特例。)
您可以使用以下代码片段:
myTextBox.SelectionStart = myTextBox.Text.Length;
myTextBox.ScrollToCaret();
它将自动滚动到最后。
.NET 4.0 中的界面似乎发生了变化。以下 method 实现了上述所有目标。正如 Tommy Engebretsen 所建议的那样,将它放在 TextChanged 事件处理程序中使其自动化。
textBox1.ScrollToEnd();
System.Windows.Controls.Primitives
命名空间(PresentationFramework
程序集,WPF)的 TextBoxBase
类中。此方法不存在且在 WinForms 中不起作用,其 TextBox
类继承自 System.Windows.Forms
命名空间中的 TextBoxBase
(System.Windows.Forms
程序集,WinForms)。
ScrollToEnd()
的性能可能非常差。在我的应用程序中,它占分析时间的 50% 以上。
尝试将建议的代码添加到 TextChanged 事件:
private void textBox1_TextChanged(object sender, EventArgs e)
{
textBox1.SelectionStart = textBox1.Text.Length;
textBox1.ScrollToCaret();
}
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
我需要添加一个刷新:
textBox1.SelectionStart = textBox1.Text.Length;
textBox1.ScrollToCaret();
textBox1.Refresh();
我发现了这个线程中没有解决的一个简单的区别。
如果您将所有 ScrollToCarat()
调用作为表单的 Load()
事件的一部分进行,则它不起作用。我刚刚将我的 ScrollToCarat()
调用添加到表单的 Activated()
事件中,它工作正常。
编辑
仅在第一次触发表单的 Activated
事件时(而不是在后续激活时)执行此滚动非常重要,否则它会在您的表单被激活时滚动每次,您可能不会这样做想。
因此,如果您只是在程序加载时捕获 Activated()
事件以滚动文本,那么您可以在事件处理程序本身内取消订阅该事件,因此:
Activated -= new System.EventHandler(this.Form1_Activated);
如果您在每次激活表单时需要执行其他操作,则可以在第一次触发 Activated()
事件时将 bool
设置为 true,这样您就不会滚动后续激活,但仍然可以执行你需要做的其他事情。
此外,如果您的 TextBox
位于不是 SelectedTab
的选项卡上,则 ScrollToCarat()
将不起作用。因此,您至少需要在滚动时将其设为选定选项卡。如果您在执行此操作时表单闪烁,您可以将代码包装在 YourTab.SuspendLayout();
和 YourTab.ResumeLayout(false);
对中。
编辑结束
希望这可以帮助!
我用这个。简单、干净、快速!
txtTCPTxRx.AppendText(newText);
下面是我使用的实际代码
ThreadSafe(() =>
{
string newLog = $"{DateTime.Now:HH:mm:ss:ffff->}{dLog}{Environment.NewLine}";
txtTCPTxRx.AppendText(newLog);
});
关于皮特关于选项卡上的文本框的评论,我让它工作的方式是添加
textBox1.SelectionStart = textBox1.Text.Length;
textBox1.ScrollToCaret();
到选项卡的 Layout 事件。
当文本更改时,这将滚动到文本框的末尾,但仍允许用户向上滚动
outbox.SelectionStart = outbox.Text.Length;
outbox.ScrollToEnd();
在 Visual Studio Enterprise 2017 上测试
对于希望看到网络表单实施的其他任何人,您希望使用页面请求管理器的 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);
这只对我有用......
txtSerialLogging->Text = "";
txtSerialLogging->AppendText(s);
我尝试了上述所有情况,但问题是在我的情况下 text 可以减少,增加并且也可以长时间保持静止。静态意味着,静态长度(行),但内容不同。
所以,当长度(线)在一段时间内保持不变时,我最后面临着一种跳线的情况......
我为此使用了一个函数:
private void Log (string s) {
TB1.AppendText(Environment.NewLine + s);
TB1.ScrollToCaret();
}
不定期副业成功案例分享
tb.Text += ....
和 WndProc 和 marshals 做到这一点现在我觉得很愚蠢 :DtextBox.VisibleChanged
不起作用。但我将其更改为 txtResponse.TextChanged 并且它有效。