Working sample of Control.VisibleChanged Event in vb.net - 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

Related

VB.net One sub for many events

I'm working on a function in VB.Net where a user can select a supplier from a list.
The idea is that the user will filter the list until the right supplier is visible in a datagridview
the user can then either double click on the row header, the cell content or select a supplier and then click an OK button
I am wondering though, how do I avoid building one Sub for each of the above three events, Can I create one sub that catches all three events?
Private Sub supplierSearchOkButton_Click(sender As Object, e As EventArgs) Handles supplierSearchOkButton.Click
initiativeForm.supplierConcatTextBox.Text = supplierSearchDataGridView.SelectedRows(0).Cells(3).Value.ToString()
Me.Close()
End Sub
Private Sub supplierSearchDataGridView_CellContentDoubleClick(sender As Object, e As DataGridViewCellEventArgs) Handles supplierSearchDataGridView.CellContentDoubleClick
initiativeForm.supplierConcatTextBox.Text = supplierSearchDataGridView.SelectedRows(0).Cells(3).Value.ToString()
Me.Close()
End Sub
Private Sub supplierSearchDataGridView_RowHeaderMouseDoubleClick(sender As Object, e As DataGridViewCellMouseEventArgs) Handles supplierSearchDataGridView.RowHeaderMouseDoubleClick
initiativeForm.supplierConcatTextBox.Text = supplierSearchDataGridView.SelectedRows(0).Cells(3).Value.ToString()
Me.Close()
End Sub
Personally, I would go with the extra method option mentioned in the comments but, if you want to, you should be able to do this:
Private Sub SetSupplier(sender As Object, e As EventArgs) Handles supplierSearchOkButton.Click,
supplierSearchDataGridView.CellContentDoubleClick,
supplierSearchDataGridView.RowHeaderMouseDoubleClick
initiativeForm.supplierConcatTextBox.Text = supplierSearchDataGridView.SelectedRows(0).Cells(3).Value.ToString()
Me.Close()
End Sub
or even this:
Private Sub SetSupplier() Handles supplierSearchOkButton.Click,
supplierSearchDataGridView.CellContentDoubleClick,
supplierSearchDataGridView.RowHeaderMouseDoubleClick
initiativeForm.supplierConcatTextBox.Text = supplierSearchDataGridView.SelectedRows(0).Cells(3).Value.ToString()
Me.Close()
End Sub
If you're not using any properties of the other e parameters then you can use the most general EventArgs for all three events and if you're not using the parameters at all then you can ditch them altogether. I didn't test this specifically but I'm fairly sure both will work.

vb.net subroutine not updating module level variable until exit sub

I have come to learn that a module level variable's value will not be altered until a sub routine that changed it exits.
StopBackgroundWorker1 = True
Thread.Sleep(1500)
If BackgroundWorker1Complete = False Then
Exit Sub
End If
in this example, I added a long delay for testing. I'm simply trying to stop and start a background worker safely with vb 2017 new background worker class.
The example above with "StopBackgroundWorker1 = True", I was hoping to stop the worker at a safe place and then continue within that sub with other code.
But what is happening is that the "StopBackgroundWorker1 = True" is not being set "True" until the sub exits.
There must be another way to do what I am trying to do, please help
Ok here is a complete example,
Imports System.ComponentModel
Imports System.Threading
Public Class Form1
Private flag As Boolean = False
Dim Completed As Boolean = False
Private Sub Form1_Load(sender As Object, e As EventArgs) Handles MyBase.Load
BackgroundWorker1.RunWorkerAsync()
End Sub
Private Sub Button1_Click(sender As Object, e As EventArgs) Handles Button1.Click
flag = True
'Do
' do loop never see's a true flag
'Loop Until Completed
Thread.Sleep(500)
If Completed = True Then
Label1.BackColor = Color.Red
End If
End Sub
Private Sub BackgroundWorker1_DoWork(sender As Object, e As System.ComponentModel.DoWorkEventArgs) Handles BackgroundWorker1.DoWork
Do
Thread.Sleep(25)
Loop Until flag
End Sub
Private Sub BackgroundWorker1_RunWorkerCompleted(sender As Object, e As
RunWorkerCompletedEventArgs) Handles BackgroundWorker1.RunWorkerCompleted
Completed = True
End Sub
Private Sub Button2_Click(sender As Object, e As EventArgs) Handles
Button2.Click
Label1.Text = flag.ToString
End Sub
End Class
Now the concept is if you hit button1 and wait for background worker to complete, it should turn lable1 red. but it doesn't. The do loop looking for a true flag will spin forever locking the form up.
I have determined with this example that the flag is not set to true until you exit the sub. Hit Button1 again and lable1 turns red.
Thanks in advance for any answers.
This doesn't answer your question per se but I want to post a large code snippet so I'll post it as an answer. It demonstrates that what you think is the problem is not the problem, i.e. that a field's value changes as soon as you change it, even if that change is made from a BackgroundWorker.DoWork event handler.
Create a new Windows Forms application project, add a Button, a Label and a BackgroundWorker to your form and then paste in this code over the default code of the form:
Imports System.ComponentModel
Imports System.Threading
Public Class Form1
Private flag As Boolean
Private Sub Form1_Load(sender As Object, e As EventArgs) Handles MyBase.Load
BackgroundWorker1.WorkerReportsProgress = True
BackgroundWorker1.RunWorkerAsync()
End Sub
Private Sub Button1_Click(sender As Object, e As EventArgs) Handles Button1.Click
Label1.Text = flag.ToString()
End Sub
Private Sub BackgroundWorker1_DoWork(sender As Object, e As System.ComponentModel.DoWorkEventArgs) Handles BackgroundWorker1.DoWork
Thread.Sleep(5000)
flag = True
BackgroundWorker1.ReportProgress(0)
Thread.Sleep(5000)
End Sub
Private Sub BackgroundWorker1_ProgressChanged(sender As Object, e As ProgressChangedEventArgs) Handles BackgroundWorker1.ProgressChanged
Label1.BackColor = Color.Green
End Sub
Private Sub BackgroundWorker1_RunWorkerCompleted(sender As Object, e As RunWorkerCompletedEventArgs) Handles BackgroundWorker1.RunWorkerCompleted
Label1.BackColor = Color.Red
End Sub
End Class
Run the project and, when the form appears, start clicking the Button at a pace of a few times per second. You'll see that the value of the flag field, as displayed on the Label, changes from False to True as soon as the code to set it is executed in the DoWork event handler. The Label will turn green when that happens, so it's easy to spot. You'll know that it didn't wait until the DoWork event handler completes because the Label will turn red at that point.
EDIT: Now that you have provided all the relevant information, the issue is obvious. As I have already said, the moment you set a variable, that is the value of that variable. There's no waiting because there cannot be any waiting because there's nowhere to store a temporary value for the variable.
The reason that it looks otherwise is that your test code is faulty. If you use the debugger then you will see how. When you use a BackgroundWorker, the DoWork event handler is executed on a secondary thread but the RunWorkerCompleted event handler is executed on the UI thread. That means that your DoWork event handler can execute at the same time as your Click event handler for Button1 because they are on different threads, but the RunWorkerCompleted event handler cannot run at the same time, so it has to wait until the Click event handler completes before it can be executed. That means that the code to set the Completed field doesn't get executed until the Click event handler completes. It's not that the field value doesn't change when it's set but rather that it doesn't actually get set. If you place breakpoints on the two lines that access that Completed field then you'll see that.
The mistake you're making is trying to do something in that Click event handler after the DoWork event handler completes. That's wrong. That's exactly what the RunWorkerCompleted event handler is for. That's where you do UI work after the background work completes.
Also, you can get rid of that flag variable. Cancellation functionality is built into the BackgroundWorker class. Look at the CancelAsync method and the CancellationPending property.
Many thanks to "jmcilhinney" for his insights! I have figured out the code I was looking for!
This code allows me to stop and start a background thread safely by allowing the background thread to finish completely before restarting.
During the time that the background thread is stopped, user actions can perform operations without the worry of cross-threading or with thread.abort garbled code conditions.
Finally, stress-free threading!
I wish I could find the doc on MSDN that I read that stated the await async method was far superior to task.run but that's another argument.
This may not be the best code in the world but it works!
And in light of trying to rewrite all code in my project with async and await I'll stick with this!
Public Class Form1
Private flag As Boolean = False
Dim Completed As Boolean = False
Private Sub Form1_Load(sender As Object, e As EventArgs) Handles MyBase.Load
BackgroundWorker1.RunWorkerAsync()
End Sub
Private Async Sub Button1_Click(sender As Object, e As EventArgs) Handles Button1.Click
flag = True
Await Task.Run(Sub()
Do
Loop Until Completed
End Sub)
If Completed = True Then
Label1.BackColor = Color.Red
End If
BackgroundWorker1.RunWorkerAsync()
End Sub
Private Sub BackgroundWorker1_DoWork(sender As Object, e As System.ComponentModel.DoWorkEventArgs) Handles BackgroundWorker1.DoWork
Do
Thread.Sleep(250)
Loop Until flag
End Sub
Private Sub BackgroundWorker1_RunWorkerCompleted(sender As Object, e As
RunWorkerCompletedEventArgs) Handles BackgroundWorker1.RunWorkerCompleted
Completed = True
End Sub
End Class
For all of the academics out there this code is more appropriate.
Imports System.ComponentModel
Imports System.Threading
Public Class Form1
Private Completed As Boolean = False
Private Sub Form1_Load(sender As Object, e As EventArgs) Handles MyBase.Load
BackgroundWorker1.WorkerSupportsCancellation = True
BackgroundWorker1.RunWorkerAsync()
End Sub
Private Async Sub Button1_Click(sender As Object, e As EventArgs) Handles
Button1.Click
BackgroundWorker1.CancelAsync()
Await Task.Run(Sub()
Do
Loop Until Completed
End Sub)
If Completed = True Then
Label1.BackColor = Color.Red
End If
BackgroundWorker1.RunWorkerAsync()
End Sub
Private Sub BackgroundWorker1_DoWork(sender As Object, e As System.ComponentModel.DoWorkEventArgs) Handles BackgroundWorker1.DoWork
Do
Thread.Sleep(250)
Loop Until BackgroundWorker1.CancellationPending
End Sub
Private Sub BackgroundWorker1_RunWorkerCompleted(sender As Object, e As RunWorkerCompletedEventArgs) Handles BackgroundWorker1.RunWorkerCompleted
Completed = True
End Sub
End Class

use the same method for two different events?

So basically I've got three listboxes containing items. I want the items to be deletable, so for this I've got a ContextMenuStrip with only one item : Delete. Though, I'd like the items to be deletable, too, via a press on the Delete key. So i've got my code, that you can see here :
Dim TempList As New List(Of String)
For Each Trigger In ListBoxTriggers.SelectedItems
TempList.Add(Trigger)
Next
For Each Trigger In TempList
ListBoxTriggers.Items.Remove(Trigger)
Next
It's a little longer because there is data related stuff but now this is the part concerning the removing from the ListBox. Now, for this I've been using
Private Sub ToolStripMenuItemTriggers_Click(sender As Object, e As EventArgs) Handles SupprimerToolStripMenuItemTriggers.Click
(supprimer means delete in French). But the thing is I'd like to process the
Private Sub ListBoxDescription_KeyDown(sender As Object, e As KeyEventArgs) Handles ListBoxDescription.KeyDown
in the same method. But I can't since e is not the same type... I of course can copy the same code in both handlers but that's not really... clean. I can, too, just create another method that I'll call in both ases like
Private Sub ListBoxDescription_KeyDown(sender As Object, e As KeyEventArgs) Handles ListBoxDescription.KeyDown
Delete()
End Sub
Private Sub ToolStripMenuItemTriggers_Click(sender As Object, e As EventArgs) Handles SupprimerToolStripMenuItemTriggers.Click
Delete()
End Sub
But I don't really like it neither... doesn't look like the most efficient solution...
Is there anything I can do for this ?
Thank you in advance
KeyEventArgs derives from EventArgs, so you can declare
Private Sub ListBoxDescription_KeyDown(sender As Object, e As EventArgs) Handles ListBoxDescription.KeyDown
and if you actually need e as KeyEventArgs then you can use
Dim kea = DirectCast(e, KeyEventArgs)
Also, if your delete method has a signature like
Sub DeleteThings(sender As Object, e As EventArgs)
then you can do
AddHandler ListBoxDescription.KeyDown, AddressOf DeleteThings
AddHandler ToolStripButton1.Click, AddressOf DeleteThings
Note that you do not need a Handles clause when using AddHandler.
You'll have to write the common event handler similar to this:
Private Sub CommonEventHandler(sender As Object, e As EventArgs) _
Handles ToolStripMenuItemTriggers.Click, ListBoxDescription.KeyDown
If sender Is ListBoxDescription Then
Dim kea = DirectCast(e, KeyEventArgs)
If kea.KeyData <> Keys.F2 Then Exit Sub
End If
'' Common code
''...
End Sub
Works fine, pretty hard to win elegance points with it however. You might as well move the Common code into a separate private method. The usual advice is to treat whomever is going to maintain your code some day as a homicidal maniac that knows where you live.

Changing button name if file exists?

I'm working on a custom GUI, my last step is for the button to change automatically check if the file exists or not on startup. The method below is my download/open button. Any help is appreciated!
Private Sub Button7_Click(ByVal sender As Object, ByVal e As EventArgs) Handles Button7.Click
If Dir("DownloadedFile.zip") <> "" Then
Process.Start("DownloadedFile.zip")
Else
WC.DownloadFileAsync(New Uri("http://download852.mediafire.com/3a688rz1a6ig/dk71cs34ihs3v6x/Devil+went+down+to+georgia.rar"), "DownloadedFile.zip")
MsgBox("Starting Download")
End If
End Sub
A subroutine is a seperate method that does not return a value. I would take the If statement out of your click eventhandler and put it in its own method as I stated like this.
Private Sub CheckForFile()
If Dir("DownloadedFile.zip") <> "" Then
Process.Start("DownloadedFile.zip")
Else
WC.DownloadFileAsync(New Uri("http://download852.mediafire.com/3a688rz1a6ig/dk71cs34ihs3v6x/Devil+went+down+to+georgia.rar"), "DownloadedFile.zip")
MsgBox("Starting Download")
End If
End Sub
Call it from your button like this.
Private Sub Button7_Click(sender As Object, e As EventArgs) Handles Button7.Click
CheckForFile()
End Sub
Then handle the Shown Event and call it from there. It will run as soon as the initial form is shown
Private Sub Form1_Shown(sender As Object, e As EventArgs) Handles Me.Shown
CheckForFile()
End Sub
Responding to your comment You need to use the WebClient DownloadFileCompleted Event and the WebClient DownloadProgressChanged Event

Is it possible to detect a Form mouseclick from a User Control

I have created a User Control and would like to be able to detect when the user clicks on the Form.
I have seen this question which is related but the suggestion to use the the Leave event doesn't always do what I want because the focus doesn't necessarily change when the user clicks the Form (my control could be the only control on the Form in which case focus stays with my control).
Any ideas?
I want to be able to do something like this from within the User Control:
Private Sub ParentForm_Click(sender As Object, e As System.EventArgs) _
Handles Me.Parent.Click
End Sub
I would do it slightly differently:
Private _form As Control
Private Sub UserControl_ParentChanged(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles MyBase.ParentChanged
If _form IsNot Nothing Then
RemoveHandler _form.Click, AddressOf ParentOnClick
End If
_form = Me.FindForm()
If _form IsNot Nothing Then
AddHandler _form.Click, AddressOf ParentOnClick
End If
End Sub
Private Sub ParentOnClick(ByVal sender As Object, ByVal e As EventArgs)
'...
End Sub
This gives it a little more resillience - if it is not a direct child of a Form, if its parent changes etc.
I figured out how to do this myself - for anyone interested I am doing the following:
Private _parentForm As Form
Private Sub UserControl_Load(sender As System.Object, e As System.EventArgs) Handles MyBase.Load
_parentForm = CType(Parent, Form)
AddHandler _parentForm.Click, AddressOf ParentForm_Click
End Sub
Private Sub ParentForm_Click(sender As Object, e As System.EventArgs)
debug.writeline("Parent form clicked")
End Sub