17

I have a background worker control that is set to perform a task, and update a multiline text box on my main UI using a delegate procedure. this is all working perfectly, however once the updating scrolls off the bottom of the text box, the scroll bars appear, but the continuous refreshing causes the text box to stay locked at the top. Ideally, I would like the text box to auto-scroll itself to the bottom to show the latest entry in real-time. What would be the best way to implement this?

I have tried using the scrolltocaret() method, with and without a SelectionStart = txtlog.Text.Length command preceding it. perhaps I'm putting it in the wrong place?

some code samples below:

Delegate code:

Delegate Sub updateresults_delegate(ByVal textbox As TextBox, ByVal text As String)

Private Sub updatelog_threadsafe(ByVal textbox As TextBox, ByVal text As String)
            If textbox.InvokeRequired Then
                Dim mydelegate As New updateresults_delegate(AddressOf updatelog_threadsafe)
                Me.Invoke(mydelegate, New Object() {textbox, text})
                'Me.txtlog.SelectionStart = txtlog.Text.Length
                'Me.txtlog.ScrollToCaret()
            Else
                textbox.Text = text
            End If
        End Sub

main backgroundworker activity:

For i As Integer = val1 To val2
'generate an IP address from split host parts and current value of i
                host = s1(0) & "." & s1(1) & "." & s1(2) & "." & i
                Try 'attempt to ping the IP
                    Dim reply As PingReply = pingsender.Send(host, timeoutval, buffer, options)
                    If reply.Status = IPStatus.Success Then
                        name = System.Net.Dns.GetHostEntry(host)'get DNS entry
                        resulttext += String.Format("{1} - {2}: reply: Bytes={3} time{4} TTL={5}{0}", vbCrLf, name.HostName, reply.Address.ToString, reply.Buffer.Length, getms(reply.RoundtripTime), reply.Options.Ttl) 'print out success text
                    Else
                        resulttext += String.Format("      {1}: Ping failed. {2}{0}", vbCrLf, host, reply.Status.ToString) 'print out fail text
                    End If
                    updatelog_threadsafe(txtlog, resulttext) 'send text to textbox

            System.Threading.Thread.Sleep(1000)
        Catch ex As Exception

        End Try
    Next

I guess my main question is: I'm pretty certain that the textbox.scrolltocaret() is the correct method to use for what I want, but where is the best place for me to put it? I've tried it in the delegate, the main backgroundworker, as well as before & after the runworkerasync() method. none of these worked, and now I'm stumped!

almg
  • 311
  • 1
  • 3
  • 10

2 Answers2

26

Try it this way:

'textbox.Text = text
textbox.AppendText(text)

The code you commented out wasn't running on the GUI thread, and as M Granja pointed out, AppendText will automatically scroll to the appended text in the box, so no need to call ScrollToCaret.

LarsTech
  • 80,625
  • 14
  • 153
  • 225
  • Thanks for the response. I've added this into the IF statement in the delegate, and this now works a treat! Initially it was causing an interesting compound message (1 result, then 2 results, then 3 results, etc) per refresh. this turned out to be because of the += i had on the string being sent to the textbox. this is now fixed. Thankyou! – almg Nov 01 '13 at 16:08
  • 2
    Just a note, from [this answer](http://stackoverflow.com/a/14886915/613438) : If you use TextBox.AppendText(string text), it will automatically scroll to the end of the newly appended text. It avoids the flickering scrollbar if you're calling it in a loop. It also happens to be an order of magnitude faster than concatenating onto the .Text property. Though that might depend on how often you're calling it; – M Granja May 20 '15 at 18:07
0

xxx.SetFocus ' xxx = the name of the textbox

SendKeys "^{END}" ' pop to last line

peter
  • 1