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!
CodePudding user response:
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.
- Keep data in a list
- Process data off the UI thread
- 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
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.
If your processing can be done in parallel, then you should look into