Pass an object from form to another in VB - vb.net

I have searched through the internet and couldn't find the answer to my problem, but, the issue is that I have 2 forms;
frm_bookManeger
and
frm_addBook
The first one is the main form and has a list of books (named listBook), a TreeView and a button to invoke the second form to add a new book.
After filling in all of the TextBoxes and information of a book, I press "Add". Then, the second form will be closed and all info of that book will be kept in an instance of Book class. The problem is: how can I pass this instance to the first form to store it in listBook.
For example:
If I create a constructor in form 1 to get form 2 then in form 2:
Dim f1 As form1 = New form1(me)
f1.Show()
f2.Close()
I can't do it because form 1 will start up instantly when I start program, and the default right now doesn't have any parameter in OnCreateMainForm():
Protected Overrides Sub OnCreateMainForm()
Me.MainForm = Global.WindowsApplication5.frm1
End Sub
How can I do it?
First form:
Public Class frm_bookManeger
'list of Book
Dim listBook As List(Of Book) = New List(Of Book)
Private frm_addBook As frm_addBook
Public Sub New(frm_addBook As frm_addBook) 'got error
Me.frm_addBook = frm_addBook
End Sub
Second form:
Public Class frm_addBook
Dim Public tempBook As Book = New Book()
'add book
Private Sub btn_add_Click(sender As Object, e As EventArgs) Handles btn_add.Click
tempBook.BookName1 = TextBox_name.Text
tempBook.Author1 = TextBox_author.Text
tempBook.Price1 = TextBox_price.Text
tempBook.Genre1 = TextBox_genre.Text
tempBook.EstablishedDay1 = dtp_established.Value.Date
Dim frm_Mngr As frm_bookManeger = New frm_bookManeger(Me)
End Sub
End Class

Dim frm As New form1
frm.textbox.Text = Me.passing value.Text
frm.Show()
or you can try
Public Class Form1
Private loginLabel As String
Public Sub New(ByVal loginParameter As String)
InitializeComponent()
Me.loginLabel = loginParameter
End Sub
End Class
dim frm as new Form1(label.Text)

Your frm_addBook needs a reference to the instance of frm_bookManeger so that it can use methods in the latter.
That can be done by passing a reference to the current instance of frm_bookManeger to the New constructor of frm_addBook.
Also, you probably want the book adding form to be a dialog form rather than an ordinary form.
I made a simple "Book" class and used a TextBox to display the books, so the first form is this:
Imports System.Text
Public Class frm_BookManager
Dim bookList As List(Of Book)
Public Class Book
Property Name As String
Property Author As String
End Class
Public Sub AddBook(b As Book)
If bookList Is Nothing Then
bookList = New List(Of Book)
End If
bookList.Add(b)
End Sub
Private Sub ShowBooks()
Dim sb As New StringBuilder
For Each b In bookList
sb.AppendLine(b.Name & " by " & b.Author)
Next
TextBox1.Text = sb.ToString()
End Sub
Private Sub btn_add_Click(sender As Object, e As EventArgs) Handles btn_add.Click
Using addBook As New frm_addBook(Me)
Dim result = addBook.ShowDialog()
If result = DialogResult.OK Then
ShowBooks()
End If
End Using
End Sub
Private Sub frm_BookManager_Load(sender As Object, e As EventArgs) Handles MyBase.Load
AddBook(New Book With {.Name = "Wuthering Heights", .Author = "Emily Brontë"})
ShowBooks()
End Sub
End Class
For the form to add a book, I added "Cancel" and "OK" buttons.
Public Class frm_addBook
Dim myParent As frm_BookManager
Private Sub bnOK_Click(sender As Object, e As EventArgs) Handles bnOK.Click
Dim b As New frm_BookManager.Book With {.Name = TextBox_name.Text, .Author = TextBox_author.Text}
myParent.AddBook(b)
End Sub
Public Sub New(parent As frm_BookManager)
' This call is required by the designer.
InitializeComponent()
' Add any initialization after the InitializeComponent() call.
myParent = parent
' set the DialogResult for each button so the parent can tell what happened
bnCancel.DialogResult = DialogResult.Cancel
bnOK.DialogResult = DialogResult.OK
End Sub
End Class
Notice that a new Book can be added with myParent.AddBook(b) because myParent refers to an instance of frm_BookManager.
You could modify it so that the dialog stays open and has a button to just add a book and not close the dialog. I made the ShowBooks() method Private so you can't call it from outside the class it is in - you could modify that.
There are many possibilities for small modifications to the code I showed to achieve greater functionality. And I could not resist correcting the spelling of "Maneger" to "Manager" ;)

I think the easiest way would be to have the frm_addBook form have a property which will contain the book that was added. In the frm_bookManager form, show that form using ShowDialog and if the user clicks OK on that form, the property will contain the book added. Be sure to dispose the frm_addBook form after you get the book from the public property.
Public Class Book
Public Property Name As String
Public Property Author As String
End Class
Public Class frm_bookManager
Dim bookList As New List(Of Book)()
Private Sub btnAddBook_Click(sender As Object, e As EventArgs) Handles btnAddBook.Click
Using addBookForm As New frm_addBook()
If addBookForm.ShowDialog() = DialogResult.OK Then
bookList.Add(addBookForm.BookToAdd)
End If
End Using
End Sub
End Class
Public Class frm_addBook
Public Property BookToAdd As Book
Private Sub btnOK_Click(sender As Object, e As EventArgs) Handles
'User filled in the fields and clicked this OK button
Me.BookToAdd = New Book()
Me.BookToAdd.Name = txtName.Text
Me.BookToAdd.Author = txtAuthor.Text
End Sub
End Class
I would not pass the main form instance into the add book form because it would create a tight coupling between the two forms and the add book form would only be usable by the main form. You might wish to use the add book form from other forms in the app.

Related

Sharing variables across classes and forms

I have a class called DataStorage that stores all of the publicly declared variables that I use in my program. My main class calls a public array from DataStorage and fills it with values. The main class then calls up a windows form and populates the textboxes on it with the values in the array. However, when I run the program, the form comes up with blank textboxes. I don't understand why this would be the case. I also tried populating the textboxes from the load function of the form rather than in the main class. I debugged and stepped through my code, and the main class filled the array with values fine. But when the form was called, and I started stepping through the code to populate the textboxes, it said the array was equal to nothing. If the array is stored in a separate class that both the main class and the form call, why are the values getting set to null between the main class and the form?
Here is what my code looks like:
Public Class Main
Private ds As New DataStorage
Public Sub Test()
For i = 0 to 2
ds.Info(i) = "Hello"
Next
Form1.ShowDialog()
End Sub
End Class
Public Class DataStorage
Public Info() As String
End Class
Public Class Form1
Private ds As New DataStorage
Private Sub Form1_Load(sender As Object, e As EventAgrs) Handles MyBase.Load
Textbox1.Text = ds.Info(0)
Textbox2.Text = ds.Info(1)
Textbox3.Text = ds.Info(2)
End Sub
I just called it Form1; the program starts with the sub Test in the main class.
I just need a way to have variables/arrays that are accessible by any class or form and don't change value or get set to null unless I tell it to.
You must give your array a size before you try to fill it.
Public Class TestArray
Private ds As New DataStorage
Private Sub TestArray_Load(sender As Object, e As EventArgs) Handles MyBase.Load
FillArray()
TextBox1.Text = ds.Info(0)
TextBox2.Text = ds.Info(1)
TextBox3.Text = ds.Info(2)
End Sub
Public Sub FillArray()
For i = 0 To 2
ds.Info(i) = "Hello"
Next
End Sub
End Class
Public Class DataStorage
Public Info(2) As String
End Class
If you don't want to give a size then use a List(Of T)
Public Class TestArray
Private ds As New DataStorage
Private Sub TestArray_Load(sender As Object, e As EventArgs) Handles MyBase.Load
FillList()
TextBox1.Text = ds.Info(0)
TextBox2.Text = ds.Info(1)
TextBox3.Text = ds.Info(2)
End Sub
Public Sub FillList()
For i = 0 To 2
ds.Info.Add("Hello")
Next
End Sub
End Class
Public Class DataStorage
Public Info As New List(Of String)
End Class

How can i pass parameters from datagridview to an open or main form without opening new form

I was thinking how can i pass parameters using constructors from datagridview which is in another form to my main form without opening new one. here's my code
-----------Main form constructor----------------------------------------
Public Sub New(customerID As Integer, fullName As String, phoneNumber As String, emailID As String)
' This call is required by the designer.
InitializeComponent()
' Add any initialization after the InitializeComponent() call.
_customerID = customerID
_fullName = fullName
_phoneNumber = phoneNumber
_emailID = emailID
End Sub
-----------------------------second form edit button click ----------------------------
Private Sub btnEdit_Click(sender As Object, e As EventArgs) Handles btnEdit.Click
Dim formMain As New frmMain(CInt(Me.dgvCustomerInfo.Item(0, Me.dgvCustomerInfo.SelectedRows(0).Index).Value),
Me.dgvCustomerInfo.Item(1, Me.dgvCustomerInfo.SelectedRows(0).Index).Value,
Me.dgvCustomerInfo.Item(2, Me.dgvCustomerInfo.SelectedRows(0).Index).Value,
Me.dgvCustomerInfo.Item(3, Me.dgvCustomerInfo.SelectedRows(0).Index).Value)
Me.Hide()
'formMain.Hide()
formMain.Show()
Thanks
You could create a property in your second form which is a list of the type of value that you want to pass and then when you create the form, assign your values to the property. Finally process the property using code in the form2.Shown event like this
Public Class Form1
Private Sub test()
Dim f2 As New form2
f2.formparameters = {4, 5, 6, 7}
f2.Show()
End Sub
End Class
And in your 2nd form write something like this..
Public Class form2
Friend Property formparameters() As Integer()
Private Sub form2_Shown(sender As Object, e As EventArgs) Handles Me.Shown
'process parameter data here
Me.Update()
End Sub
End Class

Passing data between forms DIRECTLY

I'm making a "Preference form" that will hold all the users preferences and when they go to Apply/Save I want the new values to transfer back to the main form and updateand close the form2. In the past I have done this like this:
Private Sub PreferencesToolStripMenuItem_Click(sender As Object, e As EventArgs) Handles PreferencesToolStripMenuItem.Click
Preferences.Show()
End Sub
and when I click the "Apply/Save" button before it closes I would Transfer all data like this:
form1.textbox.text = form2.textbox.text
Is there anything wrong doing it this way??
What I have been reading is I should be doing it like this:
Private Sub PreferencesToolStripMenuItem_Click(sender As Object, e As EventArgs) Handles PreferencesToolStripMenuItem.Click
Dim dialog As New Preferences
dialog.ShowDialog()
End Sub
And when when they click "Apply/Save" it would take all the values from Form2 and store them in a private variable (or Property) in Form2 and when that form closes I would then access the value like this:
Private Sub PreferencesToolStripMenuItem_Click(sender As Object, e As EventArgs) Handles PreferencesToolStripMenuItem.Click
Dim dialog As New Preferences
dialog.ShowDialog()
form1.textbox.text = dialog.variable
End Sub
Why would this be a better way of doing this?
UPDATE....Looking at the code below this is just a SMALL sample of all the options I will have. What is the best way to collect of the data into the object to use when serializing?
<Serializable>
Public Class Preference
#Region "Properties"
Public Property ScaleLowest As String = "5"
Public Property ScaleHighest As String = "200"
Public Property ScaleInc As String = "5"
Public Property ThickLowest As Double = 0.125
Public Property ThickHighest As Double = 4
Public Property ThickInc As Double = 0.125
Public Property WidthLowest As Double = 0.125
Public Property WidthHighest As Double = 0.6
Public Property WidthInc As Double = 0.125
Public Property LengthLowest As Double = 1
Public Property LengthHighest As Double = 96
Public Property LengthInc As Double = 1
Public Property FractionON As Boolean = False
Public Property DecimalON As Boolean = True
Public Property ColorSelection As String = "Colors"
Public Property FinalColor As String = "255, 255, 0"
Public Property roughColor As String = "255, 255, 100"
Public Property SplashON As Boolean = False
Public Property LogInON As Boolean = False
#End Region
Public Sub New()
'for creating new instance for deserializing
End Sub
Public Sub GatherAllData()
'Save Defaults
SaveSerializeObj()
End Sub
Public Sub SaveSerializeObj()
'Get Changes?????
'Serialize object to a text file.
Dim objStreamWriter As New StreamWriter("C:\Users\Zach454\Desktop\test.xml")
Dim x As New XmlSerializer(Me.GetType)
x.Serialize(objStreamWriter, Me)
objStreamWriter.Close()
End Sub
Public Function LoadSerializeObj() As Preference
'Check if new file need created
If File.Exists("C:\Users\454\Desktop\test.xml") = False Then
SaveSerializeObj()
End If
'Deserialize text file to a new object.
Dim objStreamReader As New StreamReader("C:\Users\454\Desktop\test.xml")
Dim newObj As New Preference
Dim x As New XmlSerializer(newObj.GetType)
newObj = CType(x.Deserialize(objStreamReader), Preference)
objStreamReader.Close()
Return newObj
End Function
The best option is to create a class that would have properties for your form controls. Then you can store these properties and then access these when needed.
Also there's really no reason to be passing data back and forth, you can store this data off somewhere (database, file, mysettings etc) and then load this data up into a class. Then you can store and retrieve data from this class. Then if you need to save data back to somewhere you have a class object to use.
Here is a short example to show how you can create another form (Preferences) click save and then show those values back on the other form (calling form).
This is the main form
Public Class Form1
Public _frm2 As Form2
Private Sub btnShowPreferences_Click(sender As Object, e As EventArgs) Handles btnShowPreferences.Click
Using _frm2 As New Form2()
'if we clicked save on the form then show the values in the
'controls that we want to
If _frm2.ShowDialog() = Windows.Forms.DialogResult.OK Then
txtFirstName.Text = _frm2._Preferences.FirstName
txtLastName.Text = _frm2._Preferences.LastName
End If
End Using
End Sub
End Class
Here is an example (Preferences) class
This class would hold all your properties for the preferences. This is an example, you can change anything you need to suit your needs.
Option Strict On
Public Class Preferences
#Region "Properties"
Public Property FirstName As String
Public Property LastName As String
#End Region
Public Sub New()
End Sub
End Class
The second Form could be your (Preference) form with all the controls a user would need to interact with.
Public Class Form2
Public _Preferences As New Preferences 'create class variable you can use for later to store data
Private Sub btnSave_Click(sender As Object, e As EventArgs) Handles btnSave.Click
'set your properties of the class from your form. this will then hold everything you can get from
'the first form...
With _Preferences
.FirstName = txtFirstName.Text
.LastName = txtLastName.Text
End With
Me.DialogResult = Windows.Forms.DialogResult.OK 'this is used to determine if user clicked a save button...
End Sub
End Class
I hope this get's you started, if you do not understand something please let me know.
To directly answer your question, the main difference in your two code samples is that the second uses ShowDialog to open the form modally, vs the first sample which lets you interact with the parent form while the second is open.
The second approach may be better from the view of user flow control. If your real question is whether to push data back to the main form or pull data from the dialog, it is probably better to pull from the dialog. This approach makes the dialog reusable from other forms.

update listbox in GUI from other classes

I have a listbox on my main vb.net form which I am using to display status messages from the server program I am running. My actual program consists of many different classes (in separate files) and what I would like to be able to do is to call the Sub frm.UpdateList("With Info in Here") from each of the classes to write to the listbox.
If I call the frm.UpdateList or UpdateList from the frm class, it writes to the listbox fine, but if I call it from any other class nothing happens (I don't get an error either).
I have tried with and without making it shared (and changing frm to me) but neither works as I would hope.
Would anyone be able to help me understand why this is not working, I have invoked the item, and it does get added to but just not from a separate class (which is what I need it to do).
Many Thanks!
Private Delegate Sub UpdateListDelegate(ByVal itemName As String)
Public Shared Sub UpdateList(ByVal itemName As String)
If frm.InvokeRequired Then
frm.Invoke(New UpdateListDelegate(AddressOf UpdateList), itemName)
Else
frm.ListBox1.Items.Insert(0, DateTime.Now.ToString & ": " & itemName)
End If
End Sub
Edit: Try 2, with the following thanks to Idle_Mind works on the frm class (frm is the main form and only form) but it still does not write to the listbox when called from other classes (and no errors occur):
Public Shared Sub UpdateList(ByVal itemName As String)
Dim frm As Form = My.Application.ApplicationContext.MainForm
If Not IsNothing(frm) Then
Dim matches() As Control = frm.Controls.Find("ListBox1", True)
If matches.Length > 0 AndAlso TypeOf matches(0) Is ListBox Then
Dim LB As ListBox = DirectCast(matches(0), ListBox)
LB.Invoke(New MethodInvoker(Sub() LB.Items.Insert(0, DateTime.Now.ToString & ": " & itemName)))
End If
End If
End Sub
I have a listbox on my main vb.net form
This will only work on the startup form, and is not really a good design. Consider other approaches as well:
Public Class Form1
Private Sub Button1_Click(sender As Object, e As EventArgs) Handles Button1.Click
Dim soc As New SomeOtherClass
soc.Foo()
End Sub
End Class
Public Class SomeOtherClass
Public Sub Foo()
Dim msg As String = "Hello?!"
Helper.UpdateList(msg) ' <-- You can do this from any class...
End Sub
End Class
Public Class Helper
Public Shared Sub UpdateList(ByVal itemName As String)
Dim frm As Form = My.Application.ApplicationContext.MainForm
If Not IsNothing(frm) Then
Dim matches() As Control = frm.Controls.Find("ListBox1", True)
If matches.Length > 0 AndAlso TypeOf matches(0) Is ListBox Then
Dim LB As ListBox = DirectCast(matches(0), ListBox)
LB.Invoke(New MethodInvoker(Sub() LB.Items.Insert(0, DateTime.Now.ToString & ": " & itemName)))
End If
End If
End Sub
End Class
Other correct approaches, which would require more work on your part, might include:
(1) Pass a reference to your main form into the other classes as you create them. Then those classes can either up the ListBox directly, or possibly call a method in it as suggested by Plutonix. Here's an example of this in action:
Public Class Form1
Public Sub UpdateList(ByVal itemName As String)
ListBox1.Invoke(New MethodInvoker(Sub() ListBox1.Items.Insert(0, DateTime.Now.ToString & ": " & itemName)))
End Sub
Private Sub Button1_Click(sender As Object, e As EventArgs) Handles Button1.Click
Dim soc As New SomeOtherClass(Me)
soc.Foo()
End Sub
End Class
Public Class SomeOtherClass
Private _Main As Form1
Private Sub New()
End Sub
Public Sub New(ByVal MainForm As Form1)
_Main = MainForm
End Sub
Public Sub Foo()
If Not IsNothing(_Main) Then
_Main.UpdateList("Hello?!")
End If
End Sub
End Class
You'd have to modify all of your other classes in a similar fashion so that they can receive an instance of your form.
(2) Make the other classes raise a custom event that the main form subscribes to when those classes are created.

Using Generic List(Of Form), Trouble gathering Object's Name Property

I have been very interested as of late in interfaces and the ability to further customize them beyond using them in their default state.
I have been researching IList(of T) specifically. The advantages of using generic lists as opposed to ArrayLists has astounded me. Here is a picture of a test. This is the site that goes into further explanation about the Test.
So, naturally I wanted to experiment. When I first iterate through the list with the ForNext method the code works fine. The second time I can't access the name of the Form in the list because it is disposed. Anyone have any insight how I can access the forms properties in the list.
Public Class frmMain
Dim Cabinet As List(Of Form) = New List(Of Form)
Dim FormA As New Form1
Dim FormB As New Form2
Dim FormC As New Form3
Private Sub frmMain_Load(ByVal sender As Object, ByVal e As System.EventArgs) Handles _Me.Load
Cabinet.Add(FormA)
Cabinet.Add(FormB)
Cabinet.Add(FormC)
End Sub
Sub displayForm(ByVal aForm As Form)
Dim myFormName As String = ""
Stopwatch.Start()
If aForm.IsDisposed = False Then
aForm.Show()
Else
myFormName = aForm.(How do I access this objects Name?)
aForm = New Form '<----- I would rather simply use aForm = New(aForm)
aForm.Name = myFormName
aForm.Show()
End If
Stopwatch.Stop()
Dim RealResult As Decimal = (Stopwatch.ElapsedMilliseconds / 1000)
Debug.WriteLine(RealResult)
Stopwatch.Reset()
End Sub
Private Sub btnForEach_Click(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles btnForEach.Click
'Dim instance as List
'Dim action as Action(of T)
'instance.ForEach(action)
'action = delegate to a method that performs an action on the object passeed to it
Cabinet.ForEach(AddressOf displayForm)
End Sub
I really don't understand why if VB knows that this is a Generic list, which means it is knowledgable of the list's type, and the objects are all constrained to be forms; why I can't call a constructor on an item in the list. Ex. aForm = New aForm or aForm = New Cabinet.aForm
Tear this one open for me somebody. Thanks.
You can't construct a new instance of "aForm" because its isn't a type, it is an instance of type Form.
If you wanted to prevent the ObjectDisposedException, you could hide the form instead of closing it. Place the following code in each forms code behind:
Public Class Form1
Private Sub Form1_FormClosing(sender As Object, e As FormClosingEventArgs) Handles MyBase.FormClosing
Dim form = CType(sender, Form)
form.Visible = False
e.Cancel = True
End Sub
End Class
This is a bit hacky, however, but then you wouldn't need the code in the Else block.
Edit
You could try this instead:
Private Sub displayForm(ByVal aForm As Form)
Dim indexOfCab As Integer = Cabinet.IndexOf(aForm)
If indexOfCab <> -1 Then
If aForm.IsDisposed Then
aForm = CreateForm(aForm.GetType())
Cabinet(indexOfCab) = aForm
End If
aForm.Show()
End If
End Sub
Private Shared Function CreateForm(formType As Type) As Form
Return CType(Activator.CreateInstance(formType), Form)
End Function
You wouldn't need that big Select statement.
This is the only way I have been able to get it to work. I feel it is extremely inefficient however, and hope someone can set me on a path to a better way to do this. The below is what I'm trying to achieve.
Sub displayForm(ByVal aForm As Form)
Dim myFormName As String = ""
If Cabinet.Contains(aForm) Then
Dim indexOfCab As Integer = Cabinet.IndexOf(aForm)
Dim ObjForm As Form = Cabinet.Item(indexOfCab)
If aForm.IsDisposed Then
Select Case indexOfCab
Case 0
aForm = Nothing
aForm = New Form1
Cabinet.Item(indexOfCab) = aForm
Cabinet.Item(indexOfCab).Show()
Case 1
aForm = Nothing
aForm = New Form2
Cabinet.Item(indexOfCab) = aForm
aForm.Show()
Case 2
aForm = Nothing
aForm = New Form3
Cabinet.Item(indexOfCab) = aForm
Cabinet.Item(indexOfCab).Show()
End Select
Else
Cabinet.Item(indexOfCab).Show()
End If
End If
End Sub