Consolidate redundant code in VB.NET - vb.net

Is there a more elegant or efficient way to write some of my code?
In particular, I have a toolstrip on all of my forms. Each form has its own set of methods that essentially is doing the same thing. Is there a way to delete all of the methods save one, and then just enclose it into a switch statement for my form specific variables?
An example of what I am talking about:
'Form 1
Private Sub ExitToolStripMenuItem_Click(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles ExitToolStripMenuItem.Click
Application.Exit()
End Sub
'Form 2
Private Sub ExitToolStripMenuItem_Click(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles ExitToolStripMenuItem.Click
Application.Exit()
End Sub
Could I delete one of them, and move the other to a separate module and have it act for both form1 and form2?
Private Sub ExitToolStripMenuItem_Click(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles [myWholeDangProject].ExitToolStripMenuItem.Click
Application.Exit()
End Sub

Yes, use Addhandler and AddressOf to hookup the events when the forms initialize.
AddHandler ExitToolStripMenuItem.Click, AddressOf MyModule.ExitHandler
and
Public Sub ExitHandler(ByVal sender As System.Object, ByVal e As System.EventArgs)
Application.Exit()
End Sub

There is no way to direct a WithEvents event to a shared module. Like you say, though, a shared class or VB.NET module can hold subroutines that can be called from both control event handlers.
You could make a shared event handler, but you have to use the AddHandler keyword to actually attach the code to the event though in most simple cases just a straight call to a sub would be easier.

Related

Access Main thread data from the Background thread

I´m using a backgroundworker to handle processing of data while the user is still free to for instance click another button that aborts the process.
However, the code in backgroundworker requires several pieces of data, for instance it needs to know whether a radio button is checked.
Is there a way to access data in another thread from the background thread? Or should I create global variables that hold this information?
Public Class Test
Private Sub Button1_Click(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles Button1.Click
Button1.Content = "Working..."
BackgroundWorker1.RunWorkerAsync()
End Sub
Private Sub BackgroundWorker1_DoWork(ByVal sender As Object, ByVal e As System.ComponentModel.DoWorkEventArgs) Handles BackgroundWorker1.DoWork
If RadioButton1.IsChecked Then
MsgBox("It works")
End If
End Sub
End Class
You can pass the RadioButton checked state to the RunWorkerAsync method like this:
Private Sub Button1_Click(sender As Object, e As EventArgs) Handles Button1.Click
BackgroundWorker1.RunWorkerAsync(RadioButton1.Checked)
End Sub
Private Sub BackgroundWorker1_DoWork(sender As Object, e As System.ComponentModel.DoWorkEventArgs) Handles BackgroundWorker1.DoWork
Dim checked As Boolean = CBool(e.Argument)
If checked Then
MessageBox.Show("It works")
End If
End Sub
Let me know if you need to be checking the RadioButton continuously from inside a loop, as that would be different.

form starting but not displaying vb.net

So I was coding a program that opens 20 web browsers on one page, for a youtube or view bot any URL really, but when I launched the program It displayed the text box I put in to see if it was actually starting and then nothing else
So my question is, Whats wrong with my code?
startup form (where you can launch the main code with button 1)
Public Class Startup
Private Sub Button2_Click(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles Button2.Click
Settings.Show()
End Sub
Private Sub Button1_Click(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles Button1.Click
views.Show()
End Sub
End Class
main form
Public Class views
Private Sub views_Load(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles MyBase.Load
MessageBox.Show("Form Initalised")
WebBrowser1.Navigate(My.Settings.url)
WebBrowser2.Navigate(My.Settings.url)
WebBrowser3.Navigate(My.Settings.url)
WebBrowser4.Navigate(My.Settings.url)
WebBrowser5.Navigate(My.Settings.url)
WebBrowser6.Navigate(My.Settings.url)
WebBrowser7.Navigate(My.Settings.url)
WebBrowser8.Navigate(My.Settings.url)
WebBrowser9.Navigate(My.Settings.url)
WebBrowser10.Navigate(My.Settings.url)
WebBrowser11.Navigate(My.Settings.url)
WebBrowser12.Navigate(My.Settings.url)
WebBrowser13.Navigate(My.Settings.url)
WebBrowser14.Navigate(My.Settings.url)
WebBrowser15.Navigate(My.Settings.url)
WebBrowser16.Navigate(My.Settings.url)
WebBrowser17.Navigate(My.Settings.url)
WebBrowser18.Navigate(My.Settings.url)
WebBrowser19.Navigate(My.Settings.url)
WebBrowser20.Navigate(My.Settings.url)
Threading.Thread.Sleep(My.Settings.Time)
reload.Show()
Me.Close()
End Sub
End Class
and form reload mentioned in the main form.
Public Class reload
Private Sub reload_Load(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles MyBase.Load
Me.Close()
views.Show()
End Sub
End Class
Try to use the Form.Shown event instead of the Form.Load event. In Form.Load the controls are often not fully initialized and if something goes wrong there the initialization of the whole form sometimes goes awry. It's better to use the Form.Shown for such lengthy procedures you have there.

How to use Backgroundworker to load form?

When I try to load a form with a backgroundworker class, it throws an exception because I tried to access an object that was created by another thread, not by the current thread. How to load that form by backgroundworker ?
(I am looking for marquee progress bar while the form is loading)
You can't change any controls in the DoWork section of a backgroundworker since it's a separate thread. You'll want to process any data in the DoWork section, then update your form in the RunWorkerCompleted section.
Example:
Private WithEvents bgwTasks As New BackgroundWorker
Private Sub bgwTasks_DoWork(ByVal sender As Object, ByVal e As System.ComponentModel.DoWorkEventArgs) Handles bgwTasks.DoWork
e.Result= GetDatabaseTable()
End Sub
Private Sub bgwTasks_RunWorkerCompleted(ByVal sender As Object, ByVal e As System.ComponentModel.RunWorkerCompletedEventArgs) Handles bgwTasks.RunWorkerCompleted
If e.Result IsNot Nothing Then
DataGridView1.DataSource = e.Result
End If
End Sub
Private Sub Main_Load(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles MyBase.Load
If bgwTasks.IsBusy = False Then
bgwTasks.RunWorkerAsync()
End If
End Sub
This will disable checking for Cross Threads
Me.CheckForIllegalCrossThreadCalls = False
I still suggest you search for Cross Thread regarding the proper use of the BackgroundWorker Class.
There is no problem to initialize the second form in another thread, but if you want to use it in the context on your main form, you have to use original thread. The follows code creates and initializes new form in background worker and then shows it when initialization is completed in appropriate event handler:
Public Class Form1
Dim form2 As Form2
Private Sub Form1_Load(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles MyBase.Load
BackgroundWorker1.RunWorkerAsync()
End Sub
Private Sub BackgroundWorker1_DoWork(ByVal sender As System.Object, ByVal e As System.ComponentModel.DoWorkEventArgs) Handles BackgroundWorker1.DoWork
form2 = New Form2()
form2.TextBox1.Text = "Text"
End Sub
Private Sub BackgroundWorker1_RunWorkerCompleted(ByVal sender As System.Object, ByVal e As System.ComponentModel.RunWorkerCompletedEventArgs) Handles BackgroundWorker1.RunWorkerCompleted
form2.Show()
End Sub
End Class
You can use ProgressChanged event of background worker for the purpose of report to progress bar.
You cannot do this. Forms have Thread-Affinity. ie. they can ONLY be accessed by the thread that instantiated the Form. You background thread just became the UI thread for the form!
So what you MUST do is marshal the call onto the correct thread.
Test InvokeRequired. If true, instantiate a delegate, call BeginInvoke and IMMEDIATELY return.
Now the call will be marshaled to the correct thread.

Threading functions with VB.net

I'm trying to thread my functions so the main GUI of my application doesn't fail to respond while the tasks are running.
At the moment my form1.vb is something like this. I've cut it down as to reduce the text, but everything works fine:
Public Class MAIR
Private Sub InstallTheAgent_Click(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles InstallTheAgent.Click
InstallAgentWorker.RunWorkerAsync()
End Sub
Private Sub InstallAgentWorker_DoWork(ByVal sender As System.Object, ByVal e As System.ComponentModel.DoWorkEventArgs) Handles InstallAgentWorker.DoWork
'Do some stuff
End Sub
Private Sub InstallAgentWorker_RunWorkerCompleted(ByVal sender As System.Object, ByVal e As System.ComponentModel.RunWorkerCompletedEventArgs) Handles InstallAgentWorker.RunWorkerCompleted
' Called when the BackgroundWorker is completed.
MsgBox("Installation of the Agent Complete")
ProgressBar1.Value = 0
End Sub
End Class
From my understanding, when the button "Open" is pressed, it calls the function Install and it should start this off in a separate thread, however this doesn't work.
This seems to work, but the GUI still locks up, suggesting its not in a separate thread
I recommend using the BackgroundWorker Class to implement this kind of basic threading. You don't have a lot going on here so the basic implementation should suffice. Here is a snippet of how I would go about doing it. My method is a bit too complex (I have base classes and events wired up to catch a lot of worker events) to list here succinctly, so I'll abbreviate.
Public Class MAIR
Dim worker as BackgroundWorker
Private Sub Open_Click(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles Open.Click
worker.RunWorkerAsync()
End Sub
Protected Sub Worker_DoWork(ByVal sender as Object, ByVal e as DoWorkEventArgs)
Call Shell("psexec.exe", AppWinStyle.Hide, True)
End Sub
Protected Sub Worker_ProgressChanged(Byval sender as Object, ByVal e as ProgressChangedEventArgs)
' You can track progress reports here if you use them
End Sub
Protected Sub Worker_RunWorkerCompleted(Byval sender as Object, ByVal e as RunWorkerCompletedEventArgs)
' Report back to the main application that the thread is completed
End Sub
Private Sub Install_Load(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles MyBase.Load
worker = New BackgroundWorker()
' Add the event wire-ups
' ### This event wire-up establishes the link
' ### that is used by RunWorkerAsync to initiate
' ### the code you want to run asynchronously
AddHandler worker.DoWork, AddressOf Worker_DoWork
AddHandler worker.ProgressChanged, AddressOf Worker_ProgressChanged
AddHandler worker.RunWorkerCompleted, AddressOf Worker_RunWorkerCompleted
End Sub
End Class
This is all translated from C#, so treat it as psuedo-code. The important piece is the documentation of the BackgroundWorker and its behaviors. There is a lot of terrific functionality in this class that takes the headaches of threads away for simple usages. This class is located in System.ComponentModel so you'll need the reference and an Imports statement.
Edit: Something I forgot to mention is that once the worker is fired asynchronously, the only manner of tracking it or communicating with the main application is through the ProgressChanged and RunWorkerCompleted events. No global variables or cross-thread items will be available, so you'll have to use the built-in properties to pass in any values (such as computerName it looks like) that you may need during the run.

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