I am having a problem getting a global variable to update in VB.NET. The declared value is 0, but the variable changes according to a choice that the user makes.
I have multiple forms, and I have tried updating the variable on 2 different forms with the same result.
I declared the variable like so:
Public Shared creativity As Integer = 0
Public Shared comboBox = frmStart.cbxCombo.SelectedItem
To change the value I used:
If comboBox = "Yes" Then
creativity += 30
End If
I expect the value of creativity to be 30, but it is still showing as 0. I have even tried:
creativity = creativity + 30
but I am still getting the same result, with the value displaying as 0.
Where is the function being called in the code?
For something like
public sub comboBox_change()
If comboBox = "Yes" Then
creativity += 30
End If
End sub
The function call will occur whenever the combo box is change, which you could end up with a stacking issue (multiple calls of the same function, all increment it each time it is changed).
If you have it in a function that is not called, then of course it is not being incremented, as the code is never executed, like if you made a
public sub ThisIsMyCode()
...
End sub
Without a call reference.
you should put this code
If comboBox = "Yes" Then
creativity += 30
End If
inside the SelectedItemChanged event of your combobox
Also, you should put this:
Public Shared comboBox = frmStart.cbxCombo.SelectedValue
Hope this helps
Related
I am learning vb.net and I'm having issues searching for what I need. I want to create a button that is "re-usable" throughout my application without needing to write code for each instance. So, what I would like to start with is take a variable in a form, example, public integer value and when this value changes I want to write to the text of a button. I know I can easily do this by writing code in the form btn_xxx.text = variable, but what if I have several buttons and each button looks at the same variable? Currently what I do is create a component which inherits a button and have a timer that on tick will look at the variable and write to the text. I'm sure there is a better way. Can anyone point me in the right direction? I know part of my problem is I don't know the nomenclature on what things are called, so hopefully I asked my question without too much confusion.
I saw this, https://www.daniweb.com/programming/software-development/threads/124842/detect-variable-change, but I don't see how to adapt that to my situation.
Here is what I have:
Private WithEvents Active_Alarm As New Nav_Active_Alarm
Then inside of a sub that calculates the count:
Active_Alarm.Count = CInt(dt_Active_Alarms.Rows.Count)
The user control:
Public Class Nav_Active_Alarm
Private mActive_Alarm_Count As Integer
Public Event Active_Alarm_Count_Changed(ByVal mvalue As Integer)
Public Property Count() As Integer
Get
Count = mActive_Alarm_Count
End Get
Set(ByVal value As Integer)
mActive_Alarm_Count = value
If Not Me.DesignMode Then
RaiseEvent Active_Alarm_Count_Changed(mActive_Alarm_Count)
test()
End If
End Set
End Property
Private Sub test()
If Not Me.DesignMode Then
If mActive_Alarm_Count = 0 Then
Me.btn_Goto_Active_Alarm.Image = My.Resources.Alarm_Clock_Static
'console or msgbox will work but updating the image will not
Else
Me.btn_Goto_Active_Alarm.Image = My.Resources.Alarm_Clock_Animation
'console or msgbox will work but updating the image will not
End If
End If
End Sub
End Class
If I write to console or add a msgbox I will see the event working. But, the image will not change. If I call the test sub from a timer it will work. Why won't the button update (by the way, I did try refresh and update in the code)?
Observer pattern is what you probably looking for.
This is quick and dirty.
Create a class to hold the variable value. Add a method that adds a button instance to a list.
Then a button that needs to know about the variable calls the register method.
When the value of the variable changes, it iterates through the list of buttons and sets the Text property of each one.
You might have jumped in a bit too deep too quick here. Google Custom data binding in .net, there's loads of built in stuff you can use. Though do it yourself is a good exercise.
A simple method to do this might be:
Create a form level list to hold the buttons you are interested in
Add the buttons you are interested in, into the list (maybe in form load or some other place where you have initialization code)
Create a private property in your form with a backing variable to hold the value you want to have applied to the buttons. In the setter portion spin through the list and set each buttons text.
Dim lstButtons As New List(Of Button)
Sub SetupButtons()
'call from form load or other init code
lstButtons.Add(btnPopulate)
lstButtons.Add(btnPopulate)
End Sub
Private _buttonText As String
Private Property ButtonText As String
Get
Return _buttonText
End Get
Set(value As String)
_buttonText = value
For Each b As Button In lstButtons
b.Text = value
Next
End Set
End Property
When you set the property - which now acts as your variable - it will update all of your textboxes for you.
I realize you mentioned without having to write code - but something has to tie things together. Even if you used the observer pattern (which is an elegant solution for this - so props to those who suggested it) you'd probably end up creating a class to hold the property and have that class implement the INotifyPropertyChanged from System.ComponentModel, and then you'd also have to have each button have a databinding for its text property to the property in the object of your class. There isn't really a way (that I can think of) to get around having to write some code for each form you do this in (though the class part you'd only have to write once of course).
I'm having a really strange issue with the DataGridView control in a VS2008 / .NET 3.5 winforms project. I have a simple form with a grid. In the form constructor I call a function to bind the grind to a DataTable, and then loop through the rows setting the background colour of the last cell to LightGrey and the cell itself to read-only if the column value is true. After the form finishes loading the code didn't work i.e. the cells are not set to LightGrey and are not read-only (even though when I step through the code I can see the properties being set). I then call the function again from a button, but this time the colour is changed to LightGrey and the cell is made read-only i.e. the code works.
CODE:
Dim dgr As DataGridViewRow
For i_DsRow As Integer = 0 To ds.Tables(0).Rows.Count - 1
dr = ds.Tables(0).Rows(i_DsRow)
For i_row As Integer = 0 To DgSearch.Rows.Count - 1
dgr = DgSearch.Rows(i_row)
If dr("DsColoumn1").ToString.ToUpper = dgr.Cells("DgColoumn1").Value.ToString.ToUpper Then
If Val(dr("Coloumn2").ToString) = 3 Then
dgr.Cells("SomeColomname").Value = dr("SomeColoumName2").ToString
If dgr.Cells("SomeColomname3").Value.ToString <> "" Then dgr.Cells("SomeColomname3").Value &= ", "
dgr.Cells("SomeColomname3").Value &= dr("SomeColoumName2").ToString
SetCellColor(dgr.Cells("SomeColomname"), dgr.Cells("SomeColomname3"))
End If
Exit For
End If
Next
Next
Private Sub SetCellColor(ByVal resultCell As DataGridViewCell, ByVal ColorCell As DataGridViewCell)
If resultCell.Value.ToString().ToUpper = "A".ToUpper Or resultCell.Value.ToString().ToUpper = "B".ToUpper Then
ColorCell.Style.BackColor = Color.FromName("Red")
ElseIf resultCell.Value.ToString().ToUpper = "C".ToUpper Or resultCell.Value.ToString().ToUpper = "D".ToUpper Then
ColorCell.Style.BackColor = Color.FromName("MediumSeaGreen")
Else
ColorCell.Style.BackColor = Color.FromName("Yellow")
End If
End Sub
Thanks for adding the code. I would suggest putting all formatting in the CellFormatting event of the DataGridView.
There are a couple of gotchas that you have to watch, which looking at your code shouldn't be a problem but to be aware of:
This runs for each cell in the grid. Therefore Database lookups are a really bad idea.
You do best to get values where possible from the eventargs parameter of the method
If you need a code sample, I believe there are some good c# examples (possibly vb to) this one for example.
Objective: Redirect focus from one command button to another using the first's GotFocus procedure.
Context: I have a form-independent procedure in a generic module that, on most forms, sets focus to the NewRecord button after saving the previous record. But on one form, I would like to redirect (based on certain conditions) focus back to the SignRecord button so the user can "sign" a second part of the same record (I may need this for other uses in the future). The target control is enabled and visible and can otherwise be focused and the original control can be focused when the redirect doesn't occur. Reference [2] below implies that this should be possible, though I'm not changing visibility of my controls.
Issue: When the conditions are met to redirect focus in the GotFocus procedure, it redirects as desired but the original (test) SetFocus call throws a "Run-time error '2110', Can't move focus to the control CommandNew".
What I've tried:
Exit Sub after my downstream SetFocus calls.
Call CommandSign.SetFocus in the hopes that it would make it happen outside the previous SetFocus process.
In a module,
Public Sub test()
Forms("TargetForm").CommandNew.SetFocus 'This gets the error '2110'
End Sub
In the 'TargetForm',
Private Sub CommandNew_GotFocus()
If IsNull(textDateTime) Then Exit Sub 'Works as expected
'I can see these two parts work. The framSign value changes
'and CommandSign gets focus
If checPPC And IsNull(textSigID_PPC) And framSign = 2 Then
framSign = 1
CommandSign.SetFocus
ElseIf checDAS And IsNull(textSigID_DAS) And framSign = 1 Then
framSign = 2
CommandSign.SetFocus
End If
End Sub
References:
[1]: SelectNextControl() a bad idea in a GotFocus event?
[2]: http://www.access-programmers.co.uk/forums/showthread.php?t=100071
I think your problem is that the call to Forms("TargetForm").CommandNew.SetFocus doesn't quite seem to, in fact, finish setting the focus to CommandNew until after Private Sub CommandNew_GotFocus() has finished executing. Because you've called another SetFocus before the first SetFocus could finish, there is a conflict that Access seems to be unable to cope with.
Whether or not that is the case, one thing is clear: the way you have your execution plan set up right now is unfortunately not going to work. You might try adding either a global variable or a public variable to each form that determines whether or not you should set your focus to CommandSign after you set the focus to CommandNew.
Ex. TargetForm:
Public boolSetCommandSignFocusInstead As Boolean
Private Sub CommandNew_GotFocus()
If IsNull(textDateTime) Then Exit Sub 'Works as expected
'I can see these two parts work. The framSign value changes
'and CommandSign gets focus
If checPPC And IsNull(textSigID_PPC) And framSign = 2 Then
framSign = 1
boolSetCommandSignFocusInstead = True
ElseIf checDAS And IsNull(textSigID_DAS) And framSign = 1 Then
framSign = 2
boolSetCommandSignFocusInstead = True
Else
boolSetCommandSignFocusInstead = False
End If
End Sub
Module:
Public Sub test()
Forms("TargetForm").CommandNew.SetFocus
If Forms("TargetForm").boolSetCommandSignFocusInstead Then
Forms("TargetForm").CommandSign.SetFocus
End If
End Sub
I have a an array in my module, so I want to display the contents of my array in a form textbox, here is my array
Module Module1
Sub AddCourse()
Dim Subjects() = {"Ms Office 2007", "internet and commmunications", "Lifetime skills"}
For i = 0 To UBound(Subjects) ' FOR LOOP TO WRITE AN ARRAY
i = i +1
Subjects(i)
Next
txtComputer.Text = subjects()
my problem is, when I try to use my texbox txtComputer in my module I get an error.
My question is, how do I make a form textbox to be used in a module
I get an error that reads "Error'txtComputer' is not declared. It may be inaccessible due to its protection level."
My question is based on, how do I get this error fixed?
There are several suggestions I have for you.
First, don't use UBound. That's an old VB6 function that is only provided for backwards compatability. You should instead use Subjects.Length.
Next, when you're incrementing the i variable, you don't need to say i = i + 1. You can just use the += operator for that (e.g. i += 1).
However, you shouldn't be explicitly incrementing i inside your For loop, anyway. The loop automatically increments the variable for you each time it iterates through the loop. If you do it explicitly yourself inside the loop, like that, it will skip every other item.
Next, in this case, you really should just use a For Each loop, rather than an iterator:
For Each subject As String in Subjects
'...
Next
Next, you aren't actually concatenating the items together inside you loop. You should be doing something like this:
For Each subject As String in Subjects
txtComputer.Text += subject
Next
However, in that case, for efficiency sake, you really ought to use a StringBuilder, like this:
Dim builder As New StringBuilder()
For Each subject As String in Subjects
builder.Append(subject)
Next
txtComputer.Text = builder.ToString()
But, all of this is moot because all you really need to do is to call String.Join:
txtComputer.Text = String.Join(", ", Subject)
As far as why you can't access the text box from the module, that is because the module is a separate object, so the text box is entirely out of scope. For instance, what if you had two instances of your form displayed at the same time? How in the world would this module know which form's text box you were referring to? The simplest way to correct that would be to pass a reference to your form into the module's method, like this:
Module Module1
Sub AddCourse(f As MyFormName)
f.txtComputer.Text = "Hello world"
End Sub
End Module
And then you could call it from the form, like this:
AddCourse(Me)
However, that would be exceptionally bad practice. Ideally, nothing outside of the form's code should ever deal directly with any of the controls on the form. So, the far better way to do it would be to simply have the method return the data, and then have the form set it's own control to the data that is returned, for instance:
Module Module1
Function GetCourse() As String
Return "Hello world"
End Function
End Module
And then call it from the form like this:
txtComputer.Text = GetCourse()
You can use String.Join to create a string which separates each subject with Environment.NewLine:
txtComputer.Text = String.Join(Environment.NewLine, Subjects)
The problem with your for loop is that it makes no sense at all since you have already declared and initialized the array in one line.
If you want to use a loop anyway, you can use a StringBuilder to concat all strings together:
Dim subjectBuilder = New System.Text.StringBuilder
For Each subject In Subjects
subjectBuilder.Append(subject).Append(Environment.NewLine)
Next
If subjectBuilder.Length <> 0 Then subjectBuilder.Length -= Environment.NewLine.Length
txtComputer.Text = subjectBuilder.ToString()
I have this short piece of code
Public n As Integer
Public Sub Foo()
For i = 0 To 4
MyModule.n = MyModule.n + 1
Next i
End Sub
which is defined in a Module named MyModule. This code is working as expected: After executing for the first time 'MyModule.n' has the value 5. After executing the second time it has the value 10 and so on.
When I extend the code, to add a CommandButton and place it onto the working sheet, the global variable MyModule.n loses it's value on every new call of Foo:
Public n As Integer
Public Sub Foo()
Dim btn As OLEObject
For i = 0 To 4
Set btn = Worksheets("Aufträge").OLEObjects.Add(ClassType:="Forms.CommandButton.1", _
Left:=122, Top:=321, Width:=30, Height:=30)
MyModule.n = MyModule.n + 1
Next i
End Sub
The code seems to work because the command button is created and placed correctly. Why does the global variable reset if executing the second code fragment?
Furthermore I can't place a break point after or inside the For-Loop in the second code fragment. I get the message Can't enter break mode at this time.
Based on the search I did & my conclusion is that you can't add controls dynamically to the worksheet and retain the state of variables.
Here is why: Adding a button will force the sheet to goto design mode & hence reset of variables.
Supporting links
http://www.pcreview.co.uk/forums/dynamically-adding-activex-controls-via-vba-kills-global-vba-heap-t3763287p2.html
https://web.archive.org/web/20101215134333/http://support.microsoft.com/kb/231089 (originally //support.microsoft.com/kb/231089)
You don't need to use a for next loop for the first example. Why don't you do that :
Public n As Integer
Public Sub Foo()
MyModule.n = 5
End Sub
This will do the thing in one operation.
In the second example you don't add one button, you add 5 of them. Check the help fo "For Next".