I'm doing a scoreboard using labels for scores, but the number doesn't keep on adding, it only add once. what could be the problem?
Public Class Form2
Private Sub Button1_Click(sender As Object, e As EventArgs) Handles Button1.Click
Dim a As Integer
a = a + 1
Label1.Text = a
End Sub
End Class
The variable a is local to the button_click event. So every time you call this event clicking your button the variable is allocated on the stack and has its initial value set to the default for integer which is zero
If you want to keep a counter you need to declare the variable at the global level where its scope is the set of all code in your form
Public Class Form2
Dim a As Integer
Private Sub Button1_Click(sender As Object, e As EventArgs) Handles Button1.Click
a = a + 1
Label1.Text = a
End Sub
End Class
More details on lifetime and scope of variables could be read in this MSDN article: Scope in Visual Basic and Lifetime, Scope, Visibility, and Linkage. Read it carefully because a deep understanding of these issues is fundamental to learn programming.
Related
I want to invoke a method every time a value from My.Settings is changed. Something like:
Private Sub myValue_Changed(sender As Object, e As EventArgs) Handles myValue.Changed
(...)
End Sub
I know that, if I wanted to do it with a variable, I have to make it a class and set the event on it. But I canĀ“t do it with the value from My.Settings.
Is there any way to do this?
As suggested in the comments on another answer, you can receive notification of a change in a setting via a Binding. Alternatively, you can do essentially what the Binding class does yourself, as there's not really all that much to it, e.g.
Private Sub Form1_Load(sender As Object, e As EventArgs) Handles MyBase.Load
Dim settingsPropertyDescriptors = TypeDescriptor.GetProperties(My.Settings)
Dim setting1PropertyDescriptor = settingsPropertyDescriptors(NameOf(My.Settings.Setting1))
setting1PropertyDescriptor.AddValueChanged(My.Settings, AddressOf Settings_Setting1Changed)
End Sub
Private Sub Button1_Click(sender As Object, e As EventArgs) Handles Button1.Click
My.Settings.Setting1 = "Hello World"
End Sub
Private Sub Settings_Setting1Changed(sender As Object, e As EventArgs)
Debug.WriteLine($"{NameOf(My.Settings.Setting1)} changed to ""{My.Settings.Setting1}""")
End Sub
This code adds a changed handler to the property via a PropertyDescriptor, just as the Binding class does.
In a word: no. My.Settings doesn't support this on it's own.
What you can do is make your own class that wraps My.Settings. As long as you use this new class, and never go to My.Settings directly any more, then you can put an event on that class which will do what you need.
However, even here, there's no way to enforce the use of the new class, and prevent direct access to My.Settings.
Are you looking for something like this? ApplicationSettingsBase.SettingChanging Event
Partial Friend NotInheritable Class MySettings
Inherits Configuration.ApplicationSettingsBase
Private Sub MySettings_SettingChanging(sender As Object, e As System.Configuration.SettingChangingEventArgs) Handles Me.SettingChanging
If e.SettingName.Equals(NameOf(My.Settings.Setting1)) Then
'Do Stuff
End If
End Sub
End Class
So i am currently in process of adding a basic calculator that allows the user to define 2 variables and then press a button that puts the variables into a basic math equation and presents the result but i think i have gone about it completely wrong.
this is my first time using math functions in VB and would appreciate it if someone can show me where im going wrong.
this is my code so far:
Imports System.Math
Public Class SOGACALC
Dim soga As String = Math.Abs(72 - months.Text) * opp.Text
Private Sub SOGACALC_Load(sender As Object, e As EventArgs) Handles MyBase.Load
End Sub
Private Sub Button1_Click(sender As Object, e As EventArgs) Handles Button1.Click
SOGAValue.Text = soga
End Sub
Private Sub Button2_Click(sender As Object, e As EventArgs) Handles Button2.Click
HOME.Show()
Me.Close()
End Sub
End Class
Where you have written
Dim soga As String = Math.Abs(72 - months.Text) * opp.Text
I suspect that you are anticipating that soga will be a function of the properties referred to in that and will change when those properties change. It does not work that way.
The way to get a value which varies depending on its parameters is to define a function, so you might have:
Friend Function Soga(monthValue As Control, oppThing As Control) As String
Dim month As Integer = CInt(monthValue.Text)
Dim opp As Decimal = CDec(oppThing.Text)
Return (Math.Abs(72 - month) * opp).ToString()
End Function
and call it like:
'TODO: Give Button1 a meaningful name.
Private Sub Button1_Click(sender As Object, e As EventArgs) Handles Button1.Click
SOGAValue.Text = Soga(months, opp)
End Sub
where there are controls names "months" and "opp" on the form.
I strongly recommnend that you use Option Strict On - it points out problems in code and suggests corrections for you.
Notice that I used the Decimal type for opp - I had to guess at a suitable type because nowhere in the code you showed us was there any indication of what type it needs to be.
An improvement would be to use TryParse methods instead of CInt/CDec, so that you can inform the user if they have made a simple typing error.
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 have some code where I am trying to use variables in a tabpage. The first tabpage only has one text box for user entry (miles.text) and a button to do a calculation: traveltime = mileage/speed. The value from miles.text is stored into a variable called mileage while the speed used is stored in a variable called speed (me.speedtextbox.text).
Ordinarily, doing val(variable.text) works like a charm and it's not doing it in this case. When the user enters 100 for the mileage, it should be divided by 65 (the number in the database) and, therefore, the answer should be 1.53 hours. In my case, I'm getting "infinity" and whenever I do anything else with the variable, I get "when casting from a number, the value must be a number less than infinity." But it is! It's only 65 and I double-checked that the dataset said that too, which it does. Not sure why I am getting this error...thank you!
Public Class Form1
Private Property Traveltime As Decimal
Private Sub Form1_Load(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles MyBase.Load
'TODO: This line of code loads data into the 'fooDataSet.testdata' table. You can move, or remove it, as needed.
Me.TestdataTableAdapter.Fill(Me.foouDataSet.testdata)
End Sub
Private Sub Button1_Click(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles Button1.Click
Dim mileage As Integer
Dim speed As Integer
mileage = Val(miles.Text)
speed = Val(Me.SpeedTextBox.Text)
traveltime = mileage / speed
txttraveltime.text = Traveltime.ToString
End Sub
Private Sub txtrate_TextChanged(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles txttraveltime.TextChanged
End Sub
End Class
So I did a test program where it did only one thing and that was to simply read one data column in a one row database and store it to a local variable and multiply it by 1.60 except now I am getting "reference to a non-shared member requires an object reference" and it doesn't seem to recognize Me.Speed when I declare it. What am I doing wrong?
Public Class Form1
Dim Speed As Object
Dim Me.Speed As New Speed
Private Sub Button1_Click(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles Button1.Click
Me.Speed = CDec(fooDataSet.testdataRow.Item("speed"))*1.60
Speedtextbox.text = Me.Speed.tostring
End Sub
Private Sub Form1_Load(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles MyBase.Load
'TODO: This line of code loads data into the 'fooDataSet.testdata' table. You can move, or remove it, as needed.
Me.TestdataTableAdapter.Fill(Me.fooDataSet.testdata)
End Sub
End Class
Before you do anything else, you should do the following:
Open the project's properties (right-click on the Project, then select Properties)
Click on the Compile tab (left-hand side)
Select All Configurations from the dropdown menu
Select On from the Option Explicit menu.
Select On from the Option Strict menu.
Save the project
This will more than likely cause a lot of errors to be displayed, but fixing these errors will substantially improve your application's health.
Now, that that is done, the following code will fix the problems in the button click:
Dim mileage As Integer
Dim speed As Integer
If IsNumeric(Me.Miles.Text) Then
mileage = CInt(Me.Miles.Text)
End If
If IsNumeric(Me.SpeedTextBox.Text) Then
speed = CInt(Me.SpeedTextBox.Text)
End If
If speed <> 0 Then
Traveltime = CDec(mileage / speed)
Else
Traveltime = 0
End If
txtTravelTime.Text = Traveltime.ToString
However, the code as you have it will produce correct results, so there must be something else amiss. Try the above first and if there are still issues, you can update your question with the details.
I would implement the calculation in a separate class and then use object-binding. Here is how the travel time calculator would look like:
Imports System.ComponentModel
Public Class TraveltimeCalculator
Implements INotifyPropertyChanged
Private _miles As Double
Public Property Miles() As Double
Get
Return _miles
End Get
Set(ByVal value As Double)
If _miles <> value Then
_miles = value
OnPropertyChanged("Miles")
OnPropertyChanged("Traveltime")
End If
End Set
End Property
Private _speed As Double
Public Property Speed() As Double
Get
Return _speed
End Get
Set(ByVal value As Double)
If _speed <> value Then
_speed = value
OnPropertyChanged("Speed")
OnPropertyChanged("Traveltime")
End If
End Set
End Property
Public ReadOnly Property Traveltime() As Double
Get
Return If(_speed = 0.0, 0.0, _miles / _speed)
End Get
End Property
#Region "INotifyPropertyChanged Members"
Public Event PropertyChanged As PropertyChangedEventHandler _
Implements INotifyPropertyChanged.PropertyChanged
Private Sub OnPropertyChanged(ByVal propertyName As String)
RaiseEvent PropertyChanged(Me, New PropertyChangedEventArgs(propertyName))
End Sub
#End Region
End Class
In Visual Studio, add a data source in the Data Sources panel. Choose "Object" and then select the TraveltimeCalculator (it has to be compiled, before you can do that). Now you can drag the speed, mileage and travel time fields from the data sources panel to your form. All the wire-up will happen automatically. VS automatically inserts a BindingSource and a navigator into your form. You will not need the navigator and can safely remove it. The only thing you still have to do is to add the following code in the form load event handler or in the form constructor:
Private Sub frmTravelTime_Load(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles MyBase.Load
TraveltimeCalculatorBindingSource.DataSource = New TraveltimeCalculator()
End Sub
When you enter speeds and mileages, the travel time textbox will automatically be updated. Non-numeric entries will automatically be rejected and all the text-number conversions happen automatically.
I discovered what the problem was.
To store a field from a one-line database to a local variable for calculations, apparently it has to happen in the form1_load event, after the dataadapter fill statement, like so:
Me.TestdataTableAdapter.Fill(Me.foouDataSet.testdata)
speed = Me.fooDataSet.testdata(0).speed
and just DIM speed as Decimal after the Public Class line. The same could be done for any other field you want to work with in a similar kind of single datarow:
yourvarname = Me.yourdatasetname.yourtablename(0).the_database_field_you_want_to_fetch
(Wow! Did I just write something textbooky? LOL)
Then, after the button click, to do a calculation, it is:
traveltime = CDec(miles.Text/ speed)
txttraveltime.Text = traveltime.ToString
making sure to DIM traveltime as Decimal.
Works! The problem was the (0) to indicate row 0 (because it's only one row.) Thank you everyone for your help, especially Competent_Tech. I learned something and I'm happy that I could get back to you guys and share.
http://pastebin.com/3A4P61Gt The code in question is specifically at line 143. Whenever I try to access a label in the array like so Dicelbls(0).Text I get a null reference error. Obviously I am not declaring the array right, any suggestions?
You are right, the problem is at line 143:
Dim Dicelbls As Label() = {lblP1Die0, lblP1Die1, lblP1Die2, lblP2Die0, lblP2Die1, lblP2Die2, lblP1Score, lblP2Score}
Specifically, at the point in the object initialization process when this code runs, the references behind those Label variables are still null/Nothing. So you're putting references to Nothing into your array.
To fix the code, move the initialization to the Form_Load event instead.
Try to add initialization in Form_Load event.
Dim Dicelbls As Label()
Private Sub Form1_Load(..)
Dicelbls= new Label() {lblP1Die0, lblP1Die1, lblP1Die2, lblP2Die0, lblP2Die1, lblP2Die2, lblP1Score, lblP2Score}
....
End Sub
You're declaring the array correctly, but in the wrong place. Leave the variable declaration where it is, and move the assignment to somewhere after the form is created.
Class frmMain
Dim Dicelbls As Label()
Private Sub frmMain_Load(sender As Object, e As EventArgs) Handles Me.Load
Dicelbls = {lblP1Die0, lblP1Die1, lblP1Die2, lblP2Die0, lblP2Die1, lblP2Die2, lblP1Score, lblP2Score}
End Sub
...
End Class
Try this one:
Dim Dicelbls(8) As Label
Private Sub frmMain_Load(sender As Object, e As EventArgs) Handles Me.Load
Dicelbls = {lblP1Die0, lblP1Die1, lblP1Die2, lblP2Die0, lblP2Die1, lblP2Die2, lblP1Score, lblP2Score}
End Sub