How to pass a form, object or data to a second form - vb.net

I have created 2 forms.
The first one is the button that you want to back up.
In the second there are paths that can be modified.
How to make a reference that after pressing the "backup" button will get a path of 2 forms.
The path is saved when I closed form2
I know how to do it in one form but unfortunately I can not refer to another form.
Source of Form 2:
Private Sub Browser_from1_Click(sender As Object, e As EventArgs) Handles Browser_from1.Click
Dim FolderBrowserDialog1 As New FolderBrowserDialog
FolderBrowserDialog1.ShowDialog()
TextBox1from.Text = FolderBrowserDialog1.SelectedPath
If Browser_from1.Text <> "" And TextBox1from.Text <> "" Then
Backup.StartCopy.Enabled = True
End If
End Sub
Private Sub Browser_to1_Click(sender As Object, e As EventArgs) Handles Browser_to1.Click
Dim FolderBrowserDialog1 As New FolderBrowserDialog
FolderBrowserDialog1.ShowDialog()
TextBox2to.Text = FolderBrowserDialog1.SelectedPath
If Browser_to1.Text <> "" And TextBox2to.Text <> "" Then
Backup.StartCopy.Enabled = True
End If
End Sub
Private Sub TextBox1from_TextChanged(sender As Object, e As EventArgs) Handles TextBox1from.TextChanged
End Sub
Private Sub save_settings_Click(sender As Object, e As EventArgs) Handles save_settings.Click
My.Settings.pathmem = TextBox2to.Text
My.Settings.pathmem1 = TextBox1from.Text
My.Settings.Save()
End Sub
Private Sub setting_Load(sender As Object, e As EventArgs) Handles MyBase.Load
TextBox1from.Text = My.Settings.pathmem1
TextBox2to.Text = My.Settings.pathmem
End Sub
End Class

You dont want to create a reference to a form - that would (or could) create a whole new form. You want to hold onto the form reference.
This is done by passing a reference to the forms, but the talk of one form fiddling with the controls on another form is a bad idea because it breaks encapsulation. But forms are classes (it says so at the top of each one), so you can add Properties and Methods (Sub and/or Functions) to facilitate passing information back and forth.
Method One - Passing a Form Reference
The simplest way is to pass whatever the other form needs in the constructor:
' form 1 / "main" form / form to return to
Dim frm As New Form6(Me)
frm.Show()
Me.Hide()
In order for this to work, you need to modify the constructor (Sub New) on the destination form:
Private frmReturnTo As Form
Public Sub New(f As Form)
' This call is required by the designer.
InitializeComponent()
frmReturnTo = f
End Sub
It is best not to create your own constructor until you are familiar with them. Use the drop downs at the top of the code window: from the left pick the form name; from the right, select New. The designer adds required code to them which must not be changed.
Do not add any code before the InitializeComponent() call at least until you are familiar with the life cycle of a form. The form and its controls do not exist until that runs.
To return to the "main" form:
If frmReturnTo IsNot Nothing Then
frmReturnTo.Show()
End If
You may want to remove some of the title bar buttons or add code to the form Closing event to handle when the user closes via the system menu or buttons.
Using the constructor is ideal for cases where there is some bit of data which the form must have in order to do its job.
Method Two - Passing Data
Thats all well and good, but what about passing data to another form? You can use the constructor for that too. In order to pass say, a string, integer and a Point:
' destination / second form:
Public Sub New(a As String, b As Int32, c As Point)
' This call is required by the designer.
InitializeComponent()
' Add any initialization after the InitializeComponent() call.
Label1.Text = a
Label2.Text = b.ToString
Label3.Text = c.ToString
End Sub
Call it like this:
' method two: pass data you want to share in the ctor
Dim frm As New frmData("hello", 6, New Point(150, 550))
frm.Show()
Result:
Method Three: Properties
Thats fine, but if there is a lots of data that way can get cumbersome. Plus, you may want to update some of the data from the calling/main form. For this you can create Properties on the form to handle the data:
Public Property Label1Text As String
Get
Return Me.Label1.Text
End Get
Set(value As String)
Me.Label1.Text = value
End Set
End Property
Rather than a private variable to act as the backing field, one of the controls is used. The name leaves a bit to be desired as it exposes implementation details. So, use names which describe what the data represents rather than where it displays.
Public Property SpecialValue As Integer
Get
Return Integer.Parse(Me.Label2.Text)
End Get
Set(value As Integer)
Me.Label2.Text = value.ToString
End Set
End Property
Public Property SomePoint As Point
Get
Dim data = Me.Label3.Text.Split(","c)
Return New Point(Convert.ToInt32(data(0)),
Convert.ToInt32(data(1))
)
End Get
Set(value As Point)
Me.Label3.Text = value.X.ToString & "," & value.Y.ToString
End Set
End Property
A point was used just to show that other data types can be used. Setting those values from the calling/original/source form:
Using frm As New Form6
frm.Label1Text = "Ziggy"
frm.SpecialValue = 42
frm.SomePoint = New Point(111, 222)
frm.ShowDialog()
' do stuff here with any changes
Dim theint = frm.SpecialValue
End Using ' dispose of dialog
The destination controls would well have been TextBoxes for the user to edit. The Property "wrappers" allow you to fetch those values back, so in this case, a Dialog was used.
Method Four: Methods
You can also use methods as a way to pass data to the second/helper form. Here a List(of T) collection will be passed. In the child/display form a method is added to receive the data which it then displays. The task represented is proofing or viewing a filtered list:
Public Sub UpdateDisplay(lst As List(Of SimpleItem), filter As String)
DataGridView1.DataSource = lst
Label1.Text = String.Format("{0} Total {1} Items", lst.Count, filter)
End Sub
In the main/calling form:
' form level variable
Private frmDV As frmDataView
elsewhere...perhaps in a Click event:
' myList is a simple list of items
' Users pick which color to filter on via a combo box
Dim filter As String
If cboListFilter.SelectedItem IsNot Nothing Then
'Dim frmDV As New frmDataView
If frmDV Is Nothing OrElse frmDV.IsDisposed Then
frmDV = New frmDataView
End If
filter = cboListFilter.SelectedItem.ToString()
' apply the filter
Dim tmpList = myList.Where(Function(w) w.Color = filter).ToList()
frmDV.UpdateDisplay(tmpList, filter)
frmDV.Show()
Else
Return
End If
Result:
With DataBased apps a modified version of this can allow for the case where you display DataGridView data in detail form on another form. You need not have the second form rung SQL to add or update the record, and then the main form running another query to "refresh" the display. If the DataSource is a DataTable backed up by a fully configured DataAdapter, pass the DataTable and have the child form add, change or delete using that. The data will automagically be in the DataTable and DataGridView`.
There are other ways to do this, but they generally all boil down to passing something from A to B. Which way is "best" depends on what the app does, the use-case and the nature of the data. There is no one right way or best way.
For instance, Properties and in many cases Functions allow the B Form to close the feedback loop. With DB items, a DataChanged property might tell the calling form that data was added or changed so that form knows to use the DataAdapter to update the db.

'SECOND FORM
Public class secondForm (blah blah)
Public overloads property owner as myMainForm
'Must be only the form you prepared for that
Private sub secondForm_load(blah blah) handles blah blah
Texbox1.text=Owner.customcontrol.text
End sub
End class
'MAIN FORM
public class myMainForm(blah blah)
Private sub button1_click(blah blah) handles blah blah
Dim NewSecondForm as secondForm = New secondForm
NewSecondForm.owner(me)
NewSecondForm.show(me)
NewSecondForm.dispose()
' so you can have bidirectional communication between the two forms and access all the controls and properties from each other
End sub
End Class

Related

Pass Variable to NEW Form vs. OPEN Form

I have a WinForm that I want to pass a variable to. This form may or may not already be open. The code I was using would check to see if the form was open (using an IsOpen function) and then do a .BringToFront if it was open, or it would do a .Show if it was not open.
Now with trying to use New() to pass the variable, things are not working as expected. Creating the form variable, using Dim AdBook As New frmAddressBook, means a new instance of the form is created. When I run the IsOpen function, it does see the form. However, since I created a "new" form, when I run the AdBook.FillPatientInfo function of that form, the other variables have not been set up because that "new" form has not actually been loaded.
The only solution I have found thus far is to close the form and then open it again. But that sounds like more overhead than it should be. And besides, I'm not sure how I would close a form that I can't get a hold of.
Form 1 Click Event:
Private Sub tsmiAddressBook_Click(sender As Object, e As EventArgs) Handles tsmiAddressBook.Click
If IsDBNull(dgvPhysician.CurrentRow.Cells(1).Value) Then Exit Sub
Dim AdBook As New frmAddressBook(CInt(dgvPhysician.CurrentRow.Cells(1).Value))
If IsOpen(AdBook, Reflection.MethodBase.GetCurrentMethod().Name) Then
AdBook.BringToFront()
AdBook.FillPatientInfo(CInt(dgvPhysician.CurrentRow.Cells(1).Value))
Else
AdBook.Show(Me)
End If
End Sub
Form 2 New() code:
Private intID As Integer = Nothing
Public Sub New(ByVal newID As Integer)
' This call is required by the designer.
InitializeComponent()
' Add any initialization after the InitializeComponent() call.
intID = newID
End Sub
Code that checks to see if form IsOpen:
Public Function IsOpen(ByVal frm As Form, SubName As String) As Boolean
Try
Dim frmCol As New FormCollection()
frmCol = Application.OpenForms
Dim Cnt As Integer = 0
For Each f As Form In frmCol
If f.Name = frm.Name Then Cnt += 1
Next
Return CBool(IIf(Cnt > 0, True, False))
Catch ex As Exception
CustExErrorMsg("Functions", Reflection.MethodBase.GetCurrentMethod().Name, ex.Message)
Return False
End Try
End Function

Open Form By Variable Value

Form Name comes from a variable. I would like to open Form from variable value.
In VBA load("UserFormName") will show the form. But I don't know how to do it in VB.Net.
Ok, of course one would want to be able to open a form by string name.
When you create a vb.net winforms project, then all forms are available as a "base" static class.
You often see a LOT of code thus simply use the base form class.
If I need to display say form2, then I really don't need to create a instance of that form (unless you want to have multiple instances of that form. So a truckload of code will simply launch + use the "base static" class of that form.
eg:
Form2.Show()
I don't consider this all that bad of a practice, since what do you think the project settings to "set" the startup form in the project settings does?
It simply sets the built in instance of "mainForm" = to your startup form and it does NOT create new instance.
So, now that we all can agree for 15+ years anyone who has set the startup form in their project is NOT creating a NEW instance of that form, but in fact using the base class instance. This is really a programming choice.
So, code to display (show) the base static instance of a form by string name will look like this:
Private Sub Button1_Click(sender As Object, e As EventArgs) Handles Button1.Click
Dim strForm As String = "Form1"
ShowFormByName(strForm)
End Sub
Public Sub ShowFormByName(strFormName As String)
System.Reflection.Assembly.GetExecutingAssembly().CreateInstance(ProductName & "." & strFormName).show()
End Sub
Private Function FormByName(strFormName As String) As Form
Return System.Reflection.Assembly.GetExecutingAssembly().CreateInstance(ProductName & "." & strFormName)
End Function
However, above includes a helper sub that will simply "show" that built in instance of the forms.
And above also includes a function to return class type of the form, since for sure a good many developers prefer to first create a instance of the form, and then "show()" it.
So, often we do want multiple instances, or we just perfer the codeing approach of creating a new instance of the form object.
So, we use the 2nd helper function to return a form object of the type we passed by string.
So, to display 3 instances of form1, but the FIRST instance being the base class, then two more but brand new instances of that form, we have this code:
Private Sub Button1_Click(sender As Object, e As EventArgs) Handles Button1.Click
Dim strForm As String = "Form1"
ShowFormByName(strForm)
Dim f1 As Form = FormByName(strForm)
Dim f2 As Form = FormByName(strForm)
f1.Show()
f2.Show()
End Sub
So the above code snip shows how to display the built in base class form without having to create a instance of that form.
However, the next two forms we load are "new" instances of that form as "string".
So the helper sub, and helper function will give you both choices as to which preference floats your boat.
Dim form = System.Reflection.Assembly.GetExecutingAssembly().CreateInstance(Application.ProductName & "." & MySubForm)
Dim frm As New Form
frm = form
frm.MdiParent = AFrmMainScreen
frm.WindowState = FormWindowState.Maximized
frm.Show()
I prefer to use Reflection.Assembly.GetEntryAssembly because I use several different projects in one solution. This allows me to put this code in a different project(dll) that has a usercontrol that I can then reuse across multiple solutions. You also don't need to know the "Namespace" for the form as long as it is in the startup project.
The code below gets the form type from the exported types from the entry assembly and then uses Activator.CreateInstance to create a new instance of the form. Then I return that form in the function.
Public Function GetForm(ByVal objectName As String) As Form
Try
Dim frmType = Reflection.Assembly.GetEntryAssembly.GetExportedTypes.FirstOrDefault(Function(x) x.Name = objectName)
Dim returnForm = TryCast(Activator.CreateInstance(frmType), Form)
Return TryCast(returnForm, Form)
Catch ex As Exception
Return Nothing
End Try
End Function
To use the above function:
Dim MyForm = GetForm(FormLocation)
If MyForm IsNot Nothing Then
MyForm.ShowDialog()
'You can do any form manipulation from here.
Else
MessageBox.Show($"{FormLocation} was not found.")
End If

How to store a variable after form closes for use later on another form

So I am building a form based application, and I am running into an issue with passing data between forms. I have a combo box that, based on selection, triggers a new form to open with several buttons to pick from. Once you select a button, the form closes, but I can't get the selection to be carried over to the original form.
basic idea of the code is like this
Public Class frmMain
Public intStore As integer
Private Sub cboSample_SelectedIndexChanged(sender As Object, e As EventArgs) Handles cboSample.SelectedIndexChanged
Dim selection as Integer
selection = cboSample.SelectedIndex
If selection = -1 Then
Else
Select Case selection
Case 0
frmOne.Show()
Case 1
frmTwo.Show()
End Select
End If
End Sub
End Class
Here is a sample of the second form code
Public Class frmOne
Public storage As varStorage
Private Sub btn_Clicked(sender As Object, e As EventArgs) Handles btn.Clicked
storage = New varStorage With {.datastore = 1}
Me.Close()
End Sub
End Class
frmTwo is pretty much the same but handles more options
The Class I created looks like this
Public Class varStorage
Public _dataStore As Integer
Public Property dataStore() As Integer
Get
Return _dataStore
End Get
Set (value As Integer)
_dataStore = value
End Set
End Property
End Class
as I said, the issue comes from the point of the form being called, and the form closing, the variable data is not being saved. I am almost certain I am missing some code somewhere, but not sure where. any help would be greatly appreciated.
Rather than using Show() to open another form, using ShowDialog() will open the form modally. The form will behave like a MessageBox. It will wait for you to respond to, and close, this form before returning to your main form. We can obtain a value from this modal-form, safe in the knowledge that the form has been closed (and so the value we have obtained will not be changed).
The other way is to make a Public Property of the form. then you can create Get and Set methods to have access to your form objects
Public Class yourFormClass
Public Property Note As String
Get
Return txtNote.Text
End Get
Set(value As String)
End Set
End Property
End Class
and Then you can use it like
dialog = New yourFormClass()
someOtherTextbox.Text = dialog.Note

How do I use the Tag property with forms and code in VB 2012?

I am writing a program using a database for customers and technicians. The main form (CustomerIncidents) has a toolstripbutton that opens a different form to (SearchByState) where the user inputs a state code and looks for any incidents.
If the user clicks into one of the datagrid cells I want that customers information to be stored in the TAG so that when the form is closed using the OK button that it will show back up in the main form (CustomerIncidents).
Edited 03/11/14 12:21pm
The problem is in the Main Form. When I click the OK button in the Second Form it tries to convert the DialogResult Button to a String. I can't figure out how to fix it.
Customer Form (Main Form) Opens to Secondary Form
Private Sub btnOpenState_Click(ByVal sender As System.Object,
ByVal e As System.EventArgs) Handles btnOpenState.Click
Dim frmSearchState As New FindCustomer
----->>Dim selectedButton As DialogResult = frmSearchState.ShowDialog()
If selectedButton = Windows.Forms.DialogResult.OK Then
CustomerIDToolStripTextBox.Text = frmSearchState.Tag.ToString
End If'
Search By State Form (Secondary Form) Or "Child Form"
Private Sub btnOk_Click(message As String, ByVal e As DataGridViewCellEventArgs) Handles btnOk.Click
message = CustomersDataGridView.Rows(e.RowIndex).Cells(e.ColumnIndex).Value.ToString
Me.Tag = message
Me.DialogResult = DialogResult.OK
End Sub
The click event for a button does not have a DataGridViewCellEventArgs parameter, and will throw an exception when you try to use it.
You don't need to use the Tag property since you can just create your own property.
In your child form, create a property called GridValue:
Private Sub btnOk_Click(sender As Object, e As EventArgs) Handles btnOk.Click
If dgv.CurrentCell Is Nothing OrElse dgv.CurrentCell.Value Is Nothing Then
MessageBox.Show("A cell needs to be selected.")
Else
Me.DialogResult = DialogResult.OK
End If
End Sub
Public ReadOnly Property GridValue As String
Get
Return dgv.CurrentCell.Value.ToString
End Get
End Property
In your parent form, you can now access your information:
Using frmSearchState As New FindCustomer
If frmSearchState.ShowDialog(Me) = DialogResult.Ok Then
CustomerIDToolStripTextBox.Text = frmSearchState.GridValue
End If
End Using
My personal approach for doing this kind of stuff is to create a public property in the child form, having the same type as the DATA you want to take back to your main form. So instead of storing DataGridView's reference in Tag property, you should really be storing the actual value that was there in the cell that the user clicked on.
For example, if your DGV cell has a string value in it, you could do something like:
Public Readonly Property StateName As String
Get
If YourDGV.SelectedCell IsNot Nothing Then
Return YourDGV.SelectedCell.Value
Else
Return ""
End If
End Get
End Property
(I have written that code by hand, so there may be some syntax problems, but you should be able to get the idea.)
You can now use ShowDialog() in the main form to bring up this child form and upon OK or Cancel, you could check the value of StateName property of your child form to get this value. The thing to remember here is that closing a form doesn't dispose off all its constituent controls and properties and therefore you can access them even after the form has finished ShowDialog() call.

Initialize not properly make with classes vb.net

So, I need to do an program for a client and he wants a search bar in it. So I made it and everything worked perfectly but I put it in my main form. Now, I want to put it in a class but when I initialize the program, it gives me the following error
An error occurred while creating the form. For more information,
see Exception.InnerException. The error is: The form is self-reference during
construction from a default instance, which led to infinite recursion. In the
constructor of the form, refer to the form using 'Me'.
I tried to put Me.Rbtn_X... but it doesn't recognize it.
Initialization
' Main form
Public Sub New()
InitializeComponent()
Initialize_search()
End Sub
Initialize_search()
' Main form
' search is initialize like this :
' Dim search as New Research
Private Sub Initialize_search()
search.generate_autocomplete()
End Sub
generate_autocomplete()
' Research class
Sub generate_autocomplete()
' Main_form = Main form
Dim field = ""
' This is the place where the program fail
If Main_form.RbtnR_avancee_contact.Checked Then
field = "personneressource"
Else
field = "beneficiaire"
End if
' ....
End Sub
Is there something I didn't understand or It's not possible to do it that way?
Edit: added Form_shown event
Public Sub New()
InitializeComponent()
' Initialize_search()
End Sub
Private Sub Form_personne_Shown(sender As Object, e As EventArgs) Handles Me.Shown
MessageBox.Show("You are in the Form.Shown event.")
End Sub
The form is not created (fully) until New completes. By adding your Initialize_search to it, it eventually leads to the statement `Main_form.RbtnR_avancee_contact.Checked'. This is wrong on two counts:
1) the form doesnt exist yet, so you cant refer to it. (this is what the error meant with 'form is self-reference during construction')
2) the ref should be Me.RbtnR (which is what it meant by 'refer to the form using 'Me'')
Move your Initialize_search to the Form_shown event. Your code should look like this (including Lar's suggestion)
' Main form
Public Sub New()
' REQUIRED
InitializeComponent()
End Sub
If there is really something that needs to be setup for this, add it to the form_shown event:
Private Sub Form1_Shown(ByVal sender As Object, ByVal e As System.EventArgs) _
Handles Me.Shown
' NOTE: even .NET refers to ME not MainForm etc
InitializePanel
InitializeSeach
End Sub
Then:
Private Sub Initialize_search()
search.generate_autocomplete(Me.RbtnR_avancee_contact.Checked)
End Sub
Then:
Sub generate_autocomplete(AdvContact as Boolean)
Dim field AS STRING = ""
If AdvContact Then
field = "personneressource"
Else
field = "beneficiaire"
End if
' ....
End Sub
Your search class doesn't have a reference to the instance of the form's controls.
Try passing the value instead:
Sub generate_autocomplete(advancedChecked As Boolean)
Dim field As String = ""
If advancedChecked Then
field = "personneressource"
Else
field = "beneficiaire"
End if
End Sub
Then when you call it:
search.generate_autocomplete(Me.RbtnR_avancee_contact.Checked)
Even if did work like you want it to, according to your code, it would always result in field containing the same value (whichever was set in designer).
Instead, try putting this code inside RbtnR_avancee_contact.Checked event. Or even TextChanged for the autocomplete box (and initialize it for the first time user enters anything), it would examine the checked state and populate autocomplete items.
With this approach, if your user never uses the search box, you don't need to initialize it.