Me.FormClosing works on some forms but not others - vb.net

I wish in my code to handle the case when the red X at the upper right of the form is clicked. To this end I consulted this and created an event handler thus:-
Private Sub DoFoo (sender As System.Object, e As System.EventArgs) _
Handles Me.FormClosing
' Do things
End Sub
but I have found (from setting breakpoints) that on certain forms this event handler is not invoked when the red X is clicked, whereas on others it is. The forms are all of type System.Windows.Forms.Form but naturally are different in most respects. Does anyone know what might be causing this and what to do about it?
Edit
In answer to Vitor's question, the form that isn't working is created thus:-
If my_form Is Nothing Then
my_form = New MyForm(parameters)
my_form.Title = "Contour Plot Options"
Else
my_form.BringToFront
End If
my_form.Show
Those that are behaving as expected are created like this:-
If my_working_form Is Nothing Then
my_working_form = New MyWorkingForm
End If
my_working_form.Show
I can't see any Visible property to set or clear anywhere.

Your parameters aren't quite right. A FormClosing event has a FormClosingEventArgs argument:
Private Sub DoFoo(ByVal sender As Object, ByVal e As FormClosingEventArgs) _
Handles Me.FormClosing
    If (e.CloseReason = CloseReason.UserClosing) Then
    End If
End Sub
You can inspect the e variable for the property `CloseReason', which would include a UserClosing enum, which means the user closed the form.
Each form should handle its own FormClosing event. Instead of subscribing to the event, I find it better to just override it like this:
Protected Overrides Sub OnFormClosing(ByVal e As FormClosingEventArgs)
If (e.CloseReason = CloseReason.UserClosing) Then
End If
MyBase.OnFormClosing(e)
End Sub

If you are instantiating your form you need to remember to AddHandler for the event you want to subscribe to.
my_form = New MyForm(parameters)
my_form.Title = "Contour Plot Options"
AddHandler my_form.Closing, AddressOf MyForm_Closing
'...
Sub MyForm_Closing(s As Object, ByVal e As FormClosingEventArgs)
'...
End Sub
Of course, to avoid memory leaks you should do it like this:
'global code
Private myFormClosingEventHandler As FormClosedEventHandler = Nothing
'...
Private Sub CreateForm
my_form = New MyForm(parameters)
my_form.Title = "Contour Plot Options"
myFormClosingEvent = New FormClosedEventHandler(AddressOf MyForm_Closing)
AddHandler my_form.Closing, myFormClosingEventHandler
'...
End Sub
Sub MyForm_Closing(s As Object, ByVal e As FormClosingEventArgs)
RemoveHandler my_form.Closing, myFormClosingEventHandler
'...
End Sub
Alternatively you can have it all pre-initalized for you by doing this in your class instead:
Private WithEvents my_form1 As Form = New Form()
Private WithEvents my_form2 As Form = New Form()
'... etc.
Now you can add in your code handlers using the Handle keyword as usual without using the AddHandler and RemoveHandler.
Protected Sub my_form1_Closing(s as object, e as FormClosingEventArgs) _
Handles my_form1.Closing
'...
End Sub

Related

How can I change one label out of many by right clicking one of them

I have some simple code. It changes the BorderStyle property of a Label by right-clicking. Nothing fancy, but still. However, I have twenty labels. Is there a simpler way of doing this instead of "copy-paste" this code 20 times?
Private Sub Label1_MouseDown(ByVal sender As Object, ByVal e As System.Windows.Forms.MouseEventArgs) _
Handles Label1.MouseDown
If e.Button = MouseButtons.Right Then
If Label1.BorderStyle = BorderStyle.None Then
Label1.BorderStyle = BorderStyle.FixedSingle
Else
Label1.BorderStyle = BorderStyle.None
End If
End If
End Sub
Private Sub Label2_MouseDown...
...
End Sub
You could either create a custom control which inherits from Label and has the behaviour you want, or you could write a handler which works out which control it is responding to from the sender parameter.
The latter, presented first here, is simpler for a one-off, but the former would be more re-usable, and you wouldn't have to maintain the list of Labels for the AddHandler.
Sub Label_MouseDown(ByVal sender As Object, ByVal e As System.Windows.Forms.MouseEventArgs)
Dim lbl = DirectCast(sender, Label)
If e.Button = MouseButtons.Right Then
If lbl.BorderStyle = BorderStyle.None Then
lbl.BorderStyle = BorderStyle.FixedSingle
Else
lbl.BorderStyle = BorderStyle.None
End If
End If
End Sub
Private Sub Form1_Load(sender As Object, e As EventArgs) Handles MyBase.Load
For Each l In {Label1, Label2}
AddHandler l.MouseDown, AddressOf Label_MouseDown
Next
End Sub
The AddHandler line connects the MouseDown event of each of the Labels to the specified event handler. (You can add more than one event handler to an event, if needed.)
For a control (your very own custom one) derived from an existing control (a System.Windows.Forms.Label in this case), let's call it BorderedControl, you can follow the instructions at How to: Inherit from Existing Windows Forms Controls (it's too close to plagiarism to copy it to here), and then your code for the control might look like:
Public Class BorderedLabel
Inherits Label
Protected Overrides Sub OnMouseDown(e As MouseEventArgs)
If e.Button = MouseButtons.Right Then
If Me.BorderStyle = BorderStyle.None Then
Me.BorderStyle = BorderStyle.FixedSingle
Else
Me.BorderStyle = BorderStyle.None
End If
End If
MyBase.OnMouseDown(e)
End Sub
Sub New()
' This call is required by the designer.
InitializeComponent()
' Add any initialization after the InitializeComponent() call.
End Sub
End Class
After you have built your project after adding that code, you will find a new control, named "BorderedLabel", in the ToolBox. You can drag that onto the form "design surface" and it will behave just like an ordinary Label except that it will have your BorderStyle-changing code incorporated automatically.

Working sample of Control.VisibleChanged Event in vb.net

I'm struggling to make the MSDN code sample for the Control.VisibleChanged event work: I don't see the MsgBox.
Private Sub Button_HideLabel(ByVal sender As Object, ByVal e As EventArgs)
myLabel.Visible = False
End Sub 'Button_HideLabel
Private Sub AddVisibleChangedEventHandler()
AddHandler myLabel.VisibleChanged, AddressOf Label_VisibleChanged
End Sub 'AddVisibleChangedEventHandler
Private Sub Label_VisibleChanged(ByVal sender As Object, ByVal e As EventArgs)
MessageBox.Show("Visible change event raised!!!")
End Sub 'Label_VisibleChanged
You need to "wire up" the events to the event handlers.
To start with, to get the code in HideLabel_Click to be called you need it to respond to a click on the button named "HideLabel".
There are two ways to do that: you can use AddHandler or the Handles clause.
To demonstrate the latter:
Option Strict On
Public Class Form1
Private Sub HideLabel_Click(sender As Object, e As EventArgs) Handles HideLabel.Click
myLabel.Visible = False
End Sub
Private Sub myLabel_VisibleChanged(sender As Object, e As EventArgs) Handles myLabel.VisibleChanged
MessageBox.Show("Visible change event raised!!!")
End Sub
End Class
However, you will notice that the message is shown even before the form appears. That is because of what goes on behind the scenes to create the form.
To avoid that happening, you can add the handler after the form has been shown:
Option Strict On
Public Class Form1
Private Sub HideLabel_Click(sender As Object, e As EventArgs) Handles HideLabel.Click
myLabel.Visible = False
End Sub
Private Sub myLabel_VisibleChanged(sender As Object, e As EventArgs)
MessageBox.Show("Visible change event raised!!!")
End Sub
Private Sub Form1_Shown(sender As Object, e As EventArgs) Handles MyBase.Shown
AddHandler myLabel.VisibleChanged, AddressOf myLabel_VisibleChanged
End Sub
End Class
Another way, in VB2015 and later, is to use a "lambda expression" instead of a separate method, although then you cannot disassociate the handler from the event with RemoveHandler:
Option Strict On
Public Class Form1
Private Sub HideLabel_Click(sender As Object, e As EventArgs) Handles HideLabel.Click
myLabel.Visible = False
End Sub
Private Sub Form1_Shown(sender As Object, e As EventArgs) Handles MyBase.Shown
AddHandler myLabel.VisibleChanged, Sub() MessageBox.Show("Visible change event raised!!!")
End Sub
End Class
Craig was kind enough to [and I quote verbatim] call attention to the importance of Option Strict when you add handlers manually using AddHandler. Without it, the "relaxed delegate convention" may allow adding handlers which don't exactly match the event signature that you won't be able to remove later.
Having said that, Option Strict On isn't a complete safeguard: notice how my last example compiles and works even with the wrong method signature for the handler.
[I suspect that the MSDN code sample was first created in C# as part of a larger example, so some parts have been lost in the translation and excerption.]
I get this is old but came across this post when looking for more information on VisibleChanged and couldn't help but notice that the accept answer may be misleading. If you are using a designer to create your Form and place objects on it, then the accepted answer will be fine. In fact you can get rid of the addHandler because the designer handles that for you. All you would need to do is use a handles clause with your label.
Private Sub Button_HideLabel(ByVal sender As Object, ByVal e As EventArgs) Handles Button1.Click
myLabel.Visible = False
End Sub 'Button_HideLabel
Private Sub Label_VisibleChanged(ByVal sender As Object, ByVal e As EventArgs) Handles myLabel.VisibleChanged
MessageBox.Show("Visible change event raised!!!")
End Sub 'Label_VisibleChanged
Where the issue lies with the accepted answer is if you arn't using a designer. Adding handle clauses to "wire up" simply won't work (we can make it work and if anyone is interested in that I'll be happy to post a code snippet of that, but it's not how the accepted answer lays it out). In your case all you need to do is call AddVisibleChangedEventHandler() to set up the handler. that's it. you could have done this by calling it in MyBase.Load
Private Sub Load_Form(ByVal sender As Object, ByVal e As EventArgs) Handles MyBase.Load
AddVisibleChangedEventHandler()
End Sub
Private Sub Button_HideLabel(ByVal sender As Object, ByVal e As EventArgs)
myLabel.Visible = False
End Sub 'Button_HideLabel
Private Sub Label_VisibleChanged(ByVal sender As Object, ByVal e As EventArgs)
MessageBox.Show("Visible change event raised!!!")
End Sub 'Label_VisibleChanged
Private Sub AddVisibleChangedEventHandler()
AddHandler myLabel.VisibleChanged, AddressOf Label_VisibleChanged
End Sub
Once again I know this is dated but couldn't help but notice that (more or less assuming) that you are trying to get a msgBox to appear when you click a label. That is you click a label and then toggled the visibility of another label. The other label is the one where the event handler is on for visibility change. So that inevitably gets called when clicking the original label. IF you only want this msgBox to appear when clicking that label and not when the form loads as well, you should change the addHandler statement so that you are adding a handler on the click event.
Private Sub Load_Form(ByVal sender As Object, ByVal e As EventArgs) Handles MyBase.Load
AddVisibleChangedEventHandler()
End Sub
Private Sub Label_VisibleChanged(ByVal sender As Object, ByVal e As EventArgs)
MessageBox.Show("Visible change event raised!!!")
End Sub 'Label_VisibleChanged
Private Sub AddVisibleChangedEventHandler()
AddHandler otherLabel.Click, AddressOf Label_VisibleChanged
End Sub 'AddVisibleChangedEventHandler
Also Option Strict On has nothing to do with addhandler (From my understanding, could be wrong. please enlighten me if that is the case). Option Strict On is only checking to see that you arn't implicitly typecasting. So for example:
Dim a As Double
Dim b As Integer
a = 10
b = a
results in an error when Option Strict is On but is totally legal if it is off. So in the case of you leaving off the handles clause, you'll never be implicitly typecasting and therefore is not needed.
Hope this helps anyone who sees this question

Passing the value from curElement to another private sub

Hi I have a For Each inside a Private sub and I want to pass the value of the cuElement to another Private sub.
Dim theAnchorsCollection As HtmlElementCollection = Nothing
theAnchorsCollection = WebBrowser1.Document.GetElementsByTagName("a")
For Each curElement As HtmlElement In theAnchorsCollection
curElement.Style = "style code here"
AddHandler curElement.Click, AddressOf OnaClick
Next
And I want to pass the value to this other sub:
Private Sub OnaClick(sender As Object, e As HtmlElementEventArgs)
Dim endereco As String
endereco = curElement.GetAttribute("href").ToString()
'Do other things
End Sub
I want to know if in this sub I can use something to get the value of the other's curElement.
Never mind, I found the answer it was (sender) the variable that I wanted.
As I found in the MSDN Forums
As you use Addhandler to subscribe for a given event, you pass required arguments of event to the procedure with AddressOf that delegates , whose argument signature "must" be same as event's.
A demo:
Public Class Form1
Private Sub Form1_Load(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles MyBase.Load
AddHandler Button1.MouseDown, AddressOf btn_MouseDown
End Sub
Sub btn_MouseDown(ByVal sender As Object, _
ByVal e As System.Windows.Forms.MouseEventArgs)
MsgBox(e.Button.ToString)
End Sub
End Class
Using Addhandler, we're subscribing MouseDown event of button, and specifiying the procedure which gonna be called on the follow. The passed arguments are object(sender) and MouseEventArgs (e), and you can use this in your event sub like above. (eg: To get mousebutton that was down.)

BackgroundWorker freezes GUI

I have read other posts about this but I still can't seem to get it to work right.
Whenever my BackgroundWorker begins to do work, my function API.CheckForUpdate causes the GUI to hang. I can't click on anything. It only freezes for half a second, but is enough to notice.
How can I fix this? Should I dive deeper into API.CheckForUpdate and run individual threads on particular statements, or can I just have an all-inclusive thread that handles this? API.CheckForUpdate does not reference anything in Form1.
Also, I presume Form1_Load is not the best place to put the RunWorkerAsync call. Where is a better spot?
'Declarations
Dim ApplicationUpdate As BackgroundWorker = New BackgroundWorker
Private Sub Form1_Load(sender As Object, e As EventArgs) Handles MyBase.Load
ApplicationUpdate.WorkerSupportsCancellation = True
ApplicationUpdate.WorkerReportsProgress = True
AddHandler ApplicationUpdate.DoWork, AddressOf ApplicationUpdate_DoWork
AddHandler ApplicationUpdate.ProgressChanged, AddressOf ApplicationUpdate_ProgressChanged
AddHandler ApplicationUpdate.RunWorkerCompleted, AddressOf ApplicationUpdate_RunWorkerCompleted
ApplicationUpdate.RunWorkerAsync()
End Sub
Private Sub ApplicationUpdate_DoWork(ByVal sender As Object, ByVal e As DoWorkEventArgs)
'Check for an update (get the latest version)
Dim LatestVersion = API.CheckForUpdate
End Sub
Private Sub ApplicationUpdate_ProgressChanged(ByVal sender As Object, ByVal e As ProgressChangedEventArgs)
'Nothing here
End Sub
Private Sub ApplicationUpdate_RunWorkerCompleted(ByVal sender As Object, ByVal e As RunWorkerCompletedEventArgs)
'Work completed
MsgBox("Done")
End Sub
Its not a background worker Fix but if you don't mind walking around and not finding the answer, you can code like so:
Keep in mind when you first Start a Thread and you are coding in a Model you MUST pass (me) into the initial thread because of VB having a concept of "Default Form Instances". For every Form in the application's namespace, there will be a default instance created in the My namespace under the Forms property.
and that is just adding an additional parameter like so
----------------------/ Starting Main Thread /-----------------------------------
Private Sub FindCustomerLocation()
Dim Findcontractor_Thread As New Thread(AddressOf **FindContractor_ThreadExecute**)
Findcontractor_Thread.Priority = ThreadPriority.AboveNormal
Findcontractor_Thread.Start(me)
End Sub
------------------/ Running Thread /---------------
Private Sub **FindContractor_ThreadExecute**(beginform as *NameOfFormComingFrom*)
Dim threadControls(1) As Object
threadControls(0) = Me.XamDataGrid1
threadControls(1) = Me.WebBrowserMap
**FindContractor_WorkingThread**(threadControls,beginform) ' ANY UI Calls back to the Main UI Thread MUST be delegated and Invoked
End Sub
------------------/ How to Set UI Calls from a Thread / ---------------------
Delegate Sub **FindContractor_WorkingThread**(s As Integer,beginform as *NameOfFormComingFrom*)
Sub **FindContractor_WorkingThreadInvoke**(ByVal s As Integer,beginform as *NameOfFormComingFrom*)
If beginform.mouse.InvokeRequired Then
Dim d As New FindContractor_WorkingThread(AddressOf FindContractor_WorkingThreadInvoke)
beginform.Invoke(d, New Object() {s,beginform})
Else
beginform.Mouse.OverrideCursor = Cursors.Wait
'Do something...
beginform.Mouse.OverrideCursor = Nothing
End If
End Sub
Sources From Pakks Answer Tested!
Try starting the process outside the Load event. Create a Timer and start it on the Load event, and then handle the event for the tick:
Private Sub Timer1_Tick(sender As Object, e As EventArgs) Handles Timer1.Tick
Timer1.Enabled = False
ApplicationUpdate.RunWorkerAsync()
End Sub

Custom control mybase.OnTextChanged not firing

I have a custom text box control which validates input (striped out unwanted chars). This works fine apart from when I also want to do further processing on an implementation of the control.
Example I have 3 "specialTextbox"s on a form. sText1, sText2 and sText3. sText1 & sText2 work as as intended. However, I need to make changes on the forum when the value of sText3 is changed, so I have a handler in the form to handle the ctext changed event:
Private Sub sText3(sender As Object, e As EventArgs) Handles sText3.TextChanged
'do some stuff here
End Sub
However this routine appears to override the OnTextChanged method of the custom text box. I have tried includeing a call to MyBase.OnTextChanged, but this still doesn't cascade up and no matter what I do I can't seem to get the text box to do its validation duties.
Must be something really simple, but I'm stumped!
Here is a class which overrides textbox
Public Class restrictedTextBox
Inherits Windows.Forms.TextBox
Protected validChars As List(Of Char)
Public Sub New(ByVal _validChars As List(Of Char))
MyBase.New()
validChars = _validChars
End Sub
Public Sub setValidChars(ByVal chrz As List(Of Char))
validChars = chrz
End Sub
Protected Overrides Sub OnTextChanged(e As System.EventArgs)
MyBase.OnTextChanged(e)
Dim newValue As String = ""
For Each c As Char In Me.Text.ToCharArray
Dim valid As Boolean = False
For Each c2 As Char In validChars
If c = c2 Then valid = True
Next
If valid Then newValue &= c.ToString
Next
Me.Text = newValue
End Sub
End Class
Here is a form which has a a custom textbox
Public Class frmNewForm
Private Sub btnOK_Click(ByVal sender As Object, ByVal e As EventArgs) Handles btnOK.Click
MessageBox.Show("the text from the restricted text is: " & txtRestricted.Text)
End Sub
End Class
Here is a form with a custom text box, which implements the TextChanged event
Public Class frmNewForm2
Private Sub btnOK_Click(ByVal sender As Object, ByVal e As EventArgs) Handles btnOK.Click
MessageBox.Show("the text from the restricted text is: " & txtRestricted.Text)
End If
Private Sub txtRestricted_TextChanged(sender As Object, e As EventArgs) Handles txtRestricted.TextChanged
'now that I have implemented this method, the restrictedTextBox.OnTextChanged() doesn't fire - even if I call MyBase.OnTextChanged(e)
'just to be completely clear. the line of code below DOES get executed. But the code in restrictedTextBox.vb does NOT
lblAwesomeLabel.Text=txtRestricted.Text
End Sub
End Class
It fires, but probably not the way you are implementing it.
Your sample code does not have an empty constructor for the textbox, which means you are most likely not using the designer when you are adding the textbox to the form.
But your form shows it was created by the designer:
Private Sub txtRestricted_TextChanged(sender As Object, e As EventArgs) _
Handles txtRestricted.TextChanged
End Sub
That's not possible with your posted code. If you are creating "new" controls programmatically, then you need to wire up the events programmatically, too.
Drop the handler and just leave the stub:
Private Sub txtRestricted_TextChanged(sender As Object, e As EventArgs)
'yada-yada-yada
End Sub
then when you create a new textbox, wire it up:
txtRestricted = new restrictedTextBox(myCharsList)
AddHandler txtRestricted.TextChanged, AddressOf txtRestricted_TextChanged
Me.Controls.Add(txtRestricted)