Hi
I am using following code to run a private function.
I have two values in my combo box, One and Two and two private functions with the same names, Private Sub One() and Private Sub Two()
I want my application to call the function whatever value user choses in the combo box.
If One is chosen in the combo box, Private function one should be called.
Thanks
Code is below, that does not work
Public Class Form1
Private Sub Button1_Click(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles Button1.Click
Dim vrValue = ComboBox1.Items(1)
Call vrValue()' In this case vrValue is Two, so Two() should be called.
End Sub
Private Sub two()
MsgBox("Function called")
End Sub
Make your subs functions (the only difference is the returning of a value) and put them in their own class:
Public Class RunFunctions
Dim oMessageBox As MessageBox
Public Function One() As String
'oMessageBox = MessageBox
Return "Message One"
End Function
Public Function Two() As String
Return "Message Two"
End Function
End Class
Add Each function from the class as an item in your combo box:
Public Class Combo_Functions
Dim oRunFunction As RunFunctions
Private Sub ComboBox1_SelectedIndexChanged(ByVal sender As Object _
, ByVal e As System.EventArgs) Handles ComboBox1.SelectedIndexChanged
MessageBox.Show(ComboBox1.Items(ComboBox1.SelectedIndex()))
End Sub
Private Sub Combo_Functions_Load(ByVal sender As Object _
, ByVal e As System.EventArgs) Handles Me.Load
oRunFunction = New RunFunctions
ComboBox1.Items.Add(oRunFunction.One())
ComboBox1.Items.Add(oRunFunction.Two())
End Sub
End Class
When the combo box is changed (or use the code for the button click) the messagebox for the correct function is executed.
Dim vrValue = ComboBox1.SelectedItem.ToString()
Select vrValue
Case "One"
One()
Else
Two()
End Select
It looks like what you're trying to do is to dynamically call a particular method using a string variable that contains its name. For example, the combo box would contain items "One" and "Two", and you would call the sub named "One" if the first item in the combo box is selected, or the sub named "Two" if the second item is selected. To that end, you may find this article interesting:
http://www.codeproject.com/KB/cs/CallMethodNameInString.aspx
The code in the article is in C#, which shouldn't be too difficult to convert to VB. But here's the translated version of the code for simply invoking a method without passing or returning any parameters (note: I have not tested this code). It simply uses reflection to find the appropriate method:
Public Shared Sub InvokeStringMethod(ByVal typeName As String, ByVal methodName As String)
'Get the type of the class
Dim calledType As Type = Type.[GetType](typeName)
'Invoke the method itself
calledType.InvokeMember(methodName, BindingFlags.InvokeMethod Or BindingFlags.[Public] Or BindingFlags.[Static], Nothing, Nothing, Nothing)
End Sub
You simply pass the name of the class that contains the method(s) you want to call as the typeName and the name of the method itself that you want to call as the methodName:
InvokeStringMethod("MyClass", "Two")
Related
I am in a project with multiple form.
I create a TicTacToe form here :
Private Sub MenuTicTacToe(ByVal sender As Object, ByVal e As System.EventArgs)
Dim page As Form = New TicTacToe
page.Show(Me)
End Sub
Here is a TicTacToe form:
Public Class TicTacToe
Public opponent as String
'Some code where user set opponent
Public Function Receive(S As String)
if string = opponent
'Some code
End Function
End Class
I would like to call my function Receive in my main form
If i do:
TicTactoe.Receive(S)
It call a instance of Receive where opponent does not exist.
I would like to find the oppened form of TicTacToe and call Receive
Thanks
Comments in line
Private Sub Button2_Click(sender As Object, e As EventArgs) Handles Button2.Click
page.Receive("Joe")
End Sub
'A form level variable to hold a reference to the instance of TicTacToe
'Although vb.net can use default instances, you have created an explicit
'instance of TicTacToe so you need to keep a reference if you want to
'refer to this instance.
Private page As TicTacToe
Private Sub MenuTicTacToe(ByVal sender As Object, ByVal e As System.EventArgs)
page = New TicTacToe()
page.Show(Me)
End Sub
Partial Public Class TicTacToe
Inherits Form
Public opponent As String
'Functions must be declared as a Type
'If you do not need a return value use a Sub
Public Function Receive(S As String) As String
Dim someString As String = ""
If S = opponent Then
'Do something
End If
'There must be a return Value
Return someString
End Function
End Class
Use this to show the form
Dim page As TicTacToe
Private Sub MenuTicTacToe(ByVal sender As Object, ByVal e As System.EventArgs)
page = New TicTacToe
page.Show(Me)
End Sub
Then you can use
page.Receive(S)
Edit
To use multiple forms
For Each f As TicTacToe in Application.OpenForms().OfType(Of TicTacToe)
f.Receive (S)
Next
In C#, you'd need a new instance, but as you are in VB, the compiler already does that for you.
What you are currently doing, is creating a new instance of the TicTacToe form and showing it:
Private Sub MenuTicTacToe(ByVal sender As Object, ByVal e As System.EventArgs)
Dim page As Form = New TicTacToe
page.Show(Me)
End Sub
But you don't save that instance anywhere. Then, in your next piece of code, you are using a different instance, which is the static one created by the compiler:
TicTacToe.Receive(S) // TicTacToe is the static instance
Therefore, you end up calling two different instances, which explains why there is no opponent set.
To get around this problem, do not create a new instance. In your Private Sub MenuTicTacToe, just use the instance created by the compiler, and you won't have this problem, just like this:
Private Sub MenuTicTacToe(ByVal sender As Object, ByVal e As System.EventArgs)
TicTacToe.Show(Me)
End Sub
Hope this helps.
I am having an issue when trying to delete ListView Items from a second form.
For example, if I use the following command on Form1 it works:
Listview1.SelectedItems(0).Remove
However, if I attempt to remove from Form2 like so:
Form1.Listview1.SelectedItems(0).Remove
I get the following error:
"Invalid argument=value of '0' is not valid for 'index'. Parameter name: index"
I then tried to get a count of items from the listview on Form2 and it gives me a return of 0
Form1.Listview1.Items.Count
I'm not sure what my problem is.
Update
I have posted a brief example of my code (using your suggestion as I can understand it):
frmShowMessages
Private Sub ViewMessage()
Dim frm As New frmViewMailMessage
frm.Show()
End Sub
Public Sub DeleteItem(ByVal index As Integer)
lsvReceivedMessages.Items(index).Remove()
End Sub
frmViewMessage
Private instanceForm as frmShowMessages
Private Sub frmViewMailMessage_Load(sender As Object, e As EventArgs) Handles MyBase.Load
instanceForm = New frmShowMessages()
End Sub
Private Sub cmdDelete_Click(sender As Object, e As EventArgs) Handles cmdDelete.Click
instanceForm.DeleteItem(_index)
End Sub
Hopefully my code can help identify where my issue is.
In VB.net usually you get a default Form instance for each of your Form. Probably you are creating an instance of Form1 and then you are trying to access ListView1 of default instance.
E.g.
Sub ButtonClick()
Dim f As New Form1()
f.Show()
' at this point if you access f's ListView you will get correct count
f.ListView1.Items.Count
' however if you try to access default instance it will NOT have any item
Form1.ListView.Items.Count
End Sub
It means your instance f is NOT equal to default Form1 instance.
Solution can be, make the f variable as class level variable and use it everywhere. Or if Form1 will have only 1 instance, then you can use the default instance everywhere.
Personally I would NOT go with direct control accessing over forms. I would create a Public method which should return the data as list to the caller, in this case your Form2.
UPDATED-2:
As per your given scenario, I am simplifying things for you, and doing implementation using Event.
Public Class frmShowMessages
Private Sub btnOpenMessage_Click(sender As System.Object, e As System.EventArgs) Handles btnOpenMessage.Click
Dim frmView As New frmViewMessage(Me.ListView1.SelectedItems(0).Index)
AddHandler frmView.MessageDeleted, AddressOf DeleteMessageHandler
frmView.Show()
End Sub
Private Sub DeleteMessageHandler(sender As Object, e As frmViewMessage.MessageDeletedEventArgs)
Me.ListView1.Items.RemoveAt(e.MessageIndex)
End Sub
End Class
Public Class frmViewMessage
' a class which will be used for Event communication
Public Class MessageDeletedEventArgs
Inherits EventArgs
Public Property MessageIndex As Integer
Public Sub New(ByVal iIndex As Integer)
MyBase.New()
Me.MessageIndex = iIndex
End Sub
End Class
' main event which will alert the parent that a message deletion should be done
Public Event MessageDeleted As EventHandler(Of MessageDeletedEventArgs)
' private variable that will hold the MessageIndex
Private Property MessageIndex As Integer
' method that is responsible to raise event
Protected Overridable Sub OnMessageDeleted()
RaiseEvent MessageDeleted(Me, New MessageDeletedEventArgs(Me.MessageIndex))
End Sub
' we want to create this Form using the MessageIndex of ListView
Public Sub New(ByVal iMessageIndex As Integer)
Me.InitializeComponent()
Me.MessageIndex = iMessageIndex
End Sub
' the delete button will raise the event to indicate parent that
' a deletion of message should be done
Private Sub btnDelete_Click(sender As System.Object, e As System.EventArgs) Handles btnDelete.Click
Me.OnMessageDeleted()
End Sub
End Class
Long story short, I'm having a hell of a time trying to figure out how to use invoke and/or delegates to update the userform from a separate class when using threading. I'm quite sure it's something silly and obvious to someone with more experience. I know a delegate is probably required, but all my efforts seem to only work when it's being called from main thread. I've been looking around the internet for half the day, and there's just something I'm not getting.
Here's some pseudo-code as an example:
This option works:
Public Class Form1
Private Sub Button1_Click(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles Button1.Click
Dim t1 As New Threading.Thread(AddressOf Count)
t1.IsBackground = True
t1.Start(100)
End Sub
Private Sub Count(ByVal Max As Object)
If TypeOf Max Is Integer Then
Count(CInt(Max))
End If
End Sub
Private Sub SetLabelText(ByVal text As String)
If Label1.InvokeRequired Then
Label1.Invoke(New Action(Of String)(AddressOf SetLabelText), text)
Else
Label1.Text = text
End If
End Sub
Private Sub Count(ByVal Max As Integer)
For i = 1 To Max
SetLabelText(CStr(i))
Threading.Thread.Sleep(200)
Next
End Sub
End Class
While this (one of my 1000 efforts of slightly different variation) does not. Practically speaking, I just tried to separate one of the subs into its own class for this example, but it's otherwise the same as I could make it:
Public Class Form1
Private Sub Button1_Click(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles Button1.Click
Dim t1 As New Threading.Thread(AddressOf Count)
t1.Start(100)
End Sub
Private Sub Count(ByVal Max As Object)
If TypeOf Max Is Integer Then
Dim class2 As New class2
class2.Count(CInt(Max))
End If
End Sub
Private Delegate Sub SetTextBoxTextInvoker(text As String)
Sub SetLabelText(ByVal text As String)
'or me.label1, form1.label1 or anything else I can try!
If Me.InvokeRequired Then
Me.Invoke(New SetTextBoxTextInvoker(AddressOf SetLabelText), _
text)
Else
Me.Label1.Text = text
End If
End Sub
End Class
Public Class class2
Sub Count(ByVal Max As Integer)
For i = 1 To Max
form1.SetLabelText(CStr(i))
Threading.Thread.Sleep(200)
Next
End Sub
End Class
From what I can tell, it appears that the if statement for invokerequired in the Sub "SetLabelText" never gets triggered. My best guess is that I'm not referring to the userform correctly when checking for the invokerequired parameter? Or I need to feed something else to the delegate? I'm just getting frustrated with messing around with the million little variables I might be getting wrong. Thanks in advance for any help you can provide and let me know if you need more info.
I'm not certain I understand what you are trying to do, but building upon your code, you can set the label safely ("thread-safely") by using the following code:
Public Class Form1
Private Sub Button1_Click(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles Button1.Click
Dim t1 As New Threading.Thread(AddressOf Count)
t1.IsBackground = True
t1.Start(100)
End Sub
Private Sub Count(ByVal Max As Object)
If TypeOf Max Is Integer Then
Dim class2 As New Class2
class2.Count(CInt(Max), AddressOf SetLabelText)
End If
End Sub
Private Sub SetLabelText(ByVal text As String)
If Label1.InvokeRequired Then
Label1.Invoke(New SetText(AddressOf SetLabelText), text)
Else
Label1.Text = text
End If
End Sub
End Class
Public Class Class2
Sub Count(ByVal Max As Integer, SetTextMethod As SetText)
For i = 1 To Max
SetTextMethod.Invoke((CStr(i)))
Threading.Thread.Sleep(200)
Next
End Sub
End Class
Public Delegate Sub SetText(text As String)
I created a Delegate called "SetText"; when the form calls the count function in your class, you can pass an instance of the delegate that references the SetLabelText method. Within that method you can then safely set the label text either directly or indirectly via Invoke along with a new instance of the delegate.
Something you definitely don't want to do is reference your form from your class(i.e. "form1.SetLabelText(CStr(i))"); that can create a real nightmare as the project grows in size and requirements change!
If I've misunderstood your question or not answered it properly, please do post back.
First off I would suggest using the Task Parrallel Library instead of threads. It's easier to understand and work with. For example,
Dim countTask as New Task(Sub() Count(10))
Dim displayTask = countTask.ContinueWith(Sub()
Me.Invoke(Sub() Label.Text = "10"
End Sub)
countTask.Start()
This example assumes you are calling this from the form itself. You can use this method to return values from the first task to the second. If you need a more detail example and want more examples check out http://msdn.microsoft.com/en-us/library/hh228603.aspx. If you need further help I can always throw something up on GitHub or blog about it. Good Luck.
I have a class as seen below:
Public Class parameters
Public Property test As String
Public Property test_type As String
Public Property user_test_name As String
Public Property meas As String
Public Property spec As String
...etc
End Class
I make a list of objects that I import from a csv somewhere. The user_test_name's from the list gets sent to a list box:
For Each parameters In param
' MsgBox(parameters.user_test_name)
ListBox1.Items.Add(parameters.user_test_name)
Next
now when the user selects something from the list i want the rest of the properties of that particular user_test_name object to populate in certain text/combo boxes in the application. Here is how I grab what is selected.
Private Sub ListBox1_SelectedIndexChanged(ByVal sender As System.Object,
ByVal e As System.EventArgs) Handles ListBox1.SelectedIndexChanged
Dim selected_name As String = ListBox1.SelectedItem()
' MsgBox(selected_name)
find_object_by_user_test_name(selected_name)
End Sub
Now i'm having difficulty finding the object with the selected_name from the list and using its properties to fill the text.combo boxes. I tried the following to no success:
Public Sub find_object_by_user_test_name(ByVal description)
MsgBox(description)
Dim matches = From parameters In param
Where parameters.user_test_name = description
Select parameters
' MsgBox(matches)
' MsgBox(matches.user_test_name)
TextBox1.Text = matches.test
TextBox2.Text = matches.test_name
etc,,, on and on
' populate_area(matches)
End Sub
Instead of adding the name (a string) to the ListBox, add your actual INSTANCE to it.
First, override ToString() in your class so that it displays properly in your ListBox:
Public Class parameters
Public Property test As String
Public Property test_type As String
Public Property user_test_name As String
Public Property meas As String
Public Property spec As String
Public Overrides Function ToString() As String
Return user_test_name
End Function
End Class
Next, add each instance to the ListBox:
For Each parameters In param
ListBox1.Items.Add(parameters)
Next
Now, the SelectedIndexChanged() event, you can cast the SelectedItem() item back to parameters and you already have everything at your disposal:
Private Sub ListBox1_SelectedIndexChanged(ByVal sender As System.Object,
ByVal e As System.EventArgs) Handles ListBox1.SelectedIndexChanged
If ListBox1.SelectedIndex <> -1 Then
Dim P As parameters = DirectCast(ListBox1.SelectedItem, parameters)
' ... do something with "P" ...
Debug.Print(P.user_test_name & " --> " & P.test)
End If
End Sub
If the user_test_names are unique, it may be easier to use a dictionary and retrieve the objects that way.
Dim params As New Dictionary(Of String, parameters)
params.add(MyParameterObject.user_test_name, MyParameterObject)
Then
Private Sub ListBox1_SelectedIndexChanged(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles ListBox1.SelectedIndexChanged
Dim selected_name As String = ListBox1.SelectedItem()
Dim selected_param as parameters = params(selected_name)
End Sub
Something like this should do it:
Public Sub find_object_by_user_test_name(ByVal description As String)
' assuming param is your list of parameter objects
For Each p In param
If p.user_test_name = description Then
TextBox1.Text = p.test
TestBox2.Text = p.test_name
...
Exit For
End If
Next
End Sub
A few notes about your code. First, you can't allow two objects to have the same user_test_name. Second, you can't use parameters as a variable name if you already have a class called parameters in your current namespace (which you do).
There is a simpler solution than any of these--just do the following:
Dim i as Integer = ListBox1.SelectedIndex
Then you can use i as an index to your original list of objects.
I need to build an application that could run code in private function, based on what user has selected using combobox.
For example combo box has three values, One, Two, Three
If user selects one, code written under Private Function One() runs and vise versa
Thanks
Furqan
An easier way would be to assign a function for when the combo box is selected. Inside your function have a select statement like: (Pesduo)
Function comboSelected
Case "One"
call Onefunction()
Case "Two"
call Twofunction()
End function
Why are you declaring these as private?
Form controls cannot access private functions. You should declare them as protected.
Here's a way to make it work - assuming Windows Forms.
First, define this class:
Public Class ComboAction
Public Sub New(ByVal text As String, ByVal action As Action)
_text = text
_action = action
End Sub
Private _text As String
Public ReadOnly Property Text() As String
Get
Return _text
End Get
End Property
Private _action As Action
Public ReadOnly Property Action() As Action
Get
Return _action
End Get
End Property
Public Overrides Function ToString() As String
Return Me.Text
End Function
End Class
Now create a form like this:
Public Class ComboActionForm
Private Sub ComboActionForm_Load(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles MyBase.Load
Me.ComboBox1.Items.Add(New ComboAction("Show Foo", AddressOf Foo))
Me.ComboBox1.Items.Add(New ComboAction("Show Bar", AddressOf Bar))
End Sub
Private Sub Foo()
System.Windows.Forms.MessageBox.Show("Foo")
End Sub
Private Sub Bar()
System.Windows.Forms.MessageBox.Show("Bar")
End Sub
Private Sub ComboBox1_SelectedIndexChanged(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles ComboBox1.SelectedIndexChanged
CType(Me.ComboBox1.SelectedItem, ComboAction).Action.Invoke()
End Sub
End Class
You can add as many ComboAction classes to the ComboBox as you wish. Each can have any Action you define - private methods or otherwise. The sky is the limit. :-)
See my another post. This works great!