Dynamically adding and using types in vb.net - 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

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 make this more efficient?

Private Sub btnOne_Click(sender As Object, e As EventArgs) Handles btnOne.Click
txtAnswer.AppendText(1)
End Sub
So this is my code (It's for 0 through to 9 and a dot for decimal points).
I'm working on calculator for an assignment, my question is, can I make this more efficient so I don't have a lot code doing the same thing?
I had considered setting up a function to read the contents of the buttons and add them to the textbox, but then the operation buttons and clear buttons would just add to the textbox instead of performing the code assigned to them.
you can create one method for the numeric buttons and add their value to the Tag Object. Then you can just reference the tag object to append to the text
Private Sub NumericButtons_Click(sender As Object, e As EventArgs) Handles Button1.Click
Dim btn As Button
btn = sender
txtAnswer.AppendText(btn.Tag)
End Sub
You could create a function just like that, but it should read the number from the sender's Text property, and only assign the number buttons to that function in the OnClick event.
The other buttons will have their own OnClick methods, and you should make a generic function like in this case when you find a common behavior in some of them. Perhaps you could do another generic function for all + - / x operators, it that's appropriate.

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

AddHandlers in VB.NET

I'm trying to dynamically creating dropdownList boxes, and I want trying to add AddHandlers to them so that when an item is selected in them, it fires an event, but also need to pass another variable, and I don't know what to put as the parameter for system.EventArgs. Please look at the code below to see the problem I'm having.
AddHandler inputDrop.SelectedIndexChanged, AddressOf selOption(inputDrop, ???, var1)
Protected Sub selOption(ByVal sender As Object, ByVal e As System.EventArgs, ByVal tableCount As String)
End Sub
What do I put (???) right here.
The error:
is an event, and cannot be called directly. Use a 'RaiseEvent' statement to raise an event.
In addition what Mike C already explained, if the signature of the event handler does not match the event, you can always wrap the event handler in another method, for example an anonymous one:
Protected Sub selOption(ender As Object, e As System.EventArgs, somestring As String)
End Sub
...
For i = 1 To 10
Dim cbox = new ComboBox()
Dim number = i ' local copy to prevent capturing of i '
AddHandler cbox.SelectedIndexChanged, Sub(s, e) selOption(s, e, "Hi! I am Number " & number)
Next
Now, when the index of the last ComboBox changes, the somestring parameter passed to selOption will be Hi! I am Number 10, while it will be Hi! I am Number 1 for the first ComboBox etc.
When you register an event handler, you don't specify the arguments at that time. You're basically just setting a reference to a delegate that will handle the event when it is raised.
AddHandler inputDrop.SelectedIndexChanged, AddressOf selOption
The most important thing is that the method signature of the event handler matches up exactly with the method signature defined by the event. I'm not sure that your method would work because you have that extra tableCount parameter specified. You will need to modify your method signature to be:
Protected Sub selOption(ByVal sender As Object, ByVal e As System.EventArgs)
I'm basing that off the definition of SelectedIndexChanged for Winforms. This event could be defined differently in another technology, such as ASP.net or WPF. Or if this is some custom class, it could be an entirely different signature altogether. However, typically most event handlers have a similar structure of a sender (the instance that raises the event) and some event arguments.
Then when inputDrop fires it's event (when the selected item changes), your code will get automatically called. The arguments passed to this method will be passed directly from inputDrop, you do not have to specify them.
Also, your AddHandler statement must exist inside a method or code block, it can't just live in the class definition. It's a statement that must be executed like any other piece of code, it's not a declaration.
And there is yet another way of doing it. Inherit the control in question and add a property like this:
Public Class MyComboBox : Inherits ComboBox
Public Property tableCount As String
End Class
Then set your custom value and add a handler as you would for a regular ComboBox:
combo.tableCount = tableCount
AddHandler combo.Click, AddressOf combo_Click
Inside combo_Click, CType sender to your inherited type, and get the value you stored previously:
Private Sub combo_Click(sender As Object, e As System.EventArgs)
Debug.WriteLine(CType(sender, WorkflowActionBox).tableCount)
End Sub
You will need to replace current usages of ComboBox with those of MyComboBox, where you want the new property to be available. Simple as opening your designer file and doing find/replace.

Change properties of an unknown object in 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)