I am making a game with score but i cant send the score from the first form to the second one
The score start with 1000
When the user play it decrease
How to send the value after it decrease to a label in form2
Without any code to see - I would guess that your issue is that forms don't have a reference of some sort to each other.
I would suggest making a module (and put it in a Namespace):
Namespace MyModule
Module Module1
Public f1 as Form1
Public f2 as Form2
Public Sub setScore() as String
' This assumes Form1 has a public variable playerScore,
' and Form2 has a label scoreLabel
f2.scoreLabel.Text = f1.playerScore
End Sub
End Module
End Namespace
Second step to this is to make sure in both of your forms, you set the references in the module to that form (example below for Form1 done in the Load event). Be sure that both f1 and f2 have been defined, otherwise you will get a null reference exception when you try to call setScore().
Private Sub Form1_Load(sender As Object, e As EventArgs) Handles MyBase.Load
MyModule.f1 = Me
' ... and the rest of your code in form load
End Sub
Lastly, when your score changes, just call the setScore() sub in MyModule
playerScore += 1 'example
MyModule.setScore() ' update the label in Form2
Related
The user enters data in Form1 for example his name and phone number, he clicks on the "next" button that opens Form2 and Hide Form1, if he clicks on the "back" button I want the program to show Form1 with the data he entered before
The code for the "Next" Button in Form1:
Private Sub NextButton_Click(sender As Object, e As EventArgs) Handles NextButton.Click
Dim MyForm As New Form2
MyForm.Show()
Me.Hide()
End Sub
What should I do in order to keep the data the user entered if he comes back to Form1?
The code for the "Previous" button in Form2:
Private Sub Button1_Click(sender As Object, e As EventArgs) Handles Button1.Click
Form1.Show()
Me.Close()
End Sub
One approach you could use is to only the use the DEFAULT INSTANCES of your Forms. These are accessed by using only the name of the Form, WITHOUT ever using the "New" keyword.
You're doing exactly that in Form2 when you show Form1 with:
Form1.Show()
This works because Form1 is your startup object and VB.Net used the default instance to start the application.
You could do the same thing in Form1, to show Form2:
' ... code in Form1 ...
Private Sub NextButton_Click(sender As Object, e As EventArgs) Handles NextButton.Click
Form2.Show() ' show the default instance of Form2 (do NOT use the "new" keyword)
Me.Hide()
End Sub
Just make sure in all places you Hide() the current form instead of closing it.
You can access the properties/fields of the default instances using the same syntax to show them, using only their name. For example, here is a made up value being accessed on Form2:
Dim userName As String = Form2.txtAddress1.Text
This assumes that Form2 was previously displayed and populated by the user. You could access the Forms from anywhere in the code using this type of syntax.
If you want to implement your own "default instance" mechanism, you could use Shared members in a class:
Public Class WizardForms
Public Shared F1 As New Form1
Public Shared F2 As New Form2
Public Shared F3 As New Form3
End Class
Then you could use code like this to display one:
WizardForms.F2.Show()
This would work as long as you are only HIDING the forms. If you allow the user to close the Forms (or close them via code), then you'd need extra code to make sure they get recreated as needed.
hello I have a problem to update a progress bar and a label inside a StatusStrip in the main form.
there are 2 controls in the form inside a StatusStrip:
Progressbar (ToolStripProgressBar)
ProgressLabel (ToolStripStatusLabel)
Basically I have this situation:
Public Class Main
Public Sub TEST(ByVal sender As Object, ByVal e As DoWorkEventArgs) Handles TEST.DoWork
Dim tmp as New NAMESPACE1.CLASS2(VALUES)
End Sub
End Class
Namespace NAMESPACE1
Public Class CLASS2
Public Sub New(VALUES)
Main.Progressbar.Value = 15
Main.ProgressLabel.Text = "hello!"
End Sub
End Class
End Namespace
The problem is that text or value of the controls are updated (I see it using breakpoints) in the code but not in the form in which progressbar is always a 0% and label always as nothing.
I think it's an update or refresh problem of the main form. i have tried to do Main.Refresh() and Main.Update() but it does not work anyway.
Thanks in advance.
You have 2 issues in play. The first is that Main is a class name, not a runtime reference or object variable. See Idle_Mind's answer for using Me to get the runtime object reference.
The second problem is that since Class2 is created in DoWork, it is created on the background thread, which will prevent it from accessing UI controls (which are created on the UI thread). You will get an illegal cross thread operation exception (even if you dont see it).
I'd suggest that Class2 does nothing useful which can't be done using the ReportProgress method. Getting rid of it also gets rid of the form reference issue since an event is raised on the same thread as the UI controls:
Private WithEvents bgw As BackgroundWorker
...
' in a button click or whatever starts the worker:
bgw = New BackgroundWorker
bgw.WorkerReportsProgress = True
bgw.RunWorkerAsync(5) ' times to loop
...
Private Sub bgw_DoWork(sender As Object,
e As DoWorkEventArgs) Handles bgw.DoWork
' NOTE
' This code executes on a different thread
' so do not reference UI controls!
' e.Argument is the value passed - amount of work
Dim max As Integer = CInt(e.Argument)
For n As Integer = 1 To max
Threading.Thread.Sleep(250) ' emulates work
' causes the ProgressChanged event to fire:
bgw.ReportProgress(n, String.Format("{0} of {1}", n.ToString, max.ToString))
Next
End Sub
Private Sub bgw_ProgressChanged(sender As Object,
e As ProgressChangedEventArgs) Handles bgw.ProgressChanged
'ProgressChanged fires on the UI thread, so it is safe to
' referenece controls here
TextBox4.Text = e.UserState.ToString
TextBox4.Refresh()
End Sub
Paste the code and you can see the message change in the TextBox. The same would work using your ProgressBar and ProgressLabel.
bgw.ReportProgress(n, arg)
The first argument will map to e.ProgressPercentage in the ProgressChanged event. The second is optional - UserState. I used it to pass a string for illustrative purposes (the form can already know the amount of work since it told the BGW what to do.)
If Class2 has some other purpose, you can use it as long as it is created on the UI thread (in the form) and used on that thread (ie in ProgressChanged event). You also need a method to talk to the controls so you dont have to create a new one each time:
Private myObj As Class2 ' declaration
...
myObj = New Class2(Me) ' instance with frm ref
In class2:
Public Sub Update(value As Integer, msg As String)
frmMain.Progressbar.Value = value
frmMain.ProgressLabel.Text = msg
End Sub
Then in the ProgressChanged event:
myObj.Update(x, y)
Where x and y are the value and message from whereever.
Here's an example of passing a reference to MAIN as suggested by Plutonix. I've intentionally left your pseudo-code style intact:
Public Class MAIN
Private Sub Button1_Click(sender As Object, e As EventArgs) Handles Button1.Click
TEST.RunWorkerAsync()
End Sub
Private Sub TEST_DoWork(sender As Object, e As System.ComponentModel.DoWorkEventArgs) Handles TEST.DoWork
Dim tmp As New NAMESPACE1.CLASS2(Me, VALUES) ' <-- Form reference being passed via 1st parameter
End Sub
End Class
Namespace NAMESPACE1
Public Class CLASS2
Private frmMain As MAIN
Public Sub New(ByVal frmMain As MAIN, VALUES)
Me.frmMain = frmMain
Me.frmMain.Progressbar.Value = 15
Me.frmMain.ProgressLabel.Text = "hello!"
End Sub
End Class
End Namespace
i have my form class, and a second module with some special functions,
when i click a button on my form i run a public function from the second module(which run later other public functions from the second module) in a separated thread, i set SetApartmentState(ApartmentState.STA), and i tried using deletage sub and CheckForIllegalCrossThreadCalls = False, but the problem stays the same, my thread functions (which are on the second module) can't access my form controls, but when i move the functions to the form class everything work again, what do you suggest to solve this issue?
Public Class Form1
Dim T0 As Thread
Private Sub Form1_Load(sender As Object, e As EventArgs) Handles MyBase.Load
CheckForIllegalCrossThreadCalls = False
End Sub
Private Sub Button1_Click(sender As Object, e As EventArgs) Handles Button1.Click
T0 = New Thread(AddressOf sub1)
T0.SetApartmentState(ApartmentState.STA)
T0.start()
End Sub
End Class
Module Module1
Public Sub Sub1()
msgbox(form1.textbox1.text) 'even if the textbox contains content it returns ""
Function2()
End Sub
Public Function Function1()
'SomeInstructions
msgbox(form1.textbox1.text) 'same problem here
End Function
End module
PS: it dosn't give any error or stop the code while debugging, and i tried to put the sub1 on the form class and the other functions in the module, but then, only he sub1 can access the controls, i tried delegate but i don't know if i have done it right, can anyone make any suggestions please
There are two issues at play here.
One of them is due to the way default forms work in VB.NET. See, in C# you need a concrete instance of type Form1 in order to access its non-static members, whereas in VB.NET Form1 looks like an instance of the form allowing you to write things like Form1.TextBox1.Text. This works fine while you're accessing members of Form1 from the UI thread, but when you try to get it from a background thread, a new instance of Form1 is created, and Form1.TextBox1 seen by that thread actually points to a completely different instance of TextBox.
Another way saying this is that default form instances in VB.NET are thread static.
A way to get around this is to keep a concrete reference to Form1 so that you can pass it around.
When you go
Dim myForm As New Form1
... or
Dim myForm As Form1 = Me
... the 'myForm' variable pointing to that specific Form1 instance can then be passed between threads like any other reference and will not change its meaning.
This, however, brings us to issue #2:
You should not be accessing a UI control (which means any type derived from Control, and that includes Form , from a thread other than the thread that it was created on.
If you absolutely have to, you have to marshal the calls accessing the Control to the thread that it was created on, for example by using Invoke/BeginInvoke (there are other ways too).
Here's a modification of your code to achieve what you want, plus a more complex example which demonstrates multiple thread "switches": gathering interesting state on the UI thread, performing work with it on the background thread, then displaying results on the UI thread.
Imports System.Threading
Public Class Form1
Private Sub Form1_Load(sender As Object, e As EventArgs) Handles MyBase.Load
' We don't need this anymore.
' We'll do things right and access
' the UI on the UI thread only.
' CheckForIllegalCrossThreadCalls = False
End Sub
Private Sub Button1_Click(sender As Object, e As EventArgs) Handles Button1.Click
' Note that we're passing a reference
' to THIS instance of Form1 to Sub1 and Sub2.
Dim form As Form1 = Me
' Let's spin up some threads.
Dim T0 As New Thread(Sub() Module1.Sub1(form))
T0.Start()
Dim T1 As New Thread(Sub() Module1.Sub2(form))
T1.Start()
End Sub
End Class
Module Module1
' Note that this sub now accepts
' a reference to an instance of Form1.
Public Sub Sub1(form As Form1)
' This is what we want to do:
Dim action As New Action(Sub() MsgBox(form.TextBox1.Text))
' See if we're on the right thread.
If form.InvokeRequired Then
' Invoke on the thread which created this Form1 instance.
form.Invoke(action)
Else
' Invoke on the current thread.
action.Invoke()
End If
End Sub
' This is a more complex example.
Public Sub Sub2(form As Form1)
' This function will get the text from TextBox1 when invoked.
' It still needs to be invoked on the UI thread though.
Dim getText As New Func(Of String)(Function() form.TextBox1.Text)
Dim text As String
If form.InvokeRequired Then
text = CStr(form.Invoke(getText))
Else
text = getText() ' Shorthand syntax.
End If
' Now that we have the text, let's do some
' intensive work with it while we're on
' the background thread.
For i = 0 To 5
text &= i
Thread.Sleep(100)
Next
' Now we want to show the message box - again, on the UI thread.
Dim showMessageBox As New Action(Sub() MsgBox(text))
If form.InvokeRequired Then
form.Invoke(showMessageBox)
Else
showMessageBox()
End If
End Sub
End Module
In visual basic I have a label set to increment every time a button is clicked. However when I close and reopen the form the label goes back to default value. Is there a way to keep this label the same it was before I closed the form? Thanks
When you close the form instance every local object kept by the form is disposed (destroyed). When you show again the form a new instance of the form class is created but the objects are all initialized to their default values. So you have lost your current value.
A possible workaround is to have a Shared variable inside the form class that keeps the count.
Shared variables are not destroyed with the class instance but are available for every instance of the class with their current value
You use this value to initialize your label in the form constructor with the current value of your clicks
Public Class Form1
Private Shared clickCount As Integer
Public Sub New()
InitializeComponent()
myLabel.Text = Convert.ToString(clickCount)
End Sub
' you could also use Form1_Load event if you prefer
' Private Sub Form1_Load(sender As Object, e As EventArgs) Handles MyBase.Load
' Label1.Text = Convert.ToString(clicksCount)
' End Sub
Private Sub Button1_Click(sender As Object, e As EventArgs) Handles Button1.Click
clicksCount += 1
myLabel.Text = Convert.ToString(clicksCount)
End Sub
End Class
Keep in mind that this approach use the same variable (clickCount) for every instance of Form1 you create.
I've got a fairly simple question (I think) on passing variables between forms using Visual Basic.
I've got a program with 2 forms (Form1 and Form2). Form1 has 3 radio buttons, which the user has to select one of and then loads Form2.
Now I've made it so that if radiobutton1 is picked, the Public Variable "radio_select" will equal "radiobutton1", if radiobutton2 is picked, "radio_select" will equal "radiobutton2".
But whenever I try call "radio_select" in my second form, it comes up blank. Why could this be? And how can I fix it.
I've tried using if form1.radiobutton1.checked = true but I keep getting the first radiobutton, regardless of the radio button I've selected.
I think the form is being unloaded, or there is an issue somewhere there, as it appears none of the variables get passed to the second form, once it has been initialized. Also note, the first form is hidden Me.Hide() when the second form is called.
Have you considered a slight re-design whereby you create a property on Form2 called RadioSelect and then set this from Form1 before showing Form2:
Class Form2
Public Property RadioSelect As String
...
End Class
...
Dim f2 as new Form2()
f2.RadioSelect = "radiobutton2"
f2.Show() ' Or f2.ShowDialog()
This gets you away from an unnecessary public variable and should also ensure Form2 can see what it needs from Form1, or whoever calls it.
Edit:
The following works for me:
Public Class Form1
Public Test As String
Private Sub Button1_Click(sender As Object, e As System.EventArgs) Handles Button1.Click
Test = "I'm Here"
Me.Hide()
Form2.ShowDialog()
End Sub
End Class
Public Class Form2
Private Sub Form2_Load(sender As System.Object, e As System.EventArgs) Handles MyBase.Load
Text = Form1.Test
End Sub
End Class