简体   繁体   中英

How can I reduce the delay in my output box in VB.NET?

The idea is that a new window pops-up where you can see what is happening. The problem is: there is a 20 second delay before anything shows up in the pop-up window. I can verify that because in the console output I can see directly what's happening. It starts at the beginning in the pop-up window and the pop-up disappears before all records are shown because the program is already 20 seconds ahead. Anyone knows how to show a pop-up window and directly show output?

If DgvObj.CheckRecords(100, 1) = 1 Then
    Dim dialog As New OutputBox("archive") With {
        .Width = 400,
        .Height = 200
        }
    dialog.Show()
    dialog.textBox.Text = "Waiting..." & Environment.NewLine
        
    Dim order_num As String
    For Each row As DataGridViewRow In DgvObj.DataGridName(1).Rows
        order_num = row.Cells("Order").Value
        dialog.textBox.AppendText("Archive record " & row.Index + 1 & " Order: " & order_num & Date.Now & Environment.NewLine)
        Console.WriteLine("Archive record " & row.Index + 1 & " Order: " & order_num & Date.Now)
        FormArchiveren(order_num, True)
    Next
    dialog.Close()
End If

When you do something in a windows app, like you click a button and the code inside the buttonclick handler starts calculating Pi to a trillion decimals places etc, you prevent the thread that handles all the UI updates from going back to its work of keeping the UI alive and responsive. This means the display in the app appears to hang while the work is carried out - it's a common problem and there are probably tens of thousands of questions on SO asking about it

The answer is always the same, and very simple; don't make the thread that processes your button click event take a long time to do anything. Less than 50 milliseconds is generally OK. Any operation that takes longer than 50ms, don't do it on the same thread the processes the event handler

There are many ways to solve this, and the simplest and nastiest is simply to regularly tell the UI thread, whose attention you've captured, to go and process all the UI updates that are pending; insert Application.DoEvents() after you set the textbox AppendText

Preferably, a nearly equally simple way is to use Task-based Async Pattern; make the work you do in FormArchiveren asyncronous*, make the method return a Task, make your click handler (or whatever event starts this process) async, and Await FormArchiveren(order_num, True)

Alternative ways that involve a bit more coding, and are falling out of favour these days, are things like using a BackgroundWorker or true multithreaded solution. They're less used because they're complex to get right (though backgroundworker is easier)

Be aware that if you do this (TAP, BackgroundWorker or separate thread executing FormArchiveren), you should not access UI controls from within FormArchiveren because the thread doing the work is not the thread that created the UI control, and that's a basic tenet of windows forms coding; thou shalt not use any thread other than the thread that created a control, to update anything about the control

* Making FormArchiveren async might be trivial, or could be more involved, but to offer any kind of sensible advice on it we'd need to see the definition of it (and anything else it calls on that is also part of your program. At its most trivial it would be switching to using existing Async versions of sync methods it already calls. If it does a lot of sync work itself forwhich there isn't an existing async version, then setting up tasks and running them yourself would be the "a bit more involved" bit

You can have the stats display update by using application.DoEvents

eg this:

If DgvObj.CheckRecords(100, 1) = 1 Then
Dim dialog As New OutputBox("archive") With {
    .Width = 400,
    .Height = 200
    }
dialog.Show()
dialog.textBox.Text = "Waiting..." & Environment.NewLine
Application.DoEvents()
 
Dim order_num As String
For Each row As DataGridViewRow In DgvObj.DataGridName(1).Rows
    order_num = row.Cells("Order").Value
    dialog.textBox.AppendText("Archive record " & row.Index + 1 & " Order: " & order_num & Date.Now & Environment.NewLine)
    
    Console.WriteLine("Archive record " & row.Index + 1 & " Order: " & order_num & Date.Now)
    Applicaton.DoEvents()
    FormArchiveren(order_num, True)
Next
dialog.Close()
End If

Note that EVEN if you accept the other recommended advice to move this out to another thread? You will need to use doevents or some form of refresh for that display to update while the loop and code runs. So, while a separate thread is a good idea, it will not solve nor fix the basic issue you have that during a code loop, you have to give a command for the display to update during that process.

I should also point out that doevents is HEAVY, and for a loop of more then say 100 rows? You want to put some code in to only fire that doevents every 100 rows, or every half second. So, for large processing, I suggest much caution with this approach.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM