多行文本框自动滚动到底部

7

我已经开始制作一个主服务器程序,用于重建一个游戏到一个更新的引擎上的小项目。 目前主服务器程序的代码如下:

enter image description here

大文本框显示“找到4个已安装的处理器”的内容是一个“控制台”,它输出发送到和从客户端/游戏服务器使用主服务器的原始事件消息。它不能输入,管理员(唯一能访问主服务器程序这个界面的人)只能从文本框中复制;他们不能删除/添加任何内容。
问题在于,由于它应该是一个“控制台”,所以应自动滚动到多行文本框的最后一行。
Stack Overflow上有很多涉及此问题的问题(例如this one),但当将代码放置在console_TextChanged子程序中时,我无法使其工作(文本框不会向下滚动)。
我尝试过这个:
Private Sub console_TextChanged(sender As Object, e As EventArgs) Handles console.TextChanged
    console.AppendText(Text)
    console.Select(console.TextLength, 0)
    console.ScrollToCaret()
End Sub

它不能正常工作,但会导致程序中的一个错误,每一行都会被附加上程序的标题很多次:
[net 11:32:22.243] System Started.Server Network | Crysis Wars 1.5Server Network | Crysis Wars 1.5Server Network | Crysis Wars 1.5Server Network | Crysis Wars 1.5Server Network | Crysis Wars 1.5Server Network | Crysis Wars 1.5Server Network | Crysis Wars 1.5Server Network | Crysis Wars 1.5Server Network | Crysis Wars 1.5Server Network | Crysis Wars 1.5

过去,一些C#解决方案在Visual Basic .Net中也适用于我,因此我尝试了Stack Overflow上的一些解决方案,但我也无法使它们正常工作。

这真的是自动滚动多行文本框的正确方法吗?如果是,为什么对我不起作用?

完整(相关)代码:

Private Sub Form1_Load(sender As Object, e As EventArgs) Handles MyBase.Load
    console.Text = GetNetTime() + "System Started."
    WriteToConsole("Working Area: " + CStr(My.Computer.Screen.WorkingArea().Width) + "*" + CStr(My.Computer.Screen.WorkingArea().Height))
    WriteToConsole("Found " + CStr(Environment.ProcessorCount) + " installed processor(s)")
    Dim i As Integer = 0
    While (i < Environment.ProcessorCount)
        WriteToConsole("Processor " + CStr(i) + ": " + My.Computer.Registry.LocalMachine.OpenSubKey("Hardware\Description\System\CentralProcessor\" + CStr(i)).GetValue("ProcessorNameString"))
        WriteToConsole("            Family: " + My.Computer.Registry.LocalMachine.OpenSubKey("Hardware\Description\System\CentralProcessor\" + CStr(i)).GetValue("Identifier"))
        WriteToConsole("            Manufacturer: " + My.Computer.Registry.LocalMachine.OpenSubKey("Hardware\Description\System\CentralProcessor\" + CStr(i)).GetValue("VendorIdentifier"))
        i += 1
    End While
    WriteToConsole("Starting networking services")
End Sub
Private Sub console_TextChanged(sender As Object, e As EventArgs) Handles console.TextChanged
    console.AppendText(Text)
    console.Select(console.TextLength, 0)
    console.ScrollToCaret()
End Sub
Function GetNetTime()
    Return "[net " + CStr(DateTime.UtcNow.Hour) + ":" + CStr(DateTime.UtcNow.Minute) + ":" + CStr(DateTime.UtcNow.Second) + "." + CStr(DateTime.UtcNow.Millisecond) + "] "
End Function
Function WriteToConsole(ByVal input As String)
    console.AppendText(Environment.NewLine & GetNetTime() + input)
    Return -1
End Function

1
什么意思不工作?你不应该在TextChanged事件中调用AppendText,因为这会导致无限循环。AppendText将触发TextChanged添加另一个AppendText,然后再次触发TextChanged,如此循环。这将永远持续下去。 - Sriram Sakthivel
那就解释了为什么程序的标题被附加到每行的末尾。在哪里/如何正确地实现它?编辑以添加文本框在使用该代码时不会滚动。 - AStopher
1
我无法重现这个问题。如果你只是单独使用 AppendText,那么它可以正常滚动。请提供一些简短但完整的示例来重现这个问题。 - Sriram Sakthivel
@SriramSakthivel 添加了代码,但是我仍然有一个问题,即程序标题会在每行末尾多次附加。如何克服这个障碍? - AStopher
5个回答

11

还有一个有用的解决方案:

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

它只是将光标放在文本框末尾并滚动到当前光标位置。对我完美地起作用了!


5
如果您正在使用AppendText,您可以完全摆脱console_TextChanged方法,因为AppendText已经为您做了这个。

由于某些原因(可能是一个错误?),当TextBox未暴露到屏幕时,AppendText似乎不会滚动到末尾。我现在没有很好的解释,需要查看.NET框架源代码。

作为一种解决方法,只需将所有代码移动到MyBase.Shown事件中,而不是Load事件。这样可以按预期工作,区别在于Shown事件将在表单首次呈现在屏幕上时触发,而Load则在表单呈现之前触发。


1

另一种解决方案:

TextBox1.Select(TextBox1.TextLength - 1, 1) 'select the last chr in the textbox
TextBox1.ScrollToCaret() 'scroll to the selected position

1
你可以在“load事件”中完成,但这更加复杂:
<DllImport("user32.dll", SetLastError:=True, CharSet:=CharSet.Auto)> _
Private Shared Function SendMessage(ByVal hWnd As IntPtr, ByVal Msg As UInteger, ByVal wParam As IntPtr, ByVal lParam As IntPtr) As IntPtr
End Function

在设置文本框中的文本后:

If (console.IsHandleCreated) Then
    'set focus
    SendMessage(console.Handle, &H7, IntPtr.Zero, IntPtr.Zero) 'WM_SETFOCUS
    'move caret to the end
    SendMessage(console.Handle, &HB1, CType(-1, IntPtr), CType(-1, IntPtr)) 'EM_SETSEL
    'scroll to the end
    SendMessage(console.Handle, &HB6, IntPtr.Zero, CType(console.Lines.Length, IntPtr)) 'EM_LINESCROLL
Else
    MsgBox("console window is not created")
End If

1
使用 RichTextBox 而不是 TextBox,您可以使用以下代码。
'SENDMESSAGE constants
'move to the last row in a RichTextBox
'you can obtain the same effect using ScrollToCaret but it works only if Focus is on RichTextBox
Private Const WM_VSCROLL As Int32 = &H115
Private Const SB_BOTTOM As Int32 = 7

Private Sub WriteLog(ByVal strLineLog As String)

    If Me.rtbLog.Text = "" Then
        Me.rtbLog.Text = strLineLog
    Else
        Me.rtbLog.AppendText(System.Environment.NewLine & strLineLog)
    End If

    SendMessage(Me.rtbLog.Handle, WM_VSCROLL, SB_BOTTOM, 0)

End Sub

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接