0

I am using vb.net to code. This program goes through each pixel in a picturebox checking and compare the color that the user selected. I made a code to stop the looping when the user press ESC button on the keyboard. But it looks like the program does not stop when the the esc is pressed.

I made a messagebox to pop when the esc button is pressed. When the loop is running and I press the esc button, the messagebox pops up only when the whole loop is done.

For x = rect.X To endPointX - 1 Step pixelStep
    For y = endpointY - 1 To rect.Y Step -1

        If e.X >= 0 And e.Y >= 0 And (e.X < PictureBox1.Width) And (e.Y < PictureBox1.Height) Then
            If escPress  Then
                Exit For
            End If

            If bmp.GetPixel(x, y) = cor Then
                cruzNoBitmap(PictureBox1, n, pemSize, x, y)

                addRow(x, y)
                Exit For
            End If
        End If
    Next

    If escPress Then
        Exit For
    End If
Next
Private Sub frmBitmaps_KeyDown(sender As Object, e As System.Windows.Forms.KeyEventArgs) Handles Me.KeyDown
        If  e.KeyCode = Keys.Escape Then
            MsgBox("test")
            escPress = True
        End If
End Sub

Any help I would be thankful.

theduck
  • 2,589
  • 13
  • 17
  • 23
Sam1996
  • 35
  • 5
  • Possible duplicate of [Breaking/exit nested for in vb.net](https://stackoverflow.com/questions/5312291/breaking-exit-nested-for-in-vb-net) – Andrew Morton Sep 30 '19 at 18:48
  • 4
    The message box is shown afterwards because that's when the `KeyDown` event has a chance to fire. Your two loops occupy the entire UI thread, meaning no other code can run until they're done. You need to move your loops into a separate thread so the UI can remain responsive. Look into _multithreading_ or the _Task Parallel Library_. – Visual Vincent Sep 30 '19 at 19:00
  • @VisualVincent What if I want to use a thread and call a sub that has parameters? it was like that before..to call the sub `checkColor(e)` with the thread: ` thread2 = New System.Threading.Thread(AddressOf checkColor) thread2.Start() ` – Sam1996 Sep 30 '19 at 20:07
  • 2
    @Sam1996 well you can encapsulate the method call in an anonymous method: `thread2 = New System.Threading.Thread(Sub() checkColor(e))` – djv Sep 30 '19 at 21:05
  • 2
    Why do you check `If e.X >= 0 And e.Y >= 0 And (e.X < PictureBox1.Width) And (e.Y < PictureBox1.Height) Then` every iteration? It seems that the value would be the same always (unless it's changing elsewhere during the loop but I doubt it). If that is false then neither loop should run. Check it once at the beginning of `checkColor`, or better yet, before you start the thread. – djv Sep 30 '19 at 21:20
  • djv attention to detail is immaculate – Ess Kay Oct 01 '19 at 12:17

2 Answers2

2

Here are a couple options. Both have the processing code run off the UI thread. The first uses a Thread as you suggested, but with the syntax to get you going. Code execution continues after Thread.Start is called. The second uses Async/Await which doesn't create an additional thread but still takes the processing off the UI. This is a more modern approach. Both should work for you. Instead of using your exact code, this just counts to 100 over 10 seconds.

Private escPress As Boolean = False

Private Sub RunThreadButton_Click(sender As Object, ea As EventArgs) Handles runThreadButton.Click
    Dim p As New Point()
    Dim thread2 = New System.Threading.Thread(Sub() checkColor(p))
    thread2.Start()
End Sub

Private Async Sub AsyncButton_Click(sender As Object, e As EventArgs) Handles asyncButton.Click
    Dim p As New Point()
    Await Task.Factory.StartNew(Sub() checkColor(p))
End Sub

Private Sub EscapeButton_Click(sender As Object, e As EventArgs) Handles escapeButton.Click
    escPress = True
End Sub

Private Sub checkColor(e As Point)
    Try
        For x = 0 To 9
            If escPress Then Exit For
            For y = 0 To 9
                If escPress Then Exit For
                Threading.Thread.Sleep(100)
                Console.WriteLine(y + 10 * x + 1)
            Next
        Next
    Finally
        escPress = False
    End Try
End Sub

It seems like the condition

If e.X >= 0 And e.Y >= 0 And (e.X < PictureBox1.Width) And (e.Y < PictureBox1.Height) Then

is needless since that will never change unless the PictureBox width is changing mid-loop. This could be checked once prior to the loop, or could be checked prior to calling checkColor, which is where it should be done since you're interacting with UI, and why make a pointless call for nothing?

djv
  • 15,168
  • 7
  • 48
  • 72
1

Since you have a single thread, the button function will not fire until the loops have ended.

You can confirm this by making x and y global integers, and displaying them in the messagebox.

If you want to be able to exit, you will need to create a second thread, and run the loops on that thread.

An example for multithreading can be found here: http://howtostartprogramming.com/vb-net/vb-net-tutorial-53-multithreading/

Ess Kay
  • 598
  • 4
  • 20
  • What if I want to use a thread and call a sub that has parameters? – Sam1996 Sep 30 '19 at 19:56
  • it was like that before..to call the sub `checkColor(e)` with the thread: ` thread2 = New System.Threading.Thread(AddressOf checkColor) thread2.Start() ` – Sam1996 Sep 30 '19 at 19:58
  • you can call threads with params 2 ways, but I would start a new question for that. short version. 1. create object with internal variables (use as params), then set them before you do thread.start(). 2: Pass parameters directly ' SendMailERRORNotification(DISPLAYMSG) *DIM trd = New Thread(Sub() Me.MYTHREADEDSUB(param1 )) trd.internalparam2 = 45 trd.internalparam3 = True trd.Start()* – Ess Kay Oct 01 '19 at 12:20