Change properties of an unknown object in VB.NET - vb.net

I have a sub that handles when 14 ComboBoxes have their Index changed. I am able to cast the sender of the event, and obtain properties from there. However, after that, I want to be able to change the properties of the actual sender, rather than the cast one. How would I do this?
Current code:
Private Sub ComboBoxIndexChange(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles ComboBox1.SelectedIndexChanged, ComboBox2.SelectedIndexChanged [etc]
Dim myComboBox As ComboBox = sender
Select Case myComboBox.Text
Case "Will"
Me.Controls(myComboBox.Name).Text = "555-555-555"
Case "Bob"
Me.Controls(myComboBox.Name).Text = "555-124-1234"
[etc]
End Select
End Sub
End Class
As you can see, I am currently trying to use
Me.Controls(myComboBox.Name).Text
But I get the error: Object reference not set to an instance of an object.
What can I do?

The sender parameter in an event handler will (typically) contain a reference to the object that raised the event. In the case of the SelectedIndexChanged event of a ComboBox control, it will be the ComboBox that had its SelectedIndex property changed. So in your code sample above myComboBox is referring the ComboBox that raised the event.
To clarify: if you select an item in the drop-down list of a ComboBox control, so that it raises the SelectedIndexChanged event, the sender parameter of the event handler will be that same ComboBox, not a copy of it. This is true for all reference types.
Had it been a value type raising the event it would have been a completely different story, but that is a very rare case (it it is never the case when it comes to controls on a form).

ComboBox is a reference type, so if you assign directly to the Text property of the myComboBox variable, it will update the control.

This should work:
Dim ctl As Control
Dim cmb As ComboBox
For Each ctl In Me.Controls
If sender Is ctl Then
cmb = ctl
cmb.Text = "string"
End If
Next ctl
You can add the specific string assignment code in place of cmd.Text = "string". Assigning cmb allows you to use the specific combobox properties rather than the generic control properties.

To answer the original question of why you were getting a null object reference exception, is the Control you are searching for a direct child of the Form? Does it sit in a panel or some other container on the form? If so then I don't think you will find that control in the Form's Controls collection. So this line:
Me.Controls(myComboBox.Name).Text
Only searches through the forms direct children and not recursively through the children of those controls.

If I using the following code, I will get the same err: Object reference not set to an instance of an object.
Me.Controls(TBName).DataBindings.Add("Text", dt, dt.Columns(colindex).ColumnName)
Sugestion from Christian Pena fit my case, so the right code should be:
Me.TCAll.TabPages(0).Controls(TBName).DataBindings.Add("Text", dt, dt.Columns(colindex).ColumnName)

Related

How to remove the most recently added control?

I Intended to display an PictureBox in my form when the mouse hovered over another control. I then wanted to use a separate event for when the mouse left the control. This event would remove the displayed PictureBox from controls. However, because my events are private subs, I can't directly access the name of the control in the latter event. A solution to this would be a method that removes the most recently added control. If no such method exists, or there is an alternative way of approaching this problem, any help would be appreciated.
I tried simply using Controls.Remove(), but this requires a parameter. The name of the control as a string did not work either, as the parameter must be a control itself.
Private Sub Tile_MouseEnter(Sender As Object, e As EventArgs)
Dim CloseUpPic As New PictureBox With {Properties}
CloseUpPic.Image = Sender.Image
Controls.Add(CloseUpPic)
Refresh()
End Sub
Private Sub Tile_MouseLeave(Sender As Object, e As EventArgs)
Me.Controls.Remove()
End Sub
The program won't compile due to .Remove() needing a parameter
I expected for the Control to be created and displayed when the mouse entered the tile, and to cease to exist when the mouse left the tile.
For future reference, controls have Tag property which allows you to store whatever you like. In this case, you can store a reference to the newly created PictureBox. Furthermore, the "Sender" parameter tells you which control was the source of the event. You can cast sender to a control, then store the reference. Then, in the leave event, you can cast sender to a control, cast the .Tag to a control, and finally remove it:
Private Sub Tile_MouseEnter(Sender As Object, e As EventArgs)
Dim ctl As Control = DirectCast(Sender, Control)
Dim CloseUpPic As New PictureBox With {Properties}
CloseUpPic.Image = Sender.Image
Controls.Add(CloseUpPic)
ctl.Tag = CloseUpPic
Refresh()
End Sub
Private Sub Tile_MouseLeave(Sender As Object, e As EventArgs)
Dim ctl As Control = DirectCast(Sender, Control)
Dim ctlToRemove As Control = DirectCast(ctl.Tag, Control)
Me.Controls.Remove(ctlToRemove)
End Sub
I ended up using the following code to solve my issue:
For Each Closeup In Controls.OfType(Of CloseUp)
Controls.Remove(Closeup)
Next
I created a new class of my own called Closeup, that inherits PictureBox. I then looped through each Closeup in controls (There was only one but this code works for multiple controls), and removed them.

How to Dynamically Update Label with TextBox As Input Changes

I have a spelling application that I am building in VB.Net, where I have a textbox receiving simple input (spelling words), and a label which will show the output. What I want to accomplish is when I enter something in the textbox, I can see it in my label - as I am typing into the textbox.
I will admit that I don't know what I'm doing, as I've never tried this before, so I don't know to begin in terms of setting up what I need to do. I know that I'll need some variable to hold my String input, and will probably need some type of loop, but beyond that, I am lost. The only other example is in C#, and doesn't help me any.
Can anyone give me a simple model to work off of, so I can put the approach into memory? For now, all I have is code stub from my TextChanged event handler:
Private Sub txtSpell_TextChanged(sender As Object, e As EventArgs) Handles txtSpell.TextChanged
'Set variables to hold values.
Dim someText As String
'Connect the label and textbox.
lblShowInput.Text = txtWordInput.Text
'Process loop to populate the label from textbox input.
for '(This is where I am lost on the approach)
End Sub
I know that I'll need some variable to hold my String input, and will
probably need some type of loop
I don't think you'll need a loop, or the variable to hold the value. You almost have it:
Private Sub txtSpell_TextChanged(sender As Object, e As EventArgs) Handles txtSpell.TextChanged
'Connect the label and textbox.
lblShowInput.Text = txtSpell.Text
End Sub
In the code you provided, you are referencing an object named txtWordInput inside your txtSpell text changed event handler. If you are entering the text in the txtWordInput input, you'll want to handle this in the txtWordInput textChanged event handler:
Private Sub txtWordInput_TextChanged(sender As Object, e As EventArgs) Handles txtWordInput.TextChanged
'Connect the label and textbox.
lblShowInput.Text = txtWordInput.Text
End Sub
Follow-up:
The TextChanged event is the correct event for this.
In your code, you are assigning lblShowInput.Text to txtWordInput.Text, but in the txtSpell TextChanged event handler.
You want to be in the TextChanged event handler for whatever TextBox you would like to use to update the label, as the text is changing.
To give a better example, I have created a simple Winforms VB application that has only a textbox named InputTextBox and a label named Output Label.
The Form:
The Code:
Public Class Form1
Private Sub InputTextBox_TextChanged(sender As System.Object, e As System.EventArgs) Handles InputTextBox.TextChanged
OutputLabel.Text = InputTextBox.Text
End Sub
End Class
Explanation:
InputTextBox_TextChanged is the method name generated by Visual Studio for our event handler
Handles InputTextBox.TextChanged ties the method to an actual event it is handling.
When the InputTextBox text property is changed (typically by user input), whatever we have in our InputTextBox_TextChanged Sub will execute. In this case, I am assigning the Text of OutputLabel to the Text of the InputTextBox
Output:
Resources:
I've uploaded this simple demo to GitHub if you'd like a closer look.
Take a look at the TextChanged documentation

Using events for a dimmed variable

How can I use the event for a dimmed variable that is NOT a control.
This is my dimmed variable:
Dim engine As New Speech.Recognition.SpeechRecognitionEngine
I want to use the event "engine.SpeechRecognized".
You do it the same way you would for anything else where you wanted to add handlers explicitly:
AddHandler engine.SpeechRecognized, AddressOf HandleSpeechRecognized
See the documentation for the AddHandler statement for more information.
There are two ways to add error handlers in VB.NET. You can do so "manually" by using the AddHandler statement, such as:
Dim engine As New SpeechRecognitionEngine()
AddHandler engine.SpeechDetected, AddressOf OnSpeechDetected
With this approach, you would then need to manually implement the OnSpeechDetected event handler method, such as:
Private Sub OnSpeechDetected(ByVal sender As Object, ByVal e As SpeechDetectedEventArgs)
' Do something
End Sub
However, the second method is often easier. This second method is the way that events for controls are handled. However, it is only possible if your object variable is declared as a field (at the class level, outside of any method). All you need to do is add the keyword WithEvents before the variable name, such as:
Dim WithEvents engine As New SpeechRecognitionEngine()
Then, that variable name will show up in the left-side drop-down box at the top of your code window along with all your controls. When you select it in that drop-down box, you can then select any of its events in the right-side drop-down box and it will automatically create the event handler method for you:
Private Sub engine_SpeechDetected(ByVal sender As Object, ByVal e As SpeechDetectedEventArgs) Handles engine.SpeechDetected
End Sub

How do I traverse/iterate through all the objects inside a form in VB.net?

I have a VB class inside which one of the methods accepts an array of forms.
For each form inside the array, I need it to traverse all of the objects, check if they are a specific tyoe (input, label, checkbox, etc.) and get the properties of each object. Then, I want to dump these into a text file in the following format:
Form1 | Label1 | "Enter your name"
"Enter your name" being the caption or text of the form object.
I want to do this to facilitate the translation of an application. Any ideas or thoughts you might have on this?
The following code will return an IEnumerable(Of Control) which contains all child controls of the passed in control. It will recursively descend down the tree and get all nested controls.
Public Function GetAllControls(ByVal source as Control) As IEnumerable(Of Control)
Dim seq = Enumerable.Empty(Of Control)
For Each child in source.Controls
if child.Controls.Count > 0 Then
seq = seq.Concat(GetAllControls(child))
End If
Next
Return seq
End Function
You have to do a For Each for the form's .Controls collection. However, note that if a child control of the form has more controls in its own .Controls collection, they won't be accounted for. You'll have to make a recursive function in order to traverse the whole parent-child chain of controls to find them all.
Now, for each control, you'll want to perhaps do a Case statement to check the Type Of for each control. Then cast the control to it's type and grab the properties.
If all you want to do is to be able to localise your form then a much simpler way would be to set the form's Localizable property to true. This will result in the creation of a .resx file containing all the values of the various properties for all the controls on the form so that the texts etc can be translated and distributed as a separate satellite assembly.
Public Class Form1
Private Sub Button1_Click(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles Button1.Click
ProcessForm(Me)
End Sub
Private Sub ProcessForm(ByVal frm As Form)
For Each el As Control In frm.Controls
Dim str As String
str = String.Format("{0} | {1} | {2}", frm.Name.ToString(), el.Name.ToString(), el.Text.ToString())
Debug.Print(str)
Next
End Sub
End Class

Dynamically adding and using types in vb.net

I am making a program which dynamically adds objects, such as a button or a checkbox to a form.
For each instance, a handler is added so a certain function is called for the Click event of each object.
Now, when that handler is called, how can I manipulate the object that fired off the Click event? The Sender object is useless here as I cannot change the location, text, parent, nothing at all.
Since the objects are being created dynamically, their instance name is unfortunately always going to be the same, therefore I cannot simply do things like button1.Text = "Button 1".
I really do not want to create a new subroutine for every type since the actions that would be performed are the same...so how can I manipulate these objects?
There are, at last count, 27 different object types which are being manipulated, and which I want to be manipulated by a single sub.
Thanks for the help!
It sounds like the sender is what you want, as this will be the object that fired off the Click event. You just need to figure out a way to cast it to the required type.
If you are just manipulating location, text and parent, then casting to Control will be enough:
Dim c As Control = CType(sender, Control)
Otherwise you will need to cast to the specific type which means you will need different routines per type.
Another option is to turn on late binding, which I believe in VB is Option Strict Off. Then you can refer to control properties even without the casting -- .NET will look for the property at runtime (and, be warned, will throw an exception if the property's not there).
Cast the sender to Control & you can do what you want (all your objects are Controls right?)
If you know the type of input that called the handler, then you can use typecasting to solve your issue:
Sub General_OnClick(ByVal sender As Object, ByVal e As EventArgs)
Dim b As Button = sender
b.Text = "Hello World!"
End Sub
If you don't, which you don't seem you, you might try casting to Control instead, this may give you enough control, depending on what you need to do. If not, you can always do something like:
Sub General_OnClick(ByVal sender As Object, ByVal e As EventArgs)
If TypeOf sender Is Button Then
Dim b As Button = sender
b.Text = "Hello World!"
Else If TypeOf sender Is TextBox Then
Dim tb As TextBox = sender
tb.Text = "Goodbye cruel world!"
End If
End Sub
EDIT: Updated to translate into VB.Net