VB.NET ShowDialog Form not Ending - vb.net

I converted this app from VB6. I have 2 forms. Form1 instantiates Form2 via a Menu Item.
I am having trouble getting Form2 to end when clicking close (X). If Form2 is 'idle' it closes fine; but if I am in a loop processing anything all the events fire, but it continues processing in Form2. I've tried messing with Dispose, Close, Application.Exit, Application.ExitThread. My last attempt was creating my own event to fire back to Form1 and dispose Form2 -- and it hits it but Form2 is still running. What is the deal? BTW if I use just Show vs ShowDialog -- Form2 just blinks and disappears.
Form1 does this
Dim f2 as Import
:
Hide()
f2 = New Import
AddHandler f2.die, AddressOf killf2
f2.ShowDialog(Me)
Show()
Private Sub killf2()
f2.Dispose()
f2 = Nothing
End Sub
Form2
Public Event die()
Private Shadows Sub Form1_FormClosing(ByVal sender As Object, ByVal e As System.Windows.Forms.FormClosingEventArgs) Handles MyBase.FormClosing
Dispose()
Close()
e.Cancel = False
RaiseEvent die()
End Sub

I think you've got your events crossed. You want form1, containing an instance of form2, to listen for form2's form_closing event. Then you can set f2 = nothing.
Form1 should fully enclose form2.
here's an example:
Public Class MDIMain
Private WithEvents _child As frmViewChild
Friend Sub viewChildShow()
_child = New frmViewChild
_child.MdiParent = Me
_child.WindowState = FormWindowState.Maximized
_child.Show()
End Sub
Private Sub _child_FormClosing(ByVal sender As Object, ByVal e As System.Windows.Forms.FormClosingEventArgs) Handles _child.FormClosing
_child = Nothing
End Sub
don't add anything to form2, try
Dim f2 as Import
Hide()
f2 = New Import
f2.ShowDialog(Me)
Show()
Private Sub f2_FormClosing(ByVal sender As Object, ByVal e As System.Windows.Forms.FormClosingEventArgs) Handles f2.FormClosing
set f2 = nothing
End Sub
re: your comment
it goes back to form2 and continues processing the next statement in the the click event handler
that's a feature and it will cause this behavior. you need to make sure me.close or close me is the last statement in form2, that there's nothing else to execute.

What is this loop you speak of? The user interface (windows) is separate from any code that is running. In your class derived form Form, Code is allowed to run, both before the Form is created, and after the Form is destroyed. If the code tries to access user-interface objects then an exception might occur, but otherwise there is nothing stopping your code from running when there is no user interface.
If you want your "for" loop to exit then you must send it a signal somehow, e.g. by creating a boolean "quit" member variable. Set "quit=True" when your form closes, then have your "for" loop check whether it is true.

Related

declared event which fires, but is not heard

I have a vb.net application which contains two forms. One is called "MapComponentForm" and another called "ComponentPropertiesForm". In the MapComponentForm I have defined an event which I want fired when the OK button is clicked. If the ComponentPropertiesForm is open, I want it to "hear" this event and act accordingly. Stepping through the code the event seems to fire as expected but the ComponentPropertiesForm seems to be oblivious to this. I've attached the code from the two forms after stripping out the non relevant code in hopes that someone can tell me why my event goes unheeded. I used the information here:
https://learn.microsoft.com/en-us/dotnet/visual-basic/programming-guide/language-features/events/walkthrough-handling-events
in constructing my own code.
Thanks for any suggestions.
Public Class MapComponentForm
Public Event PartMapped(ByRef status As Boolean)
Public Sub New(shape As Visio.Shape)
' This call is required by the designer.
InitializeComponent()
' Add any initialization after the InitializeComponent() call.
_shape = shape
End Sub
Private Sub OkButton_Click(sender As Object, e As EventArgs) Handles OkButton.Click
'If the component properties window is open, refresh it
If Application.OpenForms().OfType(Of ComponentPropertiesForm).Any Then
RaiseEvent PartMapped(True)
End If
end sub
end class
Public Class ComponentPropertiesForm
Private WithEvents mPartMap As MapComponentForm
Private Sub ComponentPropertiesForm_Load(sender As Object, e As EventArgs) Handles Me.Load
mPartMap = New MapComponentForm(Nothing)
End Sub
Private Sub mPartMap_PartMapped(ByRef status As Boolean) Handles mPartMap.PartMapped
If status = True Then
MsgBox("something got mapped")
End If
End Sub
end class

Threading and modal form windows

VB.Net code.
I have a program where I am running a process in a thread and in that thread I need to have a pop up message information box that is non-modal. The main process is in a thread because it has to run in parallel and the user can initiate this process many times at the same time.
I read that the modal message box needs to be a custom form that is also ran from a thread to not block the program from continuing on. such as .Show() stops the program and waits for the user input. And you have to use .ShowDialog() via a thread
My code:
Calling initial thread:
Public Event Report As EventHandler
'In a method
Task.Run(Function() BackgroundThread())
Private Function BackgroundThread() As Task()
RaiseEvent Report(Me, New System.EventArgs)
End Function
In the Report method I have a snippet of code that then calls the form window to pop up the modal window:
Private mDiaplayMessageBox As NonModalPopUp
Private Sub DisplayMessageBox()
mDiaplayMessageBox = New NonModalPopUp()
Task.Run(Sub() mDiaplayMessageBox.ShowDialog())
End Sub
The issue I am having is that when I am finished with the report method I want to close this popup message. But when there is more than one of these pop up windows open at a time, only the last window opened will close and the program loses the handle I think to the other pop up windows and they will not close.
To close the windows I have in the modal form this code
Public Sub CloseMe()
'This will grab the thread that this window is running on, solves Cross-Threading issue.
If Me.InvokeRequired Then
Me.Invoke(New MethodInvoker(AddressOf CloseMe))
Exit Sub
End If
Me.BackColor = Color.Red
Me.Close()
End Sub
This first time this code is called its will hit the Me.Invoke and then close the window. However, on any subsequent calls when it gets to Me.InvokeRequired this will then be set to false, not called the Me.Invoke and go to the Me.Close() but it will not close the window.
I tried to do something where I grab the Handle intptr value but when ever I vent just look at that value the program immediately throws a cross-threading exception.
All I want to do is close the other windows which does not seem like a hard task but I do not know what I am missing.
One of approaches you can follow to achieve your goal might be as code below shows:
You can create a custom event which you can use as a “call” to listen to for the closure of your form.
Public Class Form1
Dim frm2 As Form2
Private Sub Button1_Click(sender As Object, e As EventArgs) Handles Button1.Click
frm2 = New Form2
Task.Run(Sub()
AddHandler CloseFrm2, Sub()
Dim CloseMe As Action = Sub()
frm2.Close()
frm2.Dispose()
End Sub
If frm2.InvokeRequired Then
frm2.Invoke(Sub() CloseMe())
Else
CloseMe()
End If
End Sub
frm2.ShowDialog()
End Sub)
End Sub
Private Sub Button2_Click(sender As Object, e As EventArgs) Handles Button2.Click
RaiseEventCloseFrm2()
End Sub
End Class
Module EventHelper
Public Event CloseFrm2()
Sub RaiseEventCloseFrm2()
RaiseEvent CloseFrm2()
End Sub
End Module

How to raise event from user control to another form vb.net

I am not familiar with user controls. I have user control with OK and Cancel buttons, I add this user control to an empty form during run time, I have another form which I called "Main Form", In the code I open the empty form and add the user control, after that I want to raise an event (on the OK button) from the user control (or from the empty form I don't know!) to the Main form!
I searched the net and found way to create an event and raise the event in the same form. I tried to do the same thing between different forms but I don't know how to do that.
create event
Public Event OKEvent As EventHandler
raise event
Protected Overridable Sub OnbtnOKClick(e As EventArgs)
AddHandler OKEvent , AddressOf btnOKClickHandler
RaiseEvent OKEvent(Me, e)
End Sub
event Sub
Sub btnOKClickHandler(sender As Object, e As EventArgs)
'Event Handling
'My event code
End Sub
handle my event on btnOK.click event
Private Sub btnOK_Click(sender As Object, e As EventArgs) Handles btnOK.Click
OnbtnOKClick(e)
End Sub
Okey, that all code works in the same form, maybe its messy but that what I found on the net, I want to make similar thing but on different forms, how can I organize my code?
You don't raise an event "to" anywhere. The whole point of events is that you don't have to care who is listening. It's up to the whoever wants to react to the event to handle it and whoever raised the event never has to know about them.
So, all you need to do in this case is have the main form handle the appropriate event(s) of the user control. The user control is a control like any other and the event is an event like any other so the main form handles the event of the user control like it would handle any other event of any other control. Where you create the user control, use an AddHandler statement to register a handler for the event you declared in the user control.
EDIT:
The OnbtnOKClick method that you have shown above should not look like this:
Protected Overridable Sub OnbtnOKClick(e As EventArgs)
AddHandler OKEvent , AddressOf btnOKClickHandler
RaiseEvent OKEvent(Me, e)
End Sub
but rather like this:
Protected Overridable Sub OnbtnOKClick(e As EventArgs)
RaiseEvent OKEvent(Me, e)
End Sub
Also, the btnOKClickHandler method should be in the main form, not the user control. As I said in my answer, it's the main form that handles the event. That means that the event handler is in the main form. As I also said, when you create the user control in the main form, that is where you register the event handler, e.g.
Dim uc As New SomeUserControl
AddHandler uc.OKEvent, AddressOf btnOKClickHandler
As I hope you have absorbed in your reading, if you use AddHandler to register an event handler then you need a corresponding RemoveHandler to unregister it when the object is finished with.
If I'm understanding correctly, you have...
FormA creates FormB.
FormB creates UserControlB.
UserControlB raises "OK" event.
FormB receives "OK" event.
...but you want an additional step of:
FormA receives "OK" event.
The problem here is that FormA has no reference to UserControlB because FormB was the one that created the UserControl. An additional problem is that FormB may have no idea who FormA is (depending on how you've got it setup).
Option 1 - Pass References
If you want both FormB and FormA to respond to a SINGLE "OK" event (generated by the UserControl), then you'd have to somehow have a reference to all three players in the same place so that the event can be properly wired up. The logical place to do this would be in FormB as that is where the UserControl is created. To facilitate that, however, you'd have to modify FormB so that a reference to FormA is somehow passed to it. Then you can wire up the "OK" event directly to the handler in FormA when FormB creates its instance of UserControlB:
Public Class FormA
Private Sub Button1_Click(sender As Object, e As EventArgs) Handles Button1.Click
Dim frmB As New FormB(Me) ' pass reference to FormA into FormB via "Me" and the Constructor of FormB
frmB.Show()
End Sub
Public Sub ucB_OKEvent()
' ... do something in here ...
Debug.Print("OK Event received in FormA")
End Sub
End Class
Public Class FormB
Private _frmA As FormA
Public Sub New(ByVal frmA As FormA)
' This call is required by the designer.
InitializeComponent()
' Add any initialization after the InitializeComponent() call.
_frmA = frmA ' store reference to FormA so we can use it later
End Sub
Private Sub FormB_Load(sender As Object, e As EventArgs) Handles MyBase.Load
Dim ucB As New UserControlB
ucB.Location = New Point(10, 10)
AddHandler ucB.OKEvent, AddressOf _frmA.ucB_OKEvent ' wire up the "OK" event DIRECTLY to the handler in our stored reference of FormA
Me.Controls.Add(ucB)
End Sub
End Class
Public Class UserControlB
Public Event OKEvent()
Private Sub btnOK_Click(sender As Object, e As EventArgs) Handles btnOK.Click
RaiseEvent OKEvent()
End Sub
End Class
Option 2 - "Bubble" the Event
Another option is "bubble" the event from FormB up to FormA. In this scenario, FormB will have no idea who FormA is, so no reference will be passed. Instead, FormB will have its own "OK" event that is raised in response to receiving the original "OK" event from UserControlB. FormA will get the "OK" notification from the UserControl, just not directly:
Public Class FormA
Private Sub Button1_Click(sender As Object, e As EventArgs) Handles Button1.Click
Dim frmB As New FormB
AddHandler frmB.OKEvent, AddressOf frmB_OKEvent
frmB.Show()
End Sub
Private Sub frmB_OKEvent()
' ... do something in here ...
Debug.Print("OK Event received from FormB in FormA")
End Sub
End Class
Public Class FormB
Public Event OKEvent()
Private Sub FormB_Load(sender As Object, e As EventArgs) Handles MyBase.Load
Dim ucB As New UserControlB
ucB.Location = New Point(10, 10)
AddHandler ucB.OKEvent, AddressOf ucB_OKEvent
Me.Controls.Add(ucB)
End Sub
Private Sub ucB_OKEvent()
Debug.Print("OK Event received from UserControl in FormB")
RaiseEvent OKEvent()
End Sub
End Class
Public Class UserControlB
Public Event OKEvent()
Private Sub btnOK_Click(sender As Object, e As EventArgs) Handles btnOK.Click
RaiseEvent OKEvent()
End Sub
End Class
Design Decisions
You have to make a design decision here. Who is the source of the "OK" event? Should it be the UserControl or FormB? Will the UserControl ever be used in different forms (other than FormB)? Will FormB ever be used with Forms other then FormA? These answers may help you decide which approach is better...or may lead you to rethink how you've designed your current solution; you may need to change it altogether.

Reopening a Disposed form declared with events. 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

How to call a Sub without knowing which form is loaded into panel?

On every DataGridView1_SelectionChanged event I need to run a Private Sub OnSelectionChanged() of the form that is loaded into Panel1 (see the image http://tinypic.com/r/2nu2wx/8).
Every form that can be loaded into Panel1 has the same Private Sub OnSelectionChanged() that initiates all the necessary calculations. For instance, I can load a form that calculates temperatures or I can load a form that calculates voltages. If different element is selected in the main form’s DataGridView1, either temperatures or voltages should be recalculated.
The problem is - there are many forms that can be loaded into Panel1, and I’m struggling to raise an event that would fire only once and would run the necessary Sub only in the loaded form.
Currently I’m using Shared Event:
'Main form (Form1).
Shared Event event_UpdateLoadedForm(ByVal frm_name As String)
'This is how I load forms into a panel (in this case frm_SCT).
Private Sub mnu_SCT_Click(sender As Object, e As EventArgs) Handles mnu_SCT.Click
frm_SCT.TopLevel = False
frm_SCT.Dock = DockStyle.Fill
Panel1.Controls.Add(frm_SCT)
frm_SCT.Show()
Var._loadedForm = frm_SCT.Name
RaiseEvent event_UpdateLoadedForm(Var._loadedForm)
End Sub
‘Form that is loaded into panel (Form2 or Form3 or Form4...).
Private WithEvents myEvent As New Form1
Private Sub OnEvent(ByVal frm_name As String) Handles myEvent.event_UpdateLoadedForm
‘Avoid executing code for the form that is not loaded.
If frm_name <> Me.Name Then Exit Sub
End Sub
This approach is working but I’m sure it can be done way better (I'd be thankful for any suggestions). I have tried to raise an event in the main form like this:
Public Event MyEvent As EventHandler
Protected Overridable Sub OnChange(e As EventArgs)
RaiseEvent MyEvent(Me, e)
End Sub
Private Sub DataGridView1_SelectionChanged(sender As Object, e As EventArgs) _
Handles DataGridView1.SelectionChanged
OnChange(EventArgs.Empty)
End Sub
but I don't know to subscribe to it in the loaded form.
Thank you.
Taking into account Hans Passant’s comments as well as code he posted in related thread I achieved what I wanted (see the code below).
Public Interface IOnEvent
Sub OnSelectionChange()
End Interface
Public Class Form1
' ???
Private myInterface As IOnEvent = Nothing
' Create and load form.
Private Sub DisplayForm(frm_Name As String)
' Exit if the form is already displayed.
If Panel1.Controls.Count > 0 AndAlso _
Panel1.Controls(0).GetType().Name = frm_Name Then Exit Sub
' Dispose previous form.
Do While Panel1.Controls.Count > 0
Panel1.Controls(0).Dispose()
Loop
' Create form by its full name.
Dim T As Type = Type.GetType("Namespace." & frm_Name)
Dim frm As Form = CType(Activator.CreateInstance(T), Form)
' Load form into the panel.
frm.TopLevel = False
frm.Visible = True
frm.Dock = DockStyle.Fill
Panel1.Controls.Add(frm)
' ???
myInterface = DirectCast(frm, IOnEvent)
End Sub
Private Sub DataGridView1_SelectionChanged(sender As Object, e As EventArgs) _
Handles DataGridView1.SelectionChanged
' Avoid error if the panel is empty.
If myInterface Is Nothing Then Return
' Run subroutine in the loaded form.
myInterface.OnSelectionChange()
End Sub
End Class
One last thing – it would be great if someone could take a quick look at the code (it works) and confirm that it is ok, especially the lines marked with “???” (I don’t understand them yet).