VB.NET Windows Forms - Temporarily Disable Close 'X' Button - vb.net

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

Related

Run timer in background in visual basic form

first I'm new in this, and I have this code that shows a prompt to restart or postpone the restart for a while, the issue is that i want to hide the message and bring it back after the time specified by the user.
I'm using a "visual basic form" and the time that restart will be postponed it's selected from a "ComboBox"
My code is as follows.
Imports System.Management
Imports System.Security.Permissions
Imports System
Imports System.IO
Imports System.Collections
Imports System.SerializableAttribute
Public Class Form2
Dim PostponeReboot As Integer = 50
Private Const CP_NOCLOSE_BUTTON As Integer = &H200
Protected Overloads Overrides ReadOnly Property CreateParams() As CreateParams
Get
Dim myCp As CreateParams = MyBase.CreateParams
myCp.ClassStyle = myCp.ClassStyle Or CP_NOCLOSE_BUTTON
Return myCp
End Get
End Property
Private Sub Form2_Load(sender As Object, e As EventArgs) Handles MyBase.Load
Form1.Hide()
Label4.Text = SystemInformation.UserName
Button1.Enabled = False
ComboBox1.Enabled = False
Timer1.Interval = 1000
End Sub
Private Sub CheckBox1_CheckedChanged(sender As Object, e As EventArgs) Handles CheckBox1.CheckedChanged
If CheckBox1.Checked Then
CheckBox2.Enabled = False
Button1.Enabled = True
ComboBox1.Enabled = False
ElseIf CheckBox1.Checked = 0 Then
CheckBox2.Enabled = True
Button1.Enabled = False
ComboBox1.Enabled = False
End If
End Sub
Private Sub CheckBox2_CheckedChanged(sender As Object, e As EventArgs) Handles CheckBox2.CheckedChanged
If CheckBox2.Checked Then
CheckBox1.Enabled = False
ComboBox1.Enabled = True
Button1.Enabled = True
ElseIf CheckBox2.Checked = 0 Then
CheckBox1.Enabled = True
ComboBox1.Enabled = False
Button1.Enabled = False
End If
End Sub
Private Sub ComboBox1_SelectedIndexChanged(ByVal sender As System.Object, e As EventArgs) Handles ComboBox1.SelectedIndexChanged
If ComboBox1.Text = "1 Hora" Then
PostponeReboot = 10
ElseIf ComboBox1.Text = "2 Horas" Then
PostponeReboot = 20
ElseIf ComboBox1.Text = "4 Horas" Then
PostponeReboot = 40
ElseIf ComboBox1.Text = "Seleccione" Then
Button1.Enabled = False
End If
End Sub
Private Sub Button1_Click(sender As Object, e As EventArgs) Handles Button1.Click
If CheckBox1.Checked Then
MessageBox.Show("Rebooting")
'Shell("shutdown -r -f -t 60")
Form1.Close()
End
ElseIf CheckBox2.Checked Then
MessageBox.Show(PostponeReboot)
Timer1.Start()
Me.Hide()
End If
If PostponeReboot = 0 Then
Me.Show()
Else
Me.Hide()
End If
End Sub
Private Sub Timer1_Tick(sender As Object, e As EventArgs) Handles Timer1.Tick
PostponeReboot = PostponeReboot - 1
'Label5.Text = PostponeReboot
End Sub
End Class
In the first "If" sentence of below I want to start the timer and hide the form, and in the second "If" i want to bring it back the form, but the form remains hidden.
Private Sub Button1_Click(sender As Object, e As EventArgs) Handles Button1.Click
If CheckBox1.Checked Then
MessageBox.Show("Rebooting")
'Shell("shutdown -r -f -t 60")
Form1.Close()
End
ElseIf CheckBox2.Checked Then
MessageBox.Show(PostponeReboot)
Timer1.Start()
Me.Hide()
End If
If PostponeReboot = 0 Then
Me.Show()
Else
Me.Hide()
End If
End Sub
I've tried putting the second "If" sentence in another place but don't work, what I'm doing wrong.
I assume here that your Timer1 class raises the Timer1.Tick event every x time after Timer1.Start() is called. The fact that the form can hide tells me Timer1.Start() isn't a blocking method. As such, your second if statement will be verified right after you hide the form, without waiting for the PostponeReboot variable to reach zero. This particular button handler would then exit and your form would remain hidden. What I see is that you already have an event handler for each tick of your timer. Why not use this handler to verify the state of your PostponeReboot variable?
Private Sub Timer1_Tick(sender As Object, e As EventArgs) Handles Timer1.Tick
PostponeReboot = PostponeReboot - 1
If PostponeReboot = 0 Then
Timer1.Stop() 'I would assume
Me.Show()
End If
End Sub
Although, I would recommend you to try other solutions, like having your timer raise an event only when it reaches the elapsed time (so you don't have to handle each ticks unnecessarily). I would also recommend looking into an Universal Windows App with Toast Notifications as you could set a Notification to appear at a set time (handled by Windows) so that you don't have to have a thread running in the background for this.

Closing Excel in a Background worker

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.

vb.net background worker cancel not working

I'm having an issue where BackgroundWorker.CancelAsync() isn't working. I have WorkerSupportsCancellation set to TRUE. I am also polling BackgroundWorker1.CancellationPending in DoWork. Here is sample code of what I am trying to achieve. I have background worker looping through a time stamp and assigning a value to Measurement variable. I have a subroutine that queries the last reported Measurement variable and writes to listbox. After 5 loops I send BackgroundWorker.CancelAsync(). I can see the cancellation is pending, but it doesn't actually cancel the background worker. Is this a race condition?
Public Class Form1
Dim Measurement As String
Private Sub Button1_Click(sender As System.Object, e As System.EventArgs) Handles Button1.Click
BackgroundWorker1.RunWorkerAsync()
Delay(0.5)
For x = 1 To 5
ListBox1.Items.Add(Measurement)
ListBox1.TopIndex = ListBox1.Items.Count - 1
TextBox1.Text = BackgroundWorker1.CancellationPending
Delay(1)
Next
BackgroundWorker1.CancelAsync()
TextBox1.Text = BackgroundWorker1.CancellationPending
ListBox1.Items.Add("Cancel Pend: " & BackgroundWorker1.CancellationPending)
Delay(5)
ListBox1.Items.Add("Busy: " & BackgroundWorker1.IsBusy)
End Sub
Private Sub BackgroundWorker1_DoWork(sender As System.Object, e As System.ComponentModel.DoWorkEventArgs) Handles BackgroundWorker1.DoWork
If BackgroundWorker1.CancellationPending = True Then
e.Cancel = True
BackgroundWorker1.Dispose()
Exit Sub
Else
Do
Measurement = Now()
Loop
End If
End Sub
End Class
You just need to move the check for the cancellation inside the Do...Loop otherwise it will be tested only at the start of the DoWork event handler and never after that
Private Sub BackgroundWorker1_DoWork(sender As System.Object, e As System.ComponentModel.DoWorkEventArgs) Handles BackgroundWorker1.DoWork
If BackgroundWorker1.CancellationPending = True Then
e.Cancel = True
BackgroundWorker1.Dispose()
Else
Do
If BackgroundWorker1.CancellationPending = True Then
e.Cancel = True
BackgroundWorker1.Dispose()
Exit Do
Else
Measurement = Now()
End If
Loop
End if
End Sub

VB.net stopping a backgroundworker

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

How to break a loop with a single button click

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