Timothy Waters
Timothy Waters

Reputation: 73

How do I automate removing lines from a multiline textbox while moving it to another multiline textbox in Visual Basic .NET

I have two multiline textboxes; txtInputList and txtComplete. I can manually move a line from txtInputList to txtComplete while removing the line from txtInputList as the desired result. However, I am having difficulty in automating this for every line. Here is the manual way clicking the button for each line:

Private Sub btnProcessItems_Click(sender As Object, e As EventArgs) Handles btnProcessItems.Click
    Dim strText() As String
    strText = Split(txtInputList.Text, vbCrLf)
    txtInputList.Text = String.Join(vbCrLf, strText, 1, strText.Length - 1)
    txtComplete.AppendText(strText(0) + vbCrLf)
End Sub

That works as desired. I've tried a For-loop, but I can't get it to work.

Private Sub btnProcessItems_Click(sender As Object, e As EventArgs) Handles btnProcessItems.Click
    Dim strText() As String
    strText = Split(txtInputList.Text, vbCrLf)
    For x As Integer = 0 to strText.Length
        txtInputList.Text = String.Join(vbCrLf, strText, 1, strText.Length - 1)
        txtComplete.AppendText(strText(0) + vbCrLf)
    Next
End Sub

Can someone please help? Thank you!

Upvotes: 0

Views: 67

Answers (3)

Idle_Mind
Idle_Mind

Reputation: 39122

Totally agree with djv's answer, but here's one way to do it directly with the UI:

Private Async Sub Button1_Click(sender As Object, e As EventArgs) Handles Button1.Click
    If TextBox1.Text.Trim.Length = 0 Then
        Exit Sub
    End If

    Button1.Enabled = False
    TextBox1.Enabled = False
    TextBox2.Enabled = False
    TextBox2.Clear()

    Dim lines As New List(Of String)(TextBox1.Lines)
    Dim processed As New List(Of String)
    While (lines.Count > 0)
        Dim current As String = lines(0)
        lines.RemoveAt(0)
        TextBox1.Lines = lines.ToArray


        ' ... do something with "current" ...
        Label1.Text = "Processing: " & current
        Await Task.Delay(2000)

        processed.Add(current)
        TextBox2.Lines = processed.ToArray
    End While
    Label1.Text = ""

    TextBox1.Enabled = True
    TextBox2.Enabled = True
    Button1.Enabled = True
End Sub

Here it is in action:

enter image description here

Upvotes: 0

djv
djv

Reputation: 15774

Don't keep your data on your UI. Use a data structure to keep your data so you can operate on it directly (off the UI thread) then update the UI with the state of your data. If your data is just a list of strings then use List(Of String) or if it is more than just strings, use a List(Of someClass).

My answer will fix a number of issues for you.

  1. Keep data in a list
  2. Process data off the UI thread
  3. Use Async/Await to ensure no UI blocking

First, a class to hold the data. You have the option for a nice name for display, and a Url or whatever other property you want to use in your processor

Public Class Data
    Public Property Name As String
    Public Property Url As String
    Public Sub New(name As String)
        Me.Name = name
    End Sub
    Public Overrides Function ToString() As String
        Return Name
    End Function
End Class

Make lists of input items and complete items, and I will initialize inputs with 10 items, with letters A ... J. Also define a method to put both lists into your TextBoxes

Private inputs As List(Of Data)
Private completes As List(Of Data)

Private Sub Form1_Load(sender As Object, e As EventArgs) Handles MyBase.Load
    ' initialize our fake data
    inputs = Enumerable.Range(65, 10).Select(Function(i) New Data(New String({Chr(i)}))).ToList()
    completes = New List(Of Data)()
    bindTextBoxes()
End Sub

Private Sub bindTextBoxes()
    txtInputList.Text = String.Join(Environment.NewLine, inputs)
    txtComplete.Text = String.Join(Environment.NewLine, completes)
End Sub

enter image description here

Add an Async button handler to process all items, and Task function to process each item. The button handler doesn't process anything, rather it's handled in the Awaited task. After each item is handled, you rebind the TextBoxes to update the UI.

Private Async Sub btnProcessItems_Click(sender As Object, e As EventArgs) Handles btnProcessItems.Click
    While inputs.Any()
        Await processFirstItem()
        bindTextBoxes()
    End While
End Sub

Private Function processFirstItem() As Task
    Return Task.Run(
        Sub()
            Dim item = inputs.First()
            inputs.RemoveAt(0)
            processItem(item)
            completes.Add(item)
        End Sub)
End Function

Private Sub processItem(item As Data)
    ' do whatever you need to do on the item, emulate a delay with thread sleep
    Threading.Thread.Sleep(250) ' remove this in production
End Sub

You may notice if you try this code that it runs off the UI so your app will run smoothly. You are just acting on a data structure instead of the UI. This is one good way to do what you are trying to do.

enter image description here

If your processing can be done in parallel, then you should look into Task Parallel Library, and your work will probably be performed much faster depending on the resources being used.

Upvotes: 1

dbasnett
dbasnett

Reputation: 11773

Try this, you'll need to change the names of the textboxes.

Private Sub Button1_Click(sender As Object, e As EventArgs) Handles Button1.Click
    Dim sb As New System.Text.StringBuilder
    For Each line As String In TextBox1.Lines
        sb.AppendLine(line)
    Next
    TextBox1.Clear()
    TextBox2.Text = sb.ToString
End Sub

Upvotes: 0

Related Questions