How do I traverse/iterate through all the objects inside a form in VB.net? - vb.net

I have a VB class inside which one of the methods accepts an array of forms.
For each form inside the array, I need it to traverse all of the objects, check if they are a specific tyoe (input, label, checkbox, etc.) and get the properties of each object. Then, I want to dump these into a text file in the following format:
Form1 | Label1 | "Enter your name"
"Enter your name" being the caption or text of the form object.
I want to do this to facilitate the translation of an application. Any ideas or thoughts you might have on this?

The following code will return an IEnumerable(Of Control) which contains all child controls of the passed in control. It will recursively descend down the tree and get all nested controls.
Public Function GetAllControls(ByVal source as Control) As IEnumerable(Of Control)
Dim seq = Enumerable.Empty(Of Control)
For Each child in source.Controls
if child.Controls.Count > 0 Then
seq = seq.Concat(GetAllControls(child))
End If
Next
Return seq
End Function

You have to do a For Each for the form's .Controls collection. However, note that if a child control of the form has more controls in its own .Controls collection, they won't be accounted for. You'll have to make a recursive function in order to traverse the whole parent-child chain of controls to find them all.
Now, for each control, you'll want to perhaps do a Case statement to check the Type Of for each control. Then cast the control to it's type and grab the properties.

If all you want to do is to be able to localise your form then a much simpler way would be to set the form's Localizable property to true. This will result in the creation of a .resx file containing all the values of the various properties for all the controls on the form so that the texts etc can be translated and distributed as a separate satellite assembly.

Public Class Form1
Private Sub Button1_Click(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles Button1.Click
ProcessForm(Me)
End Sub
Private Sub ProcessForm(ByVal frm As Form)
For Each el As Control In frm.Controls
Dim str As String
str = String.Format("{0} | {1} | {2}", frm.Name.ToString(), el.Name.ToString(), el.Text.ToString())
Debug.Print(str)
Next
End Sub
End Class

Related

How do I use textbox controls in VB class?

I am trying to create an EmployeeID in an 'employee' class using a method. But the ID needs to be able to extract a letter from the 'firstname' textbox in the main design form and then use that to create a unique ID. But I'm unable to call/use any form controls in any of the classes, as they only work on main form. How do I pass the controls to the class so I can do this inside the 'employee' class method instead of the main form?
I hope this makes sense.
I agree that this is the wrong way to approach this. However, it is possible to access a form's controls from a class. You can do this either by accessing open forms (assuming it's open) or passing the textbox as an argument.
Form:
Private Sub Form1_Load(sender As Object, e As EventArgs) Handles Me.Load
Dim EmployeeA As New ClassEmployee
Dim EmployeeAID = EmployeeA.GetEmployeeIDFromOpenForm()
MsgBox("Open Form: " & EmployeeAID)
MsgBox("Passing Control: " & EmployeeA.GetEmployeeIDFromControl(TxtFirstName))
End Sub
Class:
Public Class ClassEmployee
Public Function GetEmployeeIDFromOpenForm()
Dim FirstNameText = Application.OpenForms.Item("Form1").Controls.Find("TxtFirstName", True).SingleOrDefault().Text
Return FirstNameText
End Function
Public Function GetEmployeeIDFromControl(FirstNameTextBox As TextBox)
Return FirstNameTextBox.Text
End Function
End Class

How to access data from calling object in vb.net

I have a Window-Form 'caller' in vb.net containing a datagridview with a small overview table of certain objects, each with its own ID in the first column. Now, if a row is double clicked, i want to show a dialog 'edit', where one can edit many details of that row which i do not want in the overview table.
My approach is as follows: In the caller form i wrote this to call 'edit':
Private Sub dgdata_dbclick(ByVal sender As Object, ByVal e As System.Windows.Forms.DataGridViewCellMouseEventArgs) Handles dg_data.CellMouseDoubleClick
Dim f_edit As New edit
f_edit.ShowDialog(Me)
End Sub
That works fine.
However, in the called Form "edit" i need to check, which ID was selected and load this data from the database to edit it. I can access some data from the calling form 'caller' using e.g.
MsgBox(CType(Me.Owner, caller).Text)
to show the window title of 'caller'. However, i want to extract the currently selected ID in the datagridview or at least some variabhle containing it. In the caller form, this could be easily done by evaluating
dg_data.Item(0, selectedRow).Value.ToString
but i cannot access any relevant information in 'caller'. I have a public class with some global variables there but i cannot access them as well.
Probably my strategy to solve this problem is not the most clever approach? Basically, i want to open a very detailed edit window when someone clicks on a line in an overviewtable but simultaniously blocking the rest of the application as long as the edit window is open.
Thanks!
The idea is to pass the data to the second form. When you create an instance of the second form (my class is called Form2, yours is called edit) with the New keyword the Sub New is called on Form2.
Private Sub OpenEditDialog()
Dim f_edit As New Form2(32) '32 is the number you retrieve from your DataGridView
f_edit.ShowDialog(Me)
f_edit.Dispose()
End Sub
You pass the ID to Form2 and set a variable at Form level. You can then use the variable anywhere in Form2.
Public Class Form2
Private ID As Long
Public Sub New(SelectedID As Long)
InitializeComponent()
ID = SelectedID
End Sub
Private Sub Button1_Click(sender As Object, e As EventArgs) Handles Button1.Click
MessageBox.Show(ID.ToString)
End Sub
End Class
You need to call InitializeComponent() so the controls will show up.
How do you usually get data into objects? You set a property or pass an argument to a method or constructor? Why should this be any different? Decide which you want to use and then write that code in your form. If it's required data, I would suggest a constructor. Just write this code in your form:
Public Sub New
and hit Enter. That will generate a little extra code automatically. You can then add a field to store the value, a parameter to the constructor and then assign the parameter to the field inside.
Thank you for pointing me to the correct route.
I solved it like this (which works fine and which is hopefully acceptable):
In the calling form:
Private Sub dgdata_dbclick(ByVal sender As Object, ByVal e As System.Windows.Forms.DataGridViewCellMouseEventArgs) Handles dg_data.CellMouseDoubleClick
Dim selectedRow As Integer = dg_data.CurrentCell.RowIndex
Dim f_edit As New edit
f_edit.edit(dg_data.Item(0, selectedRow).Value.ToString)
f_edit.ShowDialog(Me)
f_edit.Dispose()
End Sub
In the called form:
Public Sub edit(ByVal id As Long) 'Handles MyBase.Load
'Enter commands to prepare your form
End Sub

Is there any way to create a global function to clear TextBoxes?

I was wondering if there is any way to create a class with a global function/method/sub that upon
calling it will clear some of the textboxes of the form. How can i handle the different number of textboxes
each forms has?
The current code clears only the pre-defined 2 boxes. Thank you.
Public Class ClearElements
Public Sub CLEAR_TEXT(ByVal text1 As TextBox, ByVal text2 As TextBox)
text1.Clear()
text2.Clear()
End Sub
End Class
There are many ways to do it.
You can add the TextBoxes to a List, and clear each item in the list
Private ReadOnly someOfTheTextBoxes As New List(Of TextBox)
Private Sub Form1_Load(sender As Object, e As EventArgs) Handles MyBase.Load
someOfTheTextBoxes.Add(TextBox1)
someOfTheTextBoxes.Add(TextBox2)
End Sub
Private Sub Button1_Click(sender As Object, e As EventArgs) Handles Button1.Click
For Each t In someOfTheTextBoxes
t.Clear()
Next
End Sub
Or make this method
Public Sub CLEAR_TEXT(textboxes As IEnumerable(Of TextBox))
For Each t In textboxes
t.Clear()
Next
End Sub
and call it with your list of TextBoxes
Private Sub Button2_Click(sender As Object, e As EventArgs) Handles Button2.Click
CLEAR_TEXT(someOfTheTextBoxes)
End Sub
or make an array on the spot and pass it in
Private Sub Button3_Click(sender As Object, e As EventArgs) Handles Button3.Click
CLEAR_TEXT({TextBox1, TextBox2})
End Sub
If you are interested in recursion at all, here are some extensions I have which could help
Module Extensions
<Runtime.CompilerServices.Extension>
Public Function ChildControls(parent As Control) As IEnumerable(Of Control)
Return ChildControls(Of Control)(parent)
End Function
<Runtime.CompilerServices.Extension>
Public Function ChildControls(Of TControl As Control)(parent As Control) As IEnumerable(Of TControl)
Dim result As New List(Of TControl)
For Each ctrl As Control In parent.Controls
If TypeOf ctrl Is TControl Then result.Add(CType(ctrl, TControl))
result.AddRange(ctrl.ChildControls(Of TControl)())
Next
Return result
End Function
<Runtime.CompilerServices.Extension>
Public Function ForEach(Of TSource)(source As IEnumerable(Of TSource), action As Action(Of TSource)) As IEnumerable(Of TSource)
For Each item As TSource In source
action(item)
Next item
Return source
End Function
<Runtime.CompilerServices.Extension>
Public Function ForEach(Of TSource)(source As IEnumerable(Of TSource), action As Action(Of TSource, Integer)) As IEnumerable(Of TSource)
For i As Integer = 0 To source.Count() - 1
action(source.ElementAt(i), i)
Next
Return source
End Function
End Module
Clear all textboxes recursively
Me.ChildControls(Of TextBox).ForEach(Sub(t) t.Clear())
Or ForEach on your list
someOfTheTextBoxes.ForEach(Sub(t) t.Clear())
We have this:
You could recursively go through all the controls in the form and in case of type = Textbox clear it.
But then the plot thickens:
I've done that before. It clears all the boxes of the form. I am talking about the case where some boxes have to be untouched and some to cleared.
The solution here is two parts. First, create the recursive method as suggested like this:
Public Sub ClearText(root As Control)
For Each ctrl As Control In Root.Controls
If TypeOf ctrl Is TextBox Then ctrl.Text = String.Empty
ClearText(ctrl)
Next ctrl
End Sub
or this:
Public Sub ClearText(root As IEnumerable(Of Control))
For Each ctrl As Control In root
If TypeOf ctrl Is TextBox Then ctrl.Text = String.Empty
ClearText(ctrl.Controls)
Next ctrl
End Sub
Second, on your form, use a container like Panel, GroupBox, FlowLayoutPanel, etc for the TextBox controls you need to clear. The key is all of the TextBox controls you need to clear — and none of the ones you want to keep — should be in same common container. Once that is done, you can pass the container to one of the above methods. If this messes with your layout, you can have a small number of containers for sets of controls on different areas of the form and call the function just a few times.
Remember, Panel controls can be styled to leave no visible artifacts on the parent form at all, and used entirely for logical groupings. The second version of the method above will also allow you to create arrays or lists of the controls (or control containers) you care about.
Another way to control this is to inherit a custom control from TextBox. You don't even need to change anything. All that matters is the control is now a different type from a regular textbox, and so the recursive method can target your new control type instead of textbox.
I'm using For in some cases.
First is to know for what do you need Textboxes or any component.
Second is to know if Textboxes (or any other component) will be inside Form (root) or inside others components like panels, groupoxes, tabPages… and if them will be inside of others.
Example1: Form – GroupBox(x) – TabControl(y) – TabPage(z) – TextBox(n)
Example2: Form --- TextBox(x)
Example3: Form – GroupBoox(x) – Panel(y) – TextBox(n)
Etcetera.
You may to create some anidated subs/functions to complete something more elaborated. There are two important things:
Path of the component (see previous examples)
Number of the component. If you follow Example3, maybe could be this:
Form1 – GroupBox2 – Panel1 – TextBox3
Important: These are names of the components, and you need must be enumerated all of them.
The easy way to do what you are asking is:
Public Sub CountTextBoxesAndClear(ByVal FormName As String, Optional ByVal myObject As Object = Nothing)
Dim ArrayTextBoxName() As String
Dim myTextBox As New TextBox
Dim nTBOX As Integer
'Path of component
If myObject = Nothing Then myObject = My.Application.OpenForms.Item(FormName)
'Bucle
For i As Integer = 0 To myObject.Controls.Count - 1
If myObject.Controls(i).GetType Is GetType(TextBox) Then
'Counting
nTBOX += 1
'Redim array
ReDim Preserve ArrayTextBoxName(nTBOX)
'Get Component
ArrayTextBoxName(nTBOX) = "TextBox" & nTBOX
'Get Path
myTextBox = myObject.Controls.Item(ArrayTextBoxName(nTBOX))
'myTextBox = myObject.Controls.Item("TextBox" & nTBOX) '<< the same of above line
Try
'Clear TextBoxes
myTextBox.Clear()
Catch ex As NullReferenceException
'A TextBox is Null, no error message
End Try
End If
Next
End Sub
FormName is the name of Form, with quotes, for example “Form1”.
myObject is the object that contains textboxes, if Textboxes are inside of a Panel named Panel1, you must write Panel1 (without quotes).
Try/Catch: Maybe you need to have Textbox1, TextBox2, Textbox4, Textbox5, AnotherTextBox1, AnotherTextBox2.
And you call your sub:
CountTextBoxesAndClear("Form1")
If TextBoxes are into a Panel named Panel1:
CountTextBoxesAndClear("Form1", Panel1)
You must to have the total of textboxes but only clear (or do any action) only for TextBoxes named TextBox[x].
Try/Catch manage the error because TextBox3 does not exist. However, the correct way is Textbox1, TextBox2, Textbox3, Textbox4, AnotherTextBox1, AnotherTextBox2 and put limits in your sub/function.
For example:
Public Sub CountTextBoxesAndClear(ByVal FormName As String, Optional ByVal myObject As Object = Nothing, Optional byval start as integer = 0, Optional byval finish as integer = 0)
[…tracatra…]
For i As Integer = start To finish
[…tratra…]
Next
End Sub
And this is how to call:
CountTextBoxesAndClear("Form1", Nothing, 1, 4)
And now, you can investigate a little bit about how create subs/functions to know correct paths of components, and get contents and properties of TextBoxes, Labels, Comboboxes, checkboxes…
Additional info:
If you are working in VisualStudio, you know that if you change a name of component, all of code is changed automatically. This is a big problem if you are using start/finish vars as numbers because, you must to change manually all start/finish values in functions when you need to add/remove or move positions, for example:
CountTextBoxesAndClear("Form1", Nothing, 8, 12)
Now you need to add a new TextBox just in the eight position and move one. Your sub looks like this:
CountTextBoxesAndClear("Form1", Nothing, 9, 13)
You can create a simply function that convert the name of the component to integer (this function is only for two digits (0 to 99):
Public Function ObjToInt(ByVal IntObject As Object) As Integer
If IntObject IsNot Nothing Then
Dim ref As Integer = Val(IntObject.Name.Substring(IntObject.Name.Length - 2))
If ref = 0 Then
ref = Val(IntObject.Name.Substring(IntObject.Name.Length - 1))
End If
Return ref
Else
Return 0
End If
End Function
And your sub may be written like this:
CountTextBoxesAndClear("Form1", Nothing, ObjToInt(TextBox9), ObjToInt(TextBox13))
Thanks for your awesome solutions.
I finally figured it out using ParamArray
Public Sub CLEAR_TEXTBOXES(ParamArray arr_textboxes() As TextBox)
For Each textbox As TextBox In arr_textboxes
textbox.Clear()
Next
End Sub
Then i call class using whatever textbox i want,
CLS_CLEAR_TEXTBOX.CLEAR_TEXTBOXES(TextBox1, TextBox2, Textbox7)
It's more shorter method.
Use ParamArray and Linq.
Public Sub CLEAR_TEXT(ParamArray text As TextBox())
text.ToList().ForEach(Sub(s) s.Clear())
End Sub

How to pass a form, object or data to a second form

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

Adjust child form's public variable from MDI parent form in VB.NET

I have an MDI parent form that may open a child form called "Order". Order forms have a button that allows the user to print the order. The Order form has a print size variable defined at the beginning:
Public Class Order
Public psize As String
Private Sub button_order_Click(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles process_order.Click
' Code to handle the order and then print, etc
Now the parent form has a psize variable as well, which is set to a default of "A4".
Only when someone clicks on one of the menu items on the Parent window's menu strip will this happen:
psize = "A6"
By default, whenever the parent window opens up a new Order form, I need it to set the child form's psize variable to its own psize value. Something like this:
Dim f As Form
f = New Order
f.MdiParent = Me
f.psize = Me.psize ' BUT THIS LINE DOESN'T WORK
f.Show()
I get the error that f.psize is not a member of the form.
I know that passing variables to and from the MDI parent and child is quite common but despite trying out a few options I saw here, it doesn't seem to be working. Is this the wrong approach?
The reason the property is not available is because you are using the wrong type for the variable. The base Form type does not define that property. Instead, your derived Order type does. You could do something like this:
Dim f As Order
f = New Order
f.MdiParent = Me
f.psize = Me.psize
f.Show()
UPDATE
As you have said in comments below, what you really need to do is to be able to share a dynamic setting between all your forms so that you can change the setting at any time and have it affect all your forms that have already been displayed. The best way to do that, would be to create a new class that stores all your shared settings, for instance:
Public Class Settings
Public PaperSize As String = "A6"
End Class
As you can see, by doing so, you can easily centralize all your default settings in your settings class, which is an added benefit. Then, you need to change the public property in your Order form to the new Settings type, for instance:
Public Class Order
Inherits Form
Public Settings As Settings
End Class
Then, you need to create your shared settings object in your MDI Parent form, and then pass it it to each of your Order forms as they are created:
Public Class MyParentForm
Private _settings As New Settings()
Private Sub ShowNewOrderForm()
Dim f As New Order()
f.MdiParent = Me
f.Settings = _settings
f.Show()
End Sub
Private Sub ChangePaperSize(size As String)
_settings.PaperSize = size
End Sub
End Class
Then, since the parent form and all the child Order forms share the same Settings object, and change made to that Settings object will be seen immediately by all the forms.
Change this:
Dim f As Form
to the actual implementation of your form:
Dim f As Order
or just the shortcut:
Dim f As New Order