referencing form button from another form - vb.net

I have two forms both with the same buttons on, and I want to have it so that if I click the button both buttons will do the same thing i.e. they are referencing each other on different forms. the way i found was:
Public Class Form2
Dim form1 As New form1
Private Sub Button2_Click
form1.backcolor=black
form2.backcolor=black
end sub
end class
then
Public Class Form1
Dim form2 As New form2
Private Sub Button1_Click
form1.backcolor=black
form2.backcolor=black
end sub
end class
only this doesn't work as there is an error:An unhandled exception of type 'System.StackOverflowException' occurred in System.Windows.Forms.dll as far as i can see there is no infinite loop or stack over flow.
any help would be greatly appreciated.

You have an infinite loop, because each time one of the forms is instantiated, it is instantiating the other. Creating a Form1 will create a Form2, then Form2 immediately creates another Form1 and so on and so on...
Change your code to this:
Public Class Form2
Private Sub Button2_Click
Dim form1 As New Form1
form1.backcolor=black
form2.backcolor=black
End sub
End class
Public Class Form1
Private Sub Button1_Click
form1.backcolor=black
Dim form2 As New Form2
form2.backcolor=black
End sub
End class
Now it will only create the other class instances when you click a button.

Like Karl Anderson said, there is a infinite loop in your code. His solution will create a new form every time you click the button. If you don't want this behavior, I think that the best approach is to use the mediator pattern. And it will be much more easy if you want to add new actions and new forms.
The code will look something like this:
Public Class Mediator
Private forms As New List(Of BaseForm)
Public Sub RegisterForm(form As BaseForm)
forms.Add(form)
End Sub
Public Sub ChangeAllFormsBackColorToBlack()
For Each form In forms
form.ChangeBackColorToBlack()
Next
End Sub
End Class
Public Class BaseForm
Private med As Mediator
Public Sub New(med As Mediator)
Me.med = med
Me.med.RegisterForm(Me)
End Sub
Public Sub ChangeBackColorToBlack()
backcolor = black
End Sub
Public Sub OnButtonClick()
Me.med.ChangeAllFormsBackColorToBlack()
End Sub
End Class
Public Class Form2
Inherits BaseForm
Public Sub New(med As Mediator)
MyBase.New(med)
End Sub
Private Sub Button2_Click()
Me.OnButtonClick()
End Sub
End Class
Public Class Form1
Inherits BaseForm
Public Sub New(med As Mediator)
MyBase.New(med)
End Sub
Private Sub Button1_Click()
Me.OnButtonClick()
End Sub
End Class
Module MediatorDemo
Sub Main()
Dim med As New Mediator
Dim f1 As New Form1(med)
Dim f2 As New Form2(med)
f1.OnButtonClick()
End Sub
End Module

Related

How to update label in Form1 from Class?

I'm trying to update Label which is in Form1 obviously and it count good proxies, if there is a exception thrown it count bad proxies.
The Sub which invoke this labels are in Form1, the method is called from Class1 so I declared Frm as Form1 but it doesn't invoke and doesn't update labels =+1
I read many similar examples here on forum but I'm not sure what is the solution for me, I want to keep the code for proxies in Class1 if I move it to the Form1 then it works btw.
My Class1:
Imports System.Threading
Public Class Class1
Public Shared Property Frm As Form1
Public Shared _Count As Integer
Public Shared Sub GetProxies()
While True
Try
'MY CODE
Interlocked.Increment(_Count)
Frm.GoodProxyCount()
Catch ex As Exception
Frm.OnBadRequestChanged()
End Try
End While
Properties._runningThreads.Remove(Thread.CurrentThread)
End Sub
End Class
My Form1 Sub methods:
Public Class Form1
Public Sub OnBadRequestChanged()
Try
If Me.lblProxies.Text > 0 Then
Me.Invoke(Sub() Me.lblProxies.Text -= 1)
Else
If Not Me.chkManualProxies.Checked Then
Me.btnScrapeStart()
End If
End If
Catch ex As Exception
End Try
End Sub
Public Sub GoodProxyCount()
Try
Me.Invoke(Sub() Me.lbl_count.Text = Class1._Count.ToString())
If Me.lblProxies.Text > 0 Then
Me.Invoke(Sub() Me.lblProxies.Text -= 1)
Else
If Not Me.chkManualProxies.Checked Then
Me.btnScrapeStart()
End If
End If
Catch ex As Exception
End Try
End Sub
End Class
I have the same problem with anything I try to update like frm.checkbox.checked or other radiobuttons impossible to update or populate listview from outside of the Form1 so I don't know what am I missing here some declaration or property??
thanks for help
Here is the answer for someone who struggle with the same issue so do follow:
Add this declaration line in the top of your Form1 Class:
Public Shared instance As Form1
Declare a Label or CheckBox etc.. what you wish to 'invoke or access' from other class or forms as follows:
Public txt_Title As Label
Note that you have to give other name as the real name what is in your Form1, for example my TITLE LABEL is called: txtTitle but I had to change the name something I remember and similar in this declaration.
Create Sub in Form1 as Follows:
Public Sub Instances()
instance = Me
txt_Title = txtTitle
End Sub
Initialize the Sub in Form1_Load as follows:
Private Sub Form1_Load(sender As Object, e As EventArgs) Handles MyBase.Load
Try
Instances()
Catch ex As Exception
End Try
End Sub
NOW from the Class1 just call any Label or elements you declared in Form1 as follows:
Form1.instance.txt_Title .Text = "me desired text from Class1"
I hope it helps someone as I struggle like 2 days on this since I'm self educated coder and I code for fun I could not known this as this is for someone basics from school btw..
Assuming Class1 still has the Shared variable to reference a Form1:
Public Class Class1
Public Shared Property Frm As Form1
End Class
You'd just do, in Form1:
Private Sub Form1_Load(sender As Object, e As EventArgs) Handles MyBase.Load
Class1.Frm = Me
End Sub
Now Class1 has the correct reference to the instance of Form1 that was actually displayed on the screen and it can access all the members of it, such as:
' ... in Class1 ...
Frm.Label1.Text = "Hello from Class1"
You don't need a separate shared variable for Label1 above to be able to access it.

Multi-Threading (Calling Sub From WorkerThread)

Need to call a sub that is coded written inside the block of form1 form an external worker thread. This is what I have written:
In Form1:
Public Delegate Sub UpdateControlDelegate(ByVal C As Label, ByVal txt As String)
Private Sub UpdateControl(ByVal C As Label, ByVal txt As String)
If C.InvokeRequired Then
C.Invoke(New UpdateControlDelegate(AddressOf UpdateControl), New Object() {C, txt})
Else
C.Text = txt
End If
End Sub
Public Sub DoStuff()
'we do some stuff then when it comnes time update a certain control:
Call UpdateControl(MyLabel, "My Text For The Label)
End Sub
In The workerThread that is located in a class:
Public Class MyClass
Public Sub UpdateData
Call Form1.DoStuff
End Sub
End Class
Does this look correct? The most simplest terms on what I am trying to achieve:
WorkerThread to call a Sub that is located in Class Form1
and that sub contains code that updates a couple controls in Form1.
After doing a little more research. I have figured it out. The initial code I have written is correct. The only thing missing is a reference to the form I need to update.
Here is the COMPLETE solution when needing to run a SUB from the UI that is called from the Worker Thread:
Public Class MyClass
'working thread is being within the subs of this class
Public MyForm1111 As Form1 '<------ The variable in this class that will reference to the form1 that we need
Public Sub MySubThatIsOnAWorkerThread
MyForm1111.DoStuff '<==== must call MyForm1111.DoStuff and NOT Form1.DoStuff
End Sub
End Class
The Sub Located In Form1:
Public Class Form1
Public Delegate Sub UpdateControlDelegate(ByVal C As Label, ByVal txt As String) 'Required Delegate
Private Sub UpdateControl(ByVal C As Label, ByVal txt As String) 'Sub to update controls
If C.InvokeRequired Then
C.Invoke(New UpdateControlDelegate(AddressOf UpdateControl), New Object() {C, txt})
Else
C.Text = txt
End If
End Sub
Public Sub DoStuff() 'the sub we need to call from the worker thread
'do some calculations and code
Call UpdateControl(MyLabel, "Some Text For Label")
End Sub
Private Sub Form1_Load()
MyClass.MyForm1111 = Me <==== Set the reference here in your Form1_Load
End Sub
End Class

Blank ListView when calling Invoke

listView is owned by the class Form1. The subroutine anotherThread in a separate class transmission is started in a thread by a subroutine in Form1. Form1 owns another public subroutine addItemsListView, which uses Invoke.
When transmission.anotherThread calls addItemsListView, the subroutine runs, but listView remains blank.
Have tried delegates, invokes etc. inside each class but the same problem.
Class Form1
Property myTransmission = New transmission
Private Sub aSubRoutine() Handles MyBase.Load
Dim t As New Threading.Thread(
Sub()
myTransmission.anotherThread()
End Sub
)
t.Start()
End Sub
Public Sub addItemsListView(ByVal items As String())
If listView.InvokeRequired Then
listView.Invoke(Sub() addItemsListView(items))
Else
For each item In Items
listView.Items.Add(item)
Next
End If
End Sub
End Class
Class transmission
Public Sub anotherThread()
Form1.addItemsListView(New String() {"abc", "def"})
End Sub
End Class
So I expect "abc" and "def" to be in the listView but it remains completely blank. If I step through the code, however, everything seems to be running smoothly.
You aren't talking to your existing form. Pass it as a reference:
Public Sub anotherThread(inForm As Form1)
inForm.addItemsListView(New String() {"abc", "def"})
End Sub
then include your form when you call it:
myTransmission.anotherThread(Me)

Access a base class property in inheritance class

I'm using the base class Button in VB.net (VS2017) to create a new class called CDeviceButton. The CDeviceButton then forms as a base for other classes such as CMotorButton, CValveButton.
I want to set the Tag property in the child class CMotorButton but access it in the constructor in CDeviceButton. Doesn't work for me. It turns up being empty.
The Tag is set in the standard property when inserting the CMotorButtom instance into a form.
I've also tried to ensure teh the parent classes' constructors are run by setting mybase.New() as the first action in each constructor but that didn't change anything.
Any ideas for improvements?
Public Class CDeviceButton
Inherits Button
Public MMIControl As String = "MMIC"
Public Sub New()
MMIControl = "MMIC" & Tag
End Sub
End class
Public Class CMotorButton
Inherits CDeviceButton
Sub New()
'Do Something
end Sub
End Class
When you try to concatenate Tag with a string, you are trying to add an object that is probably nothing. I set the Tag property first and used .ToString and it seems to work.
Public Class MyButton
Inherits Button
Public Property MyCustomTag As String
Public Sub New()
'Using an existing Property of Button
Tag = "My Message"
'Using a property you have added to the class
MyCustomTag = "Message from MyCustomTag property : " & Tag.ToString
End Sub
End Class
Public Class MyInheritedButton
Inherits MyButton
Public Sub New()
If CStr(Tag) = "My Message" Then
Debug.Print("Accessed Tag property from MyInheritedButton")
Debug.Print(MyCustomTag)
End If
End Sub
End Class
And then in the Form
Private Sub Test()
Dim aButton As New MyInheritedButton
MessageBox.Show(aButton.Tag.ToString)
MessageBox.Show(aButton.MyCustomTag)
End Sub
Below is my solution I came up with that works. Basically I make sure that all initialization has taken place before reading the Tag property. What I experienced is that the Tag property is empty until the New() in CMotorButton has completed, even though the Tag property has been set when creating the instance of CMotorButton in the Form. TimerInitate has a Tick Time of 500 ms.
Not the most professional solution but works for what I need at the moment.
Another option could be multi threading but that I haven't tried and leave that for future tryouts.
Public Class CDeviceButton
Inherits Button
Public MMIControl As String = "MMIC"
Public Sub New()
TimerInitiate = New Timer(Me)
End Sub
Private Sub TimerInitiate_Tick(sender As Object, e As EventArgs) Handles TimerInitiate.Tick
If Tag <> Nothing Then
TimerInitiate.Stop()
MMIControl = "MMIC" & Tag
End If
End Sub
End class
Public Class CMotorButton
Inherits CDeviceButton
Sub New()
'Do Some stuff
TimerInitiate.Start()
End Sub
Private Sub CMotorButton_Click(sender As Object, e As EventArgs) Handles Me.Click
End Class

Handling a timer from a class

I want some values in a class to decrease whenever the timer in the main form ticks. I am creating multiple instances of the same class as my program is a simulation application and I am not storing these instances in an array or any list in that matter. I simply declare them and add their picture box to the controls on the main form. However I am hoping to have a sub routine inside the class that triggers whenever the timer in the main form ticks. I thought of something like this:
Public Class Jimmy
Dim _a As Integer = 10
Sub decreseNum(sender As Object, e As EventArgs) Handles mainapp.tmrLog.Tick
_a -= 1
End Sub
End Class
with mainapp being the name of the main form and tmrLog being the timer I want to associate my sub routine with. However the above code doesn't work
You could try defining a local reference to the timer in the Jimmy class:
Public Class Jimmy
Dim _a As Integer = 10
Private WithEvents tmr As Timer
Public Sub New(ByRef MainTmr As Timer)
tmr = MainTmr
End Sub
Sub decreseNum(sender As Object, e As EventArgs) Handles tmr.Tick
_a -= 1
End Sub
End Class
If you want all your classes react to timer.elapsed event, just sign up for it. The program below is fully operational. It is example what you can do to have your children to react to timer events of single parent/timer
Imports System
imports system.timers
Public Module Module1
Public Sub Main()
dim mc as new MainClass()
mc.CreateChildren(5)
System.Threading.Thread.Sleep(60000) ' wait and monitor output of childern
mc.Stop()
Console.WriteLine("All should stop now...")
Console.Read()
End Sub
End Module
public class MainClass 'This class could be your form
private _timer as new Timer(5000)
public sub CreateChildren(count as integer)
For i as integer = 1 to count
dim c as new Child(i)
Addhandler _timer.Elapsed, addressof c.DoWhentimerTicks
next
Console.WriteLine("timer should run now...")
_timer.Start()
end sub
public sub [Stop]()
_timer.Stop()
End Sub
End class
public class Child
private _myNO as integer
public sub new (no as integer)
_myNo = no
end sub
public sub DoWhentimerTicks(sender as object , e as ElapsedEventArgs)
Console.WriteLine(string.format("Child #{0} just ticked. Time = {1}", _myNo, e.signaltime))
end sub
End class
I found my solution, posting here for further reference.
My situation was trying to have my timer in the mainform triggering a sub in a class, and I used the following solution.
Class:
Sub addHandlesToSub
AddHandler Form1.Timer1.Tick, AddressOf subToBeTriggered
End Sub
Sub subToBeTriggered(sender As Object, e As EventArgs)
'My code
End Sub
The parameters in subToBeTriggered are useful when you want to remove the handler with
RemoveHandler Form1.Timer1.Tick, AddressOf subToBeTriggered
Otherwise, there will be an error without the parameters.
Thanks for all the answers though.