Reopening a Disposed form declared with events. VB.net - vb.net

I have a dialog form which is a bar code scanner handler form that has events on the form it was called from, done like this:
Public Class FRMCheckout
Dim WithEvents Batch_Scanner_Dialog As New CheckoutBatchScanner
Private Sub Recieve_Scaned_Object(Scan_Object As tructures.ScanDetails) Handles Batch_Scanner_Dialog.Scanned_Item
'.....Do Stuff'
End Sub
Private Sub Button1_Click_1(sender As Object, e As EventArgs) Handles Button1.Click
Batch_Scanner_Dialog.Show()
End Sub
End Class
The Batch_Scanner_Dialog is closed with just the regular old Close.Me which opbviously disposes itself.
The problem being if you wish to open the dialog again, an accessing a disposed object exception is thrown.
Locally Declaring the Dialog will not work, because it has events, so how could I fix this issue? Calling a new instance of the dialog is fine, just the original Events should be on the calling form. (They vary depending on the form the dialog is called form)
(Note: I need to use .show not .showdialog to continue to run code on the original form.)

OK the problem was solved with:
Private Sub Button1_Click_1(sender As Object, e As EventArgs) Handles Button1.Click
If Batch_Scanner_Dialog.IsDisposed Then
Dim Batch_Scanner_Dialog As New CheckoutBatchScanner
AddHandler Batch_Scanner_Dialog.Scanned_Item, AddressOf Recieve_Scaned_Object
Batch_Scanner_Dialog.Show()
Else
Batch_Scanner_Dialog.Show()
End If
End Sub

Related

Cross-thread operation not valid: problem

I am working in Visual Studio 2019 and writing in Visual Basic. I get this error: Cross-thread operation not valid: Control 'lblTesting' accessed from a thread other than the thread it was created on.' (lblTesting is just a label for testing purposes).
I call the FrmContacts like this:
Private Sub ContactsToolStripMenuItem_Click(sender As Object, e As EventArgs) Handles ContactsToolStripMenuItem.Click
Me.Enabled = False
FrmContacts.Show()
End Sub
and return from the form like this:
Private Sub FrmContacts_Closed(sender As Object, e As EventArgs) Handles Me.Closed
Me.Close()
FrmMain.Enabled = True
End Sub
The error pops up when I exit FrmContacts.
I tried deleting the form and recreating it but the error is there as soon as I add any control. I am dumbfounded. Searching has not revealed any hints.
I had to take out the Me.Close() because I was closing a closed form. The new code works fine. It is:
Private Sub FrmContacts_Closed(sender As Object, e As EventArgs) Handles Me.Closed
FrmMain.Enabled = True
End Sub
Also, if your intention is to make the "main" form inaccessible until the "child" form is closed, then simply use ShowDialog() instead:
You can use this method to display a modal dialog box in your
application. When this method is called, the code following it is not
executed until after the dialog box is closed.
Private Sub ContactsToolStripMenuItem_Click(sender As Object, e As EventArgs) Handles ContactsToolStripMenuItem.Click
FrmContacts.ShowDialog()
End Sub

vb.net / C# External handler

I have a form used to display options about processes.
When options are applyed :
frmOptions
For Each ltvi As ListViewItem In ltvProcesses.CheckedItems
Dim proc As Process = CType(ltvi.Tag, Process)
targeted_processes.Add(proc)
AddHandler proc.Exited, AddressOf frmAET.a_target_process_has_been_exited
proc.EnableRaisingEvents = True
Next
And in a tools module :
Public Sub a_target_process_has_been_exited(sender As Object, e As EventArgs)
frmAET.btnStatus.ForeColor = Color.Red
msgbox("OK")
End Sub
And... the messagebox displays its message but the color doesn't change.
After some tries, the problem is when a_target_process_has_been_exited is actived by the handler.
If I do this (Button1 belongs to frmAET, like btnStatus) :
Private Sub Button1_Click(sender As Object, e As EventArgs) Handles Button1.Click
a_target_process_has_been_exited()
End Sub
It works ! But not when I really want (when a process is ended).
So, the problem is when the sub is called by the process end event.
And when I try to specify this (maybe a frmAET's sub can modify its controls) :
AddHandler leproc.Exited, AddressOf frmAET.a_target_process_has_been_exited
Error : Reference to a non-shared member requires an objet reference
Could you help me ?
Your AddHandler seems to use AddressOf frmAET.a_target_process_has_been_exited, that means method in frmAET form itself. Not tools module as you stated.
Let's consider your frmOptions is correct and frmAET is containing this (with removed explicit reference to frmAET, since it's local)
Public Sub a_target_process_has_been_exited(sender As Object, e As EventArgs)
btnStatus.ForeColor = Color.Red
MsgBox("OK")
End Sub
As comments already explained, your event handler is called in another thread and you need to sync yourself to main UI thread. For example like this:
Public Sub a_target_process_has_been_exited(sender As Object, e As EventArgs)
Me.BeginInvoke(Sub() HandleProcessExit())
End Sub
Public Sub HandleProcessExit
btnStatus.ForeColor = Color.Red
MsgBox("OK")
End Sub
This version will block main UI thread until you click on the MsgBox button.
You should add some Try/Catch block. Exception in another threads are difficult to detect otherwise.
This code depends on implicit form instances that VB.NET creates for you. I expect your frmAET is actually My.Forms.frmAET instance to make this work.

Replacement for thread.start() and thread.abort()

I need to display a form for some amount of time - basically a "please wait, loading" form with progress bar. When certain operation completes, I want this window to disappear. Here's my try at it:
If IsNothing(mlLabels) Or mblnIsLoading Then Exit Sub
If mstrPrinterA.Equals(Me.cmbPrinters.Text, StringComparison.OrdinalIgnoreCase) Then
Exit Sub
End If
Dim th As New Threading.Thread(AddressOf WaitPrinter)
th.Start()
If mlLabels.IsPrinterOnLine(Me.cmbPrinters.Text) Then
Me.cmbPrinters.BackColor = Drawing.Color.Green
Else
Me.cmbPrinters.BackColor = Drawing.Color.Red
End If
th.Abort()
Do While th.IsAlive
Loop
th = Nothing
mstrPrinterA = Me.cmbPrinters.Text
Private Sub WaitPrinter()
Dim fw As New FormWaiting
fw.ShowDialog()
fw = Nothing
End Sub
However, I then read that using Thread.Start() and Thread.Abort() is not considered a good practice. Is there another way I can do that?
Here is a simple example of what I described in my comment above. Create a WinForms project with two forms, adding a Button to Form1 and a BackgroundWorker to Form2. Add this code to Form1:
Private Sub Button1_Click(sender As Object, e As EventArgs) Handles Button1.Click
'Display a dialogue while the specified method is executed on a secondary thread.
Dim dialogue As New Form2(New Action(Of Integer)(AddressOf Pause), New Object() {5})
dialogue.ShowDialog()
MessageBox.Show("Work complete!")
End Sub
Private Sub Pause(period As Integer)
'Pause for the specified number of seconds.
Threading.Thread.Sleep(period * 1000)
End Sub
and this code to Form2:
Private ReadOnly method As [Delegate]
Private ReadOnly args As Object()
Public Sub New(method As [Delegate], args As Object())
' This call is required by the designer.
InitializeComponent()
' Add any initialization after the InitializeComponent() call.
Me.method = method
Me.args = args
End Sub
Private Sub Form2_Load(sender As Object, e As EventArgs) Handles MyBase.Load
BackgroundWorker1.RunWorkerAsync()
End Sub
Private Sub BackgroundWorker1_DoWork(sender As Object, e As ComponentModel.DoWorkEventArgs) Handles BackgroundWorker1.DoWork
'Execute the specified method with the specified arguments.
method.DynamicInvoke(args)
End Sub
Private Sub BackgroundWorker1_RunWorkerCompleted(sender As Object, e As ComponentModel.RunWorkerCompletedEventArgs) Handles BackgroundWorker1.RunWorkerCompleted
'Close the dialogue when the work is complete.
Close()
End Sub
Run the project and click the Button on the startup form. You'll see the dialogue displayed while the work is executed and then disappear when it's done. The dialogue is written in such a way that it can be used to invoke any method with any arguments. It's the caller that gets to define what the work to be performed is.
In this particular case, the "work" is a simple sleep but you can put anything you like in there. Just note that it is executed on a secondary thread so no direct interaction with the UI is allowed. If you need UI interaction then that could be accomplished but you'd need slightly more complex code. Note that the code as it is also does not allow for returning a result from the executed method, but you could support that fairly easily too.

VB .NET application not exiting when all forms close

I wanted to create an application with two forms, a main one called Source and a subsidiary called Viewer. Using buttons on each form, the user could switch to the other, even if that form had previously been closed. So I set my program to Close When Last Form Closes. Moreover, I added message boxes when each form closed, just to check that the user was sure.
But now my application won't exit after I close both forms! It shows up in Task Manager, and if I run it in MSVS, the debugger never stops! When each form is closed, My.Application.Forms returns an empty collection. And if I force quit it using End/Application.Exit, the program still quits — see the MWE below. What do I do?
MWE
Create a blank VB.NET Windows Forms project. In the "Application" tab of your project's settings, choose "Close When Last Form Closes." In the designer, create a Form Source with one button.
Public Class Source
Public Sub Switch(sender As Object, e As EventArgs) Handles Button1.Click
My.Forms.Viewer.Show()
End Sub
Public Sub Free(sender As Object, e As FormClosingEventArgs) Handles Me.Closing
e.Cancel=(MsgBoxResult.Cancel=MsgBox("Are you sure?"))
End Sub
'Public Sub Test(sender As Object, e As EventArgs) Handles Me.Closed
'Debug.Assert(Not My.Application.Forms.Count))
'If the next line is uncommented, the application will close, like we want
'If Not My.Forms.Viewer.Visible Then Application.Exit()
'End Sub
End Class
Then create an identical form called Viewer.
Public Class Viewer
Public Sub Switch(sender As Object, e As EventArgs) Handles Button1.Click
My.Forms.Source.Show()
End Sub
Public Sub Free(sender As Object, e As FormClosingEventArgs) Handles Me.Closing
e.Cancel=(MsgBoxResult.Cancel=MsgBox("Are you sure?"))
End Sub
'Public Sub Test(sender As Object, e As EventArgs) Handles Me.Closed
'Debug.Assert(Not My.Application.Forms.Count))
'If the next line is uncommented, the application will close, like we want
'If Not My.Forms.Source.Visible Then Application.Exit()
'End Sub
End Class
Try closing any form last. Neither will cause the app to quit.
Don't use Form.Closing and Form.Closed
In MSDN's page on Form.Closing, it remarks:
Caution
The Closing event is obsolete in the .NET Framework version 2.0 [and above]; use the FormClosing event instead.
The events were deprecated because
The Form.Closed and Form.Closing events are not raised when the Application.Exit method is called to exit your application. If you have validation code in either of these events that must be executed, you should call the Form.Close method for each open form individually before calling the Exit method.
The MSDN documentation nowhere states this, but the reverse is true: in order for your form to signal to the VB.NET runtime that the form closed, Form.Closing and Form.Closed cannot be subscribed to.
If you replace Me.Closing with Me.FormClosing and Me.Closed with Me.FormClosed in your MWE, it works.

Check createHandler() before closing form() vb.net

I'm currently running into a problem with a windows form application Closing.
I have multiple forms inside a main form. If I close the main form while one of the forms inside the main form is stil initializing the application crashes on Close() because createHandler() still running.
I already solved this inside one of the sub forms to add a boolean to Load() and check if the boolean is true. Example:
Private Sub frm_Load(sender As Object, e As EventArgs) Handles MyBase.Load
ini = True
// loading components etc.
ini = False
End Sub
Private Sub frm_KeyDown(sender As Object, e As KeyEventArgs) Handles MyBase.KeyDown
If e.KeyData = Keys.Escape Then
If (not ini)
Close()
End If
End If
Now I have simulair issue in the main form.
Dim Childs = (From rec In Me.MdiChildren).ToList
For Each Child In Childs
Child.Close()
Next
Is there anyway to verify if the childForm has been initialize before I close it?
something like: Child.IsInit()