Passing Array to Another Form (vb.net) - vb.net

I got a form which declared an array in public,
Public requestedqueue() As Integer
I would like to pass the array to form3 and perform other calculations there, but how?
I tried doing(at a new form):
public newrequest() As Integer
newrequest = form2.requestedqueue
I tried to show it at a new form by doing:
TextBox1.Text = = String.Join(",",form2.newrequest)
But whenever I run into form3 it would say the newrequest is null.
But it shows as an array in form2, Im so confused.

I'm not sure what you mean by
But it shows as an array in form2
but newrequest will be Nothing because you have set it equal to the value of requestedqueue which is Nothing until you populate the array with some values.
If you had Public requestedqueue() As Integer = {1, 2, 3} then you would not encounter the error.

One way to pass data to a form is to add a property to the second form.
Lets say you have a form called Form3 and this is your form's code. In the code you will need to declare an array of integer to hold the passed data, and also declare a public property so you have a way of passing the array
Public Class Form3
Dim requestedqueue() As Integer
Public Property ArrayParameter As Integer()
Get
Return arrayData
End Get
Set(value() As Integer)
arrayData = value
End Set
End Property
End Class
Then, to pass the data from Form1, in form1, you would simply use
Form3.ArrayParameter=requestedqueue()
to set the parameter.
and if you wish, you can show the form as normal, or if the form is already visible, you can process the code using button clicks etc.
If you want to process the data in an already open form immediately without any user interaction, you can write a Procedure that does the processing and include that in the Set portion of your property.
For example. If you want to add all the elements of the array to a ListBox called ListBox1 in Form3, you could write a procedure like this..
Private Sub AddDataToListbox()
ListBox1.Items.Clear()
For Each item As Integer In requestedqueue
ListBox1.Items.Add(item)
Next
End Sub
and change your Form3.ArrayParameter code to this
Public Property ArrayParameter As Integer()
Get
Return requestedqueue
End Get
Set(value() As Integer)
requestedqueue = value
AddDataToListbox()
End Set
End Property

Related

ToString not updating on object when altering ListBox in VB

I have a form that alters the content of a class within a list box. The information is updated correctly, but my ToString override on my object doesn't refresh - meaning the old ToString doesn't change. How would I fix this?
Here's my object:
Public Class Destination
Public strDestinationName As String
Public strAddress As String
Public intQuality As Integer
Public intPrice As Integer
Public Overrides Function ToString() As String
Return strDestinationName
End Function
End Class
Here's the code where it should be updated
Dim selectedDestination As Destination
selectedDestination = CType(ListForm.lbNames.SelectedItem, Destination)
selectedDestination.strDestinationName = tbName.Text
selectedDestination.strAddress = tbAddress.Text
selectedDestination.intPrice = cbPrice.SelectedIndex
selectedDestination.intQuality = cbQuality.SelectedIndex
Me.Close()
Regardless of how you add items to a ListBox, it is the ListBox that actually displays the data. In your case, it appears that you are adding Destination objects to the ListBox somehow, given that the SelectedItem is a Destination object. Given that you have written that ToString method, you are presumably relying on that to produce the text that the ListBox displays for each item. You are now expecting to be able to change the value of the strDestinationName field of one of the items and have the ListBox reflect that change. How exactly do you think that is going to happen?
The ToString method has to be called in order to get the new value and who do you think is going to call it? It would be the ListBox that calls it because it is the ListBox that displays the result. When you change that field, you are expecting the ListBox to call your ToString method but why would it do that? What reason has the ListBox got to call that method? It has no knowledge of the change you made so why would it think that it has to get new data?
The solution to your problem is to change your code in some way to notify the ListBox that data has changed so that it knows that it needs to get that new data and display it. There are multiple ways that you could do that.
The simplest option would be to bind your data to the ListBox via a BindingSource and then, when you modify an item, call the ResetItem method or similar of the BindingSource. That will raise an event that is handled by the ListBox and the ListBox then knows that it needs to refresh the data for that item. That is what will prompt the ListBox to call your ToString method and get the new data to display. You would add the BindingSource to the form in the designer and then do the binding where you are currently adding the items, e.g.
Dim destinations As New List(Of Destination)
For i = 1 To 10
Dim d As New Destination
d.strDestinationName = "Destination " & i
destinations.Add(d)
Next
destinationBindingSource.DataSource = destinations
destinationListBox.DataSource = destinationBindingSource
The modification would look something like this:
Dim selectedDestination = DirectCast(destinationBindingSource.Current, Destination)
selectedDestination.strDestinationName = "New Destination"
destinationBindingSource.ResetCurrentItem()
The Current property returns the item currently selected in the bound UI and the ResetCurrentItem method notifies the bound UI to refresh the display of that item.
This is really not the best way to go about it though, given that you have control over the item type. What you ought to do is implement the type using properties rather than fields, get rid of the ToString method that only returns the value of one property and then add a change event to that property:
Public Class Destination
Private _destinationName As String
Public Property DestinationName As String
Get
Return _destinationName
End Get
Set(value As String)
If _destinationName <> value Then
_destinationName = value
OnDestinationNameChanged(EventArgs.Empty)
End If
End Set
End Property
Public Property Address As String
Public Property Quality As Integer
Public Property Price As Integer
Public Event DestinationNameChanged As EventHandler
Protected Overridable Sub OnDestinationNameChanged(e As EventArgs)
RaiseEvent DestinationNameChanged(Me, e)
End Sub
End Class
You can now bind a list of Destination objects directly and specify any of those properties as the DisplayMember to have that property value displayed:
Dim destinations As New List(Of Destination)
For i = 1 To 10
Dim d As New Destination
d.strDestinationName = "Destination " & i
destinations.Add(d)
Next
destinationListBox.DisplayMember = "DestinationName"
destinationListBox.DataSource = destinations
You don't need the ToString method because the DisplayMember specifies that the value of the property with that name should be displayed. When you modify the value of the DestinationName property of an item, it will raise the DestinationNameChanged event and that will notify the ListBox that it needs to refresh the display for that item, so you don't need any additional code to make the ListBox update.
That's fine if you only plan to modify existing items. There's still a problem if you want to add and/or remove items after binding though. The List(Of T) class that is used to bind the items to the control in this example does not have any events to notify the control of changes to the list like that. In that case, you can use a BindingSource again if you want. If you add and remove items via the BindingSource then it will raise that appropriate events and the ListBox will update. If you wanted to add and remove via the underlying list then you'd have to call an appropriate method of the BindingSource when you made a change.
An alternative would be to use a BindingList(Of Destination) instead of a List(Of Destination). As the name suggests, the BindingList(Of T) class is made for binding, so it will automatically raise the appropriate events when the list changes to enable the UI to update without extra code from you. Using the combination of property change events in your item class and a BindingList(Of T), you can add, edit and remove items in the bound list and the UI will reflect those changes automatically.

Looping through dictionary of objects in vba

I'm writing code to instantiate a form that shows one record in each instance. I have functions to open and close instances using a dictionary but now I need to check whether a record is already opened.
Dictionaries and collections only allow you to store pairs of data (key/item) so created a class with two properties: form object and the opened record id. I store key and this object in a dictionary.
Now I want to check if a record id is already opened so I have to loop trough the dictionary checking the record id (servicioid in code below) property of the item.
Class module:
Private propFormulario As Form
Private propServicioId As Long
Public Property Let FormObj(frmFormObj As Form)
Set propFormulario = frmFormObj
End Property
Public Property Get FormObj() As Form
Set FormObj = propFormulario
End Property
Public Property Let servicioid(lngServicioId As Long)
propServicioId = lngServicioId
End Property
Public Property Get servicioid() As Long
servicioid = propServicioId
End Property
Open and close instances:
Public dicFormServicios As New Dictionary
Public Sub AbrirServicio(lngServicioId As Long)
Dim ServicioAbierto As clsServiciosAbiertos
Set ServicioAbierto = New clsServiciosAbiertos
ServicioAbierto.FormObj = New Form_servicios2
ServicioAbierto.servicioid = lngServicioId
dicFormServicios.Add CStr(ServicioAbierto.FormObj.hwnd), ServicioAbierto
ServicioAbierto.FormObj.visible = True
End Sub
Public Sub CerrarServicio(InstanciaHwnd As Long)
If dicFormServicios.Exists(CStr(InstanciaHwnd)) Then
dicFormServicios.Remove CStr(InstanciaHwnd)
End If
End Sub
My question is how do I loop trough the dictionary and how do I check an ID is in the servicioid property of any item.
My VBA is a bit rusty, so you're going to want to do something along the lines of...
dicFormServicios.Add myForm.FormId, myForm
Then to recover a value try...
myReturnForm = dicFormServicios.Item("SomeFormName")
Details here...
Dictionary Object
Whether a value exists
Recover an item from the dictionary
(The Dictionary reference above is very useful, but really...) All of the objects that you need are already there. You can reference an object directly from the Forms collection using either an index number...
Set myForm = Forms![0]
...or by using the form's name...
Set myForm = Forms!["myFormName"]
(Such a long time since I've done any of this stuff!)

Pass value from form to button text vb.net

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).

Linking Object to UI

What is the best method to use to link a class to a user form?
Sorry to be hypothetical, but using actual code will lead to a question that is pages long.
Let's say I have a class that holds data about a person.
<<class Person>>
Public FirstName as String
Public LastName as String
Public PhoneNumber as String
<<end class>>
I put that data into a VBA UserForm listview.
Now, let's say I want to change the phone number to 555-555-1234 if the user clicks on that record in the listview.
I can read the interaction with the listview with the item click event.
Private Sub lvExample_ItemClick(ByVal Item As MSComctlLib.ListItem)
' Change the phone number
End Sub
What is the best way to get from Item in the above code to my actual object? Should I add an GUID to each object and put that in the tag of the listitem, then look it up? Should I add the listitem from the listview into the class so I can loop through all my people and then see if the Item from _ItemClick equals the Item from the object?
The easiest way is to use either the Index property, if you don't have any unique identifier, or the Key property if you do, of the ListItem.
If you choose to use the Index property, then you can't (or at least it will greatly complicate it) add any functionality to rearrange the order of list items.
You would have populated the ListView based on the objects in a collection/recordset via the ListView.ListItems.Add method. You can use the Index property to get back to that original object based on the order of items in ListItems corresponding to the order of items in your original collection of objects.
Alternatively, if you prefer the greater flexibility of using a unique key but don't wish to modify the underlying object, then you can trivially construct a unique key (the simplest being CStr(some incrementing number)) as you add each object to ListItems, storing the keys alongside the objects.
You can then use the .Key property of the ListItem. The benefit here is the user can be allowed to modify what items are in, delete stuff etc without you having to invalidate your control and re-add all objects in order to keep the linkage between Index in source and index in the list.
E.g.:
Private persons As Collection
Private Sub lvExample_ItemClick(ByVal Item As MSComctlLib.ListItem)
' Change the phone number:
'Method 1, using the index of listitem within listitems
'to tie back to index in persons collection
'Don't allow rearranging/sorting of items with this method for simplicity
With persons.Item(Item.Index)
.PhoneNumber = "555-555-1234"
'...some stuff
End With
'Method 2, using a unique key
'that you generate, as the underlying person object doesn't have a necessarily unique one
'Items would have been added in a method similar to AddItemsExample1() below
With persons.Item(Item.Key)
.PhoneNumber = "555-555-1234"
'...some stuff
End With
End Sub
Sub AddItemsExample1()
'Storage requirements vs. your existing recordset or whatever
'are minimal as all it is storing is the reference to the underlying
'person object
'Adapt per how you have your existing objects
'But basically get them into a keyed collection/dictionary
Dim i As Long
Set persons = New Collection
For Each p In MyPersonsSource
persons.Add p, CStr(i)
i = i + 1
Next p
'By keying them up, you can allow sorting / rearranging
'of the list items as you won't be working off of Index position
End Sub
Finally, another way if you have them in a recordset returned by a DB is to add a new field (I imagine you have an existing object field) and do run an UPDATE query on your records populating it with an incrementing number (this should only effect the local recordset (check the recordset settings first of course!)). Use this as the key.
You mention in a comment to your question that you get the data from SQL. For all normal purposes with a list box it is still probably easiest to just run them through a Collection object as detailed above, but if you have e.g. 4 fields from SQL for a record in a recordset then you don't hav an 'object' in the sense of being able to call properties on it. Please specify in your question or in a comment to this if you do so, as there may be a better treatment to answer your question or the actual update operation will likely require different syntax or semantics (particularly if you need to propagate any update back to the source) :)
I would use a event driven approach.
Person will have properties instead of variables and assigning those properties will raise a event.
User form will have WithEvents Person, so that changes in related person will trigger user form code.
Person class:
Public Event Changed()
Private pFirstName As String
Private pLastName As String
Private pPhoneNumber As String
Public Property Get FirstName() As String
FirstName = pFirstName
End Property
Public Property Let FirstName(ByVal v As String)
pFirstName = v
RaiseEvent Changed
End Property
Public Property Get LastName() As String
LastName = pLastName
End Property
Public Property Let LastName(ByVal v As String)
pLastName = v
RaiseEvent Changed
End Property
Public Property Get PhoneNumber() As String
PhoneNumber = pPhoneNumber
End Property
Public Property Let PhoneNumber(ByVal v As String)
pPhoneNumber = v
RaiseEvent Changed
End Property
Event catching in user form:
Public WithEvents ThisPerson As Person
Private Sub ThisPerson_Changed()
'Update user form
End Sub
So whenever you assign YourForm.RelatedObject = SomePerson, any changes done to SomePerson will trigger code in YourForm.
Since you can't change returned ListItem, I have another solution:
Add a related ListItem to your Person class:
Public FirstName as String
Public LastName as String
Public PhoneNumber as String
Public RelatedObject As Object
When populating TreeView, assign it:
Set SomeListItem = SomeTreeView.ListItems.Add(...)
Set SomePerson.RelatedObject = SomeListItems
So whenever you have a change in a ListItem:
Private Sub SomeListView_ItemClick(ByVal Item As MSComctlLib.ListItem)
Dim p As Person
For Each p In (...)
If p.RelatedObject Is Item Then
'Found, p is your person!
p.PhoneNumber = ...
End If
Next
End Sub
Alternatively, if you don't want to change your Person class, you can encapsulate it in another class, eg, PersonToObject:
Public Person As Person
Public RelatedObject As Object
And use PersonToObject as link objects.

Pass variable From child to parent, Like a Function VB.NET

I have created 2 forms (Parent & Child), i want to store the unique textbox value to a variable into the parent form.
Also like this:
Parent Code:
dim passed_value = new childform()
passed_value.show()
On close:
refresh passed_value variable using childform textbox value.
You can Do the Following to accomplish the Task :
Declare a String variable in the child form.
Public value As String
Use ShowDialog() in the Main Form to show the Child form.
Dim frm As New Form2
frm.ShowDialog()
[Set the value in your form as per your needs]
value = "New Value"
Now get the value variable from the Child form and set the textbox text according to it.
TextBox1.Text = frm.value
I would make it a property and give it the necessary access, which to me looks to be ReadOnly.
Private _myValue As DataType
Public ReadOnly Property MyValue() As DataType
Get
Return _myValue
End Get
You could access it as such: myForm.MyValue.