【问题标题】:Autoscroll to the bottom of a multiline textbox自动滚动到多行文本框的底部
【发布时间】:2014-12-22 13:54:10
【问题描述】:

我已经开始为一个涉及在较新引擎上重建游戏的小项目制作主服务器程序。 主服务器程序目前如下所示:

带有“Found 4 installed processor(s)”的大文本框是一个“控制台”,它输出使用主服务器发送到客户端/游戏服务器和从客户端/游戏服务器发送的原始事件消息。不能输入,管理员(唯一可以访问主服务器程序这个界面的人)只能从文本框中复制;他们不能删除/添加任何东西。

问题是因为它应该是一个“控制台”,它应该自动向下滚动到多行文本框的最后一行。

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 中也适用于我,因此我尝试了 some of the ones on 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

【问题讨论】:

  • 你是什么意思不起作用。你不应该在TextChanged 事件中调用AppendText,因为它会导致不间断循环。 AppendText 将触发 TextChanged 添加另一个 AppendText 进而触发 TextChanged 等等。这将需要永远。
  • @SriramSakthivel 这将解释为什么程序的标题被附加到每一行。我在哪里/如何正确地做到这一点?编辑添加当我使用该代码时文本框不会滚动。
  • 我无法重现该问题。如果你只是单独使用AppendText,那是有效的(我的意思是正确滚动)。发布一些简短但完整的示例来重现问题。
  • @SriramSakthivel 添加了代码,但是我仍然遇到程序标题在每行末尾附加几次的问题。我怎样才能克服这个障碍?

标签: vb.net winforms textbox visual-studio-2015


【解决方案1】:

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

【讨论】:

    【解决方案2】:

    一个更有用的解决方案:

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

    它只是将光标放在文本框中文本的末尾并滚动到当前光标位置。完美地为我工作!

    【讨论】:

      【解决方案3】:

      使用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
      

      【讨论】:

        【解决方案4】:

        你可以在加载事件中做到这一点,但它更复杂:

        <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
        

        textbox中设置文字后:

        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
        

        【讨论】:

          【解决方案5】:

          如果您使用的是AppendText,则可以完全摆脱console_TextChanged 方法,因为AppendText 已经为您完成了这项工作。

          由于某种原因(可能是错误?)当TextBox 没有暴露在屏幕上时,AppendText 似乎没有滚动到结束。我现在没有很好的解释,需要查看.Net框架源。

          作为一种解决方法,只需将所有代码移动到 MyBase.Shown 事件,而不是 Load 事件。这按预期工作,不同之处在于,一旦表单首次在屏幕上呈现,Shown 事件就会引发,而 Load 在呈现表单之前会被触发。

          【讨论】:

            猜你喜欢
            • 2010-10-28
            • 2017-02-07
            • 1970-01-01
            • 2013-11-12
            • 2012-03-01
            • 2011-09-30
            • 2014-12-20
            • 1970-01-01
            • 2012-02-18
            相关资源
            最近更新 更多