I have the following code that is supposed to run in a background worker when I hit the red X button to close the form:
Private Sub Form1_FormClosed(ByVal sender As System.Object, ByVal e As System.Windows.Forms.FormClosedEventArgs) Handles MyBase.FormClosed
picLoading.Visible = True
tblMain.Visible = False
bwQuitNoSave.RunWorkerAsync()
End Sub
Private Sub bwQuitNoSave_DoWork(sender As Object, e As System.ComponentModel.DoWorkEventArgs) Handles bwQuitNoSave.DoWork
'If at least one sheet has been opened, close the workbook
If FirstLoad = 2 Then
worksheet.Cells(1, 31).Value = ""
workbook.Save()
workbook.Close(False)
End If
End Sub
Private Sub bwQuitNoSave_RunWorkerCompleted(sender As Object, e As RunWorkerCompletedEventArgs) Handles bwQuitNoSave.RunWorkerCompleted
'Exit the application
APP.Quit()
End Sub
So what's supposed to happen is that the application checks to see if an integer = 2, and if it is, it saves the workbook, closes it, and then quits the application. However when I run this code, even when the application is closed, the excel process stays open.
In a previous iteration of my application, I was using this code which functioned correctly (e.g. closed out the excel process):
Private Sub Form1_FormClosed(ByVal sender As System.Object, ByVal e As System.Windows.Forms.FormClosedEventArgs) Handles MyBase.FormClosed
picLoading.Visible = True
tblMain.Visible = False
'If at least one sheet has been opened, close the workbook
If FirstLoad = 2 Then
worksheet.Cells(1, 31).Value = ""
workbook.Save()
workbook.Close(False)
End If
'Exit the application
APP.Quit()
End Sub
Does anyone see what I'm missing?
I've also tried this, and the process still remains open:
Private Sub bwQuitNoSave_DoWork(sender As Object, e As System.ComponentModel.DoWorkEventArgs) Handles bwQuitNoSave.DoWork
'If at least one sheet has been opened, close the workbook
If FirstLoad = 2 Then
worksheet.Cells(1, 31).Value = ""
workbook.Save()
workbook.Close(False)
End If
Try
workbook.Close(False)
APP.Quit()
Catch
APP.Quit()
End Try
End Sub
Private Sub bwQuitNoSave_RunWorkerCompleted(sender As Object, e As RunWorkerCompletedEventArgs) Handles bwQuitNoSave.RunWorkerCompleted
'Exit the application
releaseMemory(APP)
releaseMemory(worksheet)
releaseMemory(workbook)
End Sub
Private Sub releaseMemory(ByVal obj As Object)
Try
System.Runtime.InteropServices.Marshal.ReleaseComObject(obj)
obj = Nothing
Catch ex As Exception
obj = Nothing
Finally
GC.Collect()
End Try
End Sub
Solved it after many, many tries:
Private Sub Form1_FormClosing(sender As Object, e As FormClosingEventArgs) Handles MyBase.FormClosing
If e.CloseReason = CloseReason.UserClosing Then
e.Cancel = True
picLoading.Visible = True
tblMain.Visible = False
If FirstLoad = 2 Then
worksheet.Cells(1, 31).Value = ""
bwQuitNoSave.RunWorkerAsync()
Else
Quit()
End If
End If
End Sub
Public Sub Quit()
APP.Quit()
GC.Collect()
GC.WaitForPendingFinalizers()
GC.Collect()
GC.WaitForPendingFinalizers()
Application.Exit()
End Sub
Private Sub bwQuitNoSave_DoWork(sender As Object, e As System.ComponentModel.DoWorkEventArgs) Handles bwQuitNoSave.DoWork
'If at least one sheet has been opened, close the workbook
workbook.Save()
End Sub
Private Sub bwQuitNoSave_RunWorkerCompleted(sender As Object, e As RunWorkerCompletedEventArgs) Handles bwQuitNoSave.RunWorkerCompleted
Quit()
End Sub
Turns out the key was to have the exit via the red X canceled and manually close out the application.
Related
I am using the following code to get the size of files inside a directory
and put it in Label1:
For Each foundFile As String In My.Computer.FileSystem.GetFiles( _
"\windows",Microsoft.VisualBasic.FileIO.SearchOption.SearchTopLevelOnly,_
"*.*")
Dim filesizelabel As System.IO.FileInfo = My.Computer.FileSystem.GetFileInfo(foundFile)
Label1.Text = Label1.Text + filesizelabel.Length
Next
The problem is that i have more than 50 for each loops (a system cleaning app).
When I run the loops my app freezes until the loops finish, even if I run one loop.
Is there a solution to make it show the name of the current file? I tried this as well, but it also froze my application:
label2.text = foundfile
The application does not respond to any click, until it finishes the loops. It shows the size in Label1 and the last scanned file in Label2. This also freezes the application:
system.threading.thread.sleep(100)
Is there any alternative to foreach or a solution to fix this issue?
Here's a quick example using Async/Await with a Button Click() Handler:
Private Async Sub Button1_Click(sender As Object, e As EventArgs) Handles Button1.Click
Button1.Enabled = False
Await Task.Run(Sub()
' this runs in a different thread without blocking the GUI:
For Each foundFile As String In My.Computer.FileSystem.GetFiles(
"\windows", Microsoft.VisualBasic.FileIO.SearchOption.SearchTopLevelOnly, "*.*")
Dim filesizelabel As System.IO.FileInfo = My.Computer.FileSystem.GetFileInfo(foundFile)
' when you need to update the GUI:
Me.Invoke(Sub()
' ... do it in here ...
Label1.Text = Label1.Text + filesizelabel.Length
End Sub)
Next
End Sub)
Button1.Enabled = True
End Sub
For VB.Net 2010, try this instead:
Private Sub Button1_Click(sender As Object, e As EventArgs) Handles Button1.Click
Button1.Enabled = False
Dim T As New System.Threading.Thread(AddressOf Worker)
T.Start()
End Sub
Private Sub Worker()
' this runs in a different thread without blocking the GUI:
For Each foundFile As String In My.Computer.FileSystem.GetFiles(
"\windows", Microsoft.VisualBasic.FileIO.SearchOption.SearchTopLevelOnly, "*.*")
Dim filesizelabel As System.IO.FileInfo = My.Computer.FileSystem.GetFileInfo(foundFile)
' when you need to update the GUI:
Me.Invoke(Sub()
' ... do it in here ...
Label1.Text = Label1.Text + filesizelabel.Length
End Sub)
Next
Me.Invoke(Sub()
Button1.Enabled = True
End Sub)
End Sub
This is a prime candidate for a background worker.
Have a read about how they work, but at a high level the task is run in another thread with some events that you access in your main UI thread.
Private bw As BackgroundWorker = New BackgroundWorker
Private Sub buttonStart_Click(ByVal sender As System.Object, ByVal e As System.Windows.RoutedEventArgs)
If Not bw.IsBusy = True Then
' this will start the work
bw.RunWorkerAsync()
End If
End Sub
Private Sub buttonCancel_Click(ByVal sender As System.Object, ByVal e As System.Windows.RoutedEventArgs)
If bw.WorkerSupportsCancellation = True Then
' this will allow the user to cancel the work part way through
bw.CancelAsync()
End If
End Sub
Private Sub bw_DoWork(ByVal sender As Object, ByVal e As DoWorkEventArgs)
' your slow code goes here
End Sub
Private Sub bw_ProgressChanged(ByVal sender As Object, ByVal e As ProgressChangedEventArgs)
' you can update the UI here to show progress
End Sub
Private Sub bw_RunWorkerCompleted(ByVal sender As Object, ByVal e As RunWorkerCompletedEventArgs)
' your 'I've finished notification' code goes here
End Sub
Derek has put a bad code, it's not working and Idle mind code does not work on .NET 2.0
Dereks approach is working if code is complete, as below:
Private bw As BackgroundWorker = New BackgroundWorker
Private Sub app_Load(sender As Object, e As EventArgs) Handles MyBase.Load
AddHandler bw.DoWork, AddressOf bw_DoWork
AddHandler bw.ProgressChanged, AddressOf bw_ProgressChanged
AddHandler bw.RunWorkerCompleted, AddressOf bw_RunWorkerCompleted
..
End sub
Private Sub Btn_Click(sender As Object, e As EventArgs) Handles Btn.Click
If Not bw.IsBusy = True Then
' this will start the work
bw.RunWorkerAsync()
End If
End Sub
Private Sub bw_DoWork(ByVal sender As Object, ByVal e As DoWorkEventArgs)
'your work to not freeze form
end sub
Private Sub bw_ProgressChanged(ByVal sender As Object, ByVal e As ProgressChangedEventArgs)
' you can update the UI here to show progress
End Sub
Private Sub bw_RunWorkerCompleted(ByVal sender As Object, ByVal e As RunWorkerCompletedEventArgs)
' your 'I've finished notification' code goes here
End Sub
I have a form that prompts a user for confirmation before running a BackgroundWorker that performs some calculations. Those calculations can take anywhere from 10-30 seconds to run and I want to make sure that once the calculations begin running, they are allowed to finish uninterrupted.
Is there a way to temporarily disable the Close Button in the title bar until the BackgroundWorker finishes its job?
I found a couple similar questions but they look like a more permanent solution (here and here). I'd like the Close Button to be disabled only temporarily while the BackgroundWorker does its job.
Any help would be much appreciated. Thanks!
Private ImBusy As Boolean = False
Private Sub LookBusyForTheBoss()
Me.UseWaitCursor = True
Me.Cursor = Cursors.WaitCursor
Me.Enabled = False
ProgressBar1.UseWaitCursor = False
ProgressBar1.Style = ProgressBarStyle.Marquee
ImBusy = True
End Sub
Private Sub Form77_FormClosing(...) Handles Me.FormClosing
If ImBusy Then e.Cancel = True
End Sub
Private Sub OkHeIsGone()
Me.UseWaitCursor = False
Me.Cursor = Cursors.Default
Me.Enabled = True
ImBusy = False
End Sub
Disabling the close button would be bad taste in the extreme. Fix your application so that the calculation can be interrupted.
I agree with not hiding the 'X' but if you want it really bad I think this over here is what you are looking for:
Disable Close Button in Vb.Net
Something along these lines might work:
Private Sub BackgroundWorker1_DoWork(sender As Object, e As System.ComponentModel.DoWorkEventArgs) Handles BackgroundWorker1.DoWork
doNotClose = True
Do
'
'etc
'
'simulate long running
For x As Integer = 1 To 1000
Threading.Thread.Sleep(10)
Next
Exit Do
Loop
doNotClose = False
If closerequested Then
Me.BeginInvoke(Sub()
Me.Close()
End Sub)
End If
End Sub
Dim doNotClose As Boolean = False
Dim closerequested As Boolean = False
Private Sub Form1_FormClosing(sender As Object, e As FormClosingEventArgs) Handles Me.FormClosing
closerequested = True
If doNotClose Then
e.Cancel = True
Exit Sub
End If
End Sub
or this
Private Sub BackgroundWorker1_DoWork(sender As Object, e As System.ComponentModel.DoWorkEventArgs) Handles BackgroundWorker1.DoWork
doNotClose = True
Do
'
'etc
'
'simulate long running
For x As Integer = 1 To 1000
Threading.Thread.Sleep(10)
Next
Exit Do
Loop
doNotClose = False
End Sub
Private Sub BackgroundWorker1_RunWorkerCompleted(sender As Object, e As System.ComponentModel.RunWorkerCompletedEventArgs) Handles BackgroundWorker1.RunWorkerCompleted
If closerequested Then
Me.Close()
End If
End Sub
Dim doNotClose As Boolean = False
Dim closerequested As Boolean = False
Private Sub Form1_FormClosing(sender As Object, e As FormClosingEventArgs) Handles Me.FormClosing
closerequested = True
If doNotClose Then
e.Cancel = True
Exit Sub
End If
End Sub
My problem is that I don't know a suitable command to stop my code from running. If I use a return statement, like below, the code in the subroutine btnClose will keep running causing the program to close on an error. The form must not close if an error on saving occurs.
Private Sub Save_Customer()
If txtName.text = "" then
msgbox("Error")
return
End If
End sub
Private Sub btnClose_Click(sender As System.Object, e As System.EventArgs) Handles btnClose.Click
save_Customer()
Me.Close()
End Sub
Change the Sub to a Function, then evaluate the return:
Private Function Save_Customer() As Boolean
If txtName.text = "" then
msgbox("Error")
return False
Else
Return True
End If
End sub
Private Sub btnClose_Click(sender As System.Object,
e As System.EventArgs) Handles btnClose.Click
' evaluate the return:
If save_Customer() Then
Me.Close()
End IF
End Sub
You should better intercept Closing event, and Cancel closing via e.Cancel = True. Doing it otherwise would result in too much plumbing. (You can close the form not only via btnClose, right)? Also ALT+F4, Mouse click on X button etc. Be careful though, not to leave your user in a deadlock, where they cannot close your form, and have to fall back to using task manager.
That is probably not the best way around your problem, but you could solve it like this:
Private Function Save_Customer()
If txtName.Text = "" Then
MsgBox("Error")
Return False
Else
Return True
End If
End sub
Private Sub btnClose_Click(sender As System.Object, e As System.EventArgs) Handles btnClose.Click
If Save_Customer() = False Then
Exit Sub
Else
Me.Close()
End if
End Sub
End Class
I want to create a button that could stop my background worker and end all the process it is working on.
Here is my sample backgroundworker code:
Private Sub Button1_Click(sender As Object, e As EventArgs) Handles Button1.Click
Try
If BackgroundWorker1.IsBusy <> True Then
BackgroundWorker1.RunWorkerAsync()
End If
Catch ex As Exception
End Try
End Sub
Private Sub BackgroundWorker1_DoWork(sender As System.Object, e As System.ComponentModel.DoWorkEventArgs) Handles BackgroundWorker1.DoWork
Dim counter As Integer = 1
Do
'updated code with stop function----------------
BackgroundWorker1.WorkerSupportsCancellation = True
If BackgroundWorker1.CancellationPending Then
e.Cancel = True
ProgressBar1.Value = 0
Exit Do
End If
'updated code with stop function----------------
ListBox1.Items.Add(counter)
ProgressBar1.Value = ((counter - 1) / limit) * 100
counter = counter + 1
Loop While(counter <= 999999999999999999)
End Sub
Private Sub BackgroundWorker1_ProgressChanged(sender As System.Object, e As System.ComponentModel.ProgressChangedEventArgs) Handles BackgroundWorker1.ProgressChanged
Try
Catch ex As Exception
End Try
End Sub
Private Sub BackgroundWorker1_Completed(sender As System.Object, e As System.ComponentModel.RunWorkerCompletedEventArgs) Handles BackgroundWorker1.RunWorkerCompleted
Try
Catch ex As Exception
End Try
End Sub
Private Sub Form1_Load(sender As Object, e As EventArgs) Handles MyBase.Load
System.Windows.Forms.Control.CheckForIllegalCrossThreadCalls = False
End Sub
'updated code with stop function----------------
Private Sub StopButton_Click(sender As Object, e As EventArgs) Handles StopButton.Click
If BackgroundWorker1.IsBusy Then
If BackgroundWorker1.WorkerSupportsCancellation Then
BackgroundWorker1.CancelAsync()
End If
End If
End Sub
'updated code with stop function----------------
I want to reset the loop and return the Progress Bar to 0% when i stop the backgroundworker.
Is this possible?
The code above has been updated and it is now working fine.
I have added this code inside my do loop:
BackgroundWorker1.WorkerSupportsCancellation = True
If BackgroundWorker1.CancellationPending Then
e.Cancel = True
ProgressBar1.Value = 0
Exit Do
End If
I created a button that stops the worker:
Private Sub StopButton_Click(sender As Object, e As EventArgs) Handles StopButton.Click
If BackgroundWorker1.IsBusy Then
If BackgroundWorker1.WorkerSupportsCancellation Then
BackgroundWorker1.CancelAsync()
End If
End If
End Sub
The Backgroundworker class has the method CancelAsync() which you need to call to cancel the execution of the bgw.
You need to set the Backgroundworker.WorkerSupportsCancellation property to true and inside the while loop you need to check the CancellationPending property wether the value is true which indicates a call to the CancelAsync() method.
If CancellationPending evaluates to true, you would ( which you should have done already ) call one of the overloaded ReportProgress() (Docu) methods to set your ProgressBar value to the desired value.
EDIT: You should set the Cancel property of the DoWorkEventArgs to true so you can check the Cancelled property of the RunWorkerCompletedEventArgs inside the RunworkerCompletedevent.
You also shouldn not access any controls which lives in the UI thread. You better use the ProgressChanged(Docu) event.
See: BackgroundWorker Docu
Public Class Form1
Private iVal As Integer = 0
Private Sub bgw_DoWork(ByVal sender As System.Object, ByVal e As System.ComponentModel.DoWorkEventArgs) Handles bgw.DoWork
For iVal = iVal To 100 Step 1
bgw.ReportProgress(iVal)
Threading.Thread.Sleep(99)
If (bgw.CancellationPending = True) Then
e.Cancel = True
Exit For
End If
Next
End Sub
Private Sub bgw_ProgressChanged(ByVal sender As Object, ByVal e As System.ComponentModel.ProgressChangedEventArgs) Handles bgw.ProgressChanged
pbar.Value = e.ProgressPercentage
lblProgrss.Text = e.ProgressPercentage.ToString() & "%"
End Sub
Private Sub bgw_RunWorkerCompleted(ByVal sender As Object, ByVal e As System.ComponentModel.RunWorkerCompletedEventArgs) Handles bgw.RunWorkerCompleted
If (e.Cancelled = True) Then
pic.Visible = False
pbar.Value = iVal
lblProgrss.Text = iVal & "%"
btnstart.Text = "Start"
btnstart.BackColor = Color.Green
Else
pic.Visible = False
btnstart.Text = "Start"
btnstart.BackColor = Color.Green
iVal = 0
End If
End Sub
Private Sub btnstart_Click(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles btnstart.Click
If (btnstart.Text = "Start") Then
btnstart.Text = "Stop"
btnstart.BackColor = Color.Red
pic.Visible = True
bgw.RunWorkerAsync()
Else
If (bgw.IsBusy = True) Then
btnstart.Text = "Start"
btnstart.BackColor = Color.Green
bgw.CancelAsync()
End If
End If
End Sub
End Class
I am using VB.NET and I have:
a Start button
a Stop button
a While loop
When the Start button is pressed, the While loop begins. I want to stop the loop when the Stop button is pressed.
I had tried to use Applications.DoEvents, but the loop stops when the Stop button is pressed twice.
Below is my code using Applications.DoEvents
Dim stopclick As Boolean = False
Private Sub btnPlay_Click(ByVal sender As Object, ByVal e As System.EventArgs) Handles btnPlay.Click
btnForward.Enabled = False
btnStop.Enabled = True
While
' Perform the while statements
If stopclick = True Then
stopclick = False
Application.DoEvents()
Exit While
End If
End While
End Sub
Private Sub btnStop_Click(ByVal sender As Object, ByVal e As System.EventArgs) Handles btnStop.Click
stopClick = True
End Sub
Have I used Application.DoEvents incorrectly?
What are the other options besides Application.DoEvents? How can I stop the loop with a single Stop button click?
You need to put the call to Application.DoEvents outside of your If statement, like this:
While True
Application.DoEvents()
' Perform the while statements
If stopclick Then
stopclick = False
Exit While
End If
End While
The Stop_Click won't have a chance to be processed until you call DoEvents, so, with the way you had it, stopClick would never get set to True.
However, the larger issue, here, is that calling DoEvents, at all, is very bad practice and can lead to some very difficult-to-fix bugs if you aren't very careful. It would be far better if the loop was performed in a background thread. Check out the BackgroundWorker component for an easy to implement threading mechanism for WinForm projects. You'll find it in the tool box of your form designer.
Here's an example of how to do it with the BackgroundWorker component:
Dim stopclick As Boolean = False
Private Sub btnPlay_Click(ByVal sender As Object, ByVal e As System.EventArgs) Handles btnPlay.Click
btnForward.Enabled = False
btnStop.Enabled = True
BackgroundWorker1.RunWorkerAsync()
End Sub
Private Sub btnStop_Click(ByVal sender As Object, ByVal e As System.EventArgs) Handles btnStop.Click
stopClick = True
End Sub
Private Sub BackgroundWorker1_DoWork(sender As Object, e As DoWorkEventArgs) Handles BackgroundWorker1.DoWork
stopClick = False
While Not stopClick
' Perform the while statements
End While
End Sub
Private Sub BackgroundWorker1_RunWorkerCompleted(sender As Object, e As RunWorkerCompletedEventArgs) Handles BackgroundWorker1.RunWorkerCompleted
btnForward.Enabled = True
btnStop.Enabled = False
End Sub
You can simplify your while loop significantly by using stopClick as your condition:
While Not stopClick
Application.DoEvents()
End While
I know this is an old thread, but I found a simple workaround. Add a CheckBox to the form (it can even be "off the side of the form," if that made sense).
Private Sub btnStopLoop_Click(sender As System.Object, e As System.EventArgs) Handles btnStopLoop.Click
chkEndLoop.CheckState = CheckState.Checked
End Sub
Then, in the Loop you wish to exit:
Do Until chkEndLoop.CheckState = CheckState.Checked
'Do Stuff Here
Loop
As long as the CheckBox.CheckState = CheckState.Unchecked, the Loop will continue, but when the CheckBox becomes Checked, the Loop will Exit
You have to use btnstop.focus() at the start of the loop. This will be sure to solve your problem.
Example:
Dim stopclick As Boolean = False
Private Sub btnPlay_Click(ByVal sender As Object, ByVal e As System.EventArgs) Handles btnPlay.Click
btnForward.Enabled = False
btnStop.Enabled = True
btnStop.Focus() ' new code---------------------
While
' Perform the while statements
If stopclick = True Then
stopclick = False
Application.DoEvents()
Exit While
End If
End While
End Sub
Private Sub btnStop_Click(ByVal sender As Object, ByVal e As System.EventArgs) Handles btnStop.Click
stopClick = True
End Sub