MDI Form issues with datasource for child forms - vb.net

I have an MDI application where the MDI form is required to store a bindingsource and dataset. The child forms are "detail forms" in that they should allow the user to alter the properties of each object instance in the parent MDI form dataset.
I have tried to go about it as follows:
Pass the bindingsource object from the MDI parent into the constructor for the new child form. However I get the following error
"An exception of type 'System.ArgumentException' occurred in System.Windows.Forms.dll but was not handled in user code
Additional information: Cannot bind to the property or column Z1 on the DataSource."
Z1 is a public property of my "Pipe" Class which is one object I have created a datasource from in visual studio wizard.
My code for the MDI form 'Main'
'This is the open child form button click handler
Private Sub ToolStripMenuItem2_Click(sender As System.Object, e As System.EventArgs) Handles ToolStripMenuItem2.Click
Dim p As New PipeForm(Me.PipeBindingSource)
Me.Show(p)
End Sub
'This is the overloaded show helper method for the MDI form
Private Overloads Sub Show(f As Form)
For Each testForm As Form In Application.OpenForms
If testForm.GetType().Equals(f.GetType()) Then
f.Activate()
Return
End If
Next
f.MdiParent = Me
f.Show()
End Sub
And my code for the Child form 'PipeForm'
'constructor
Public Sub New(bs As BindingSource)
' This call is required by the designer.
InitializeComponent()
Me.PipeBindingSource = bs
' Add any initialization after the InitializeComponent() call.
End Sub
I can have separate PipeBindingSource objects on each form and work with them through textbox inputs with no issues. The problem arises when i try to set the PipeBindingSource from the parent to the child as equal.

Related

Access MDI Parent StatusStrip in Child Window VB.NET 2015

VB.NET 2015 -- Have placed StatusStrip control with 2 ToolStripStatusLabel on it in a MDI window. when i open a new form, this would be the Child of the MDI window.
How do i read from info ToolStripStatusLabel.
For Each does not loop through all items in StatusStrip, and from what i have learnt, ToolStripStatusLabel is not a Control as such so the For each loop does not work.
Me.Owner.Controls also does not work to access ToolStripStatusLabel for the same reason as above
What to Do ?
The proper way for any child form to get data from its parent is for the child form to raise an event and the parent form to pass the data back to the child via the event args. This way, the child never has to know anything specific about the parent form so, in theory, many different parent forms could display the same child form and pass it data. This is an example of loose coupling.
The child form would look something like this, providing an event that is raised when it needs data and it gets that data back via the custom EventArgs object that it created:
Public Class Form2
Public Event StatusDataNeeded As EventHandler(Of StatusDataNeededEventArgs)
Protected Overridable Sub OnStatusDataNeeded(e As StatusDataNeededEventArgs)
RaiseEvent StatusDataNeeded(Me, e)
End Sub
Private Sub GetStatusData()
Dim e As New StatusDataNeededEventArgs
OnStatusDataNeeded(e)
MessageBox.Show(e.StatusData)
End Sub
'...
End Class
Public Class StatusDataNeededEventArgs
Public Property StatusData As String
End Class
As you can see, there's no reliance on any specific type of parent form there and there's also no reliance on this form being an MDI child. It simply raises its event and anyone listening can provide the status data, no matter the relationship.
In your case, the MDI parent form might look something like this:
Public Class Form1
'Stores the data that will be displayed in the StatusStrip.
Private statusData As String
'Display the status data in the StatusStrip.
Private Sub SetStatusText()
ToolStripStatusLabel1.Text = statusData
End Sub
'Create and display a child form.
Private Sub DisplayChildForm()
Dim childForm As New Form2 With {.MdiParent = Me}
'Handle the event raised when the child requires the status data.
AddHandler childForm.StatusDataNeeded, AddressOf ChildForm_StatusDataNeeded
childForm.Show()
End Sub
'Pass the status data to the child form.
Private Sub ChildForm_StatusDataNeeded(sender As Object, e As StatusDataNeededEventArgs)
e.StatusData = statusData
End Sub
'...
End Class
As you can see, the status data is stored in its own field. The StatusStrip is for display of status data, not storage. The parent form creates and displays a child form and handles the event. In the event handler, it simply passes the status data to the property of the e parameter.
To learn more about creating your own events, see here.
The Event approach by jmcilhinney is definitely a better way however as a quick solution the My Namespace can be used in VB.Net.
My.Forms.<Form Name>.<Control name>.<property>
eg:
My.Forms.AM_MDI.ToolStripStatusLabel1.Text
For those who would love read more on My Namespace -
Namespaces in VB.NET
https://www.thoughtco.com/namespaces-in-vbnet-3424445
Do you use the 'My' namespace in VB.NET?
Do you use the 'My' namespace in VB.NET?

Error after disposing form in MDI Parent

i have a MDI Parent as the parent form and i use it to open and organize other forms as child inside it. i use this method to open child forms :
Public Sub OpenForm(ByVal frm As Form)
frm.MdiParent = MainView
frm.Show()
End Sub
the method works correctly and i don't have any problem using it to open child forms. i have 3 items in each child form :
1- DataGridViewX (from DevComponents.DotNetBar.Controls)
2- Panel
3- UserControl
i can use each of these items correctly and no error shows up. DataGridViewX is connected with a DataSource and everything is correct.
the problem occurs when i open 2 or more forms inside the MDI Parent and then try to close them.
the Error is :
The following exception occurred in the DataGridView:
System.IndexOutOfRangeException: Index 0 does not have a value.
at
System.Windows.Forms.CurrencyManager.get_Item(Int32index)
at
System.Windows.Forms.DataGridView.DataGridViewDataConnection.G" and caption "DataGridView Default Error Dialog".
and this is the code responsible for error :
Partial Class Form1
Inherits DevComponents.DotNetBar.OfficeForm
'Form overrides dispose to clean up the component list.
<System.Diagnostics.DebuggerNonUserCode()>
Protected Overrides Sub Dispose(ByVal disposing As Boolean)
Try
If disposing AndAlso components IsNot Nothing Then
components.Dispose()
End If
Finally
MyBase.Dispose(disposing) '' <<<< ERROR LINE
End Try
End Sub
now obviously i don't code inside designer nor put elements inside form using the code. i use the Designer interface.
what should i do ?
thanks
just set binding source of DataGridViewX to nothing and problem solved !
Private Sub theForm_FormClosing(sender As Object, e As FormClosingEventArgs) Handles Me.FormClosing
DataGridViewX1.DataSource = Nothing
End Sub

Closing Window's from upon switching to another or vice versa

I wrote a Window Application which has two different parent-forms (form1, and form2). Each form has several child forms. After I login it opens form1. Now I have a button (called switch to form2) on form1, which switch to form2. Now I need to close form1 after opening form2. I need to same thing from form2 to form1.
What would be the best way to handle this.
I tried something like below by adding this code to close form under form load of each forms, but I am getting the following exception.
A first chance exception of type 'System.InvalidOperationException' occurred in mscorlib.dll
Additional information: Collection was modified; enumeration operation may not execute.
Can you please suggest me to handle this problem?
Form 1
Private Sub Form1_Load(sender As Object, e As System.EventArgs) Handles Me.Load
For Each frm As Form In Application.OpenForms
If frm.Name.ToLower = "form2" Then
frm.Close()
End If
Next
End Sub
Form 2
Private Sub Form2_Load(sender As Object, e As System.EventArgs) Handles Me.Load
For Each frm As Form In Application.OpenForms
If frm.Name.ToLower = "form1" Then
frm.Close()
End If
Next
End Sub
I suppose you are responsible for creating your forms.
If so, I would overload the constructor of each of the forms to take a parameter of the type of the other form and close it there.
For your Form1 it would be :
Public Sub New(form2 As Form2)
InitializeComponent()
'and the rest of your initialization code
If form2 IsNot Nothing Then
form2.Close()
End If
End Sub
Vice versa for the constructor of Form2.
It doesn't even have to be so specialized like above. You could always generalize it to take an object of the type Form as a parameter.
why not use Form1.Hide()?
that way you can still access the other form while it is not visible.
I hope this helps.

Access to a child form

Private Sub tsGradovi_Click(sender As Object, e As EventArgs) Handles tsGradovi.Click
For Each f As Form In Application.OpenForms
If TypeOf f Is frmGradovi Then
f.Activate()
Return
End If
Next
Dim f2 As New frmGradovi
f2.MdiParent = Me
f2.Show()
f2.WindowState = FormWindowState.Maximized
resetdgvGradova()
End Sub
On this way i add the Child form to my main Form.
On that frmGradovi form i have the datagridview. Now i added class to my project.
How can i add the datagridview source from my class.
this code is not helping
frmGradovi.DGV.DataSource = SQLDataset.Tables(0)
Probabbly because frmGradovi is mdi child of form1.
Edit:
At class konekcija i need to set the datasource for the frmGradovi form. But that frmGradovi form is an mdi child form of Form1
One way to avoid these types of conundrums is not to write Form-centric code. They are basically a sandbox for collection user input. The other element is to explicitly instance forms: In your code f2 is an instance of frmGradovi. Trying to reference it as frmGradovi elsewhere risks creating a new default instance of it (you'd later have 2 forms of Type frmGradovi in your Forms collection).
I dont know what a Gradovi or a konekcija is, so I will use a Customer example. My app might have a frmCustomer and a Customer class. When it comes time to display a certain customer, rather than the MDI parent form code or button click creating the form, I'd leave that job to the Customer class:
Public Class Customer
' myFrm is an instance of frmCustomer, which is a Type
Private myFrm As frmCustomer
Private myDT As DataTable
Public Sub Display(Id As Int32)
CustId = Id
If myFrm Is Nothing Then
myFrm = New frmCustomer
' MDI boilerplate code
'...
' one time setup code like populate static CBOs:
'...
End If
UpdateDisplay()
myFrm.BringToFront()
End Sub
Public Sub UpdateDisplay()
' display code when something changes such as show new selected Customer
' e.g.:
LoadCustDataToDataTable(CustId)
With myFrm
.tbfirstName.Text = FirstName
.tbLastName.Text = LastName
' ...etc
.dgvPastOrders.DataSource = myDT
End With
End Sub
The "key" is that the Customer class is in charge of the customer form. It creates it and retains a reference to it. When the user clicks Save that task too would be offloaded to the Customer.Save method.
You'll have other gyrations to add to handle when the user closes that form (if they are allowed to close versus just hiding it). In your current approach, your class could fish the reference to its form from the collection as it needs it.

How to check a checkbox of form 1 from another form in vb.net?

I have two forms, form 1 and form 2 (windows application). How can i access and check a checkbox in form 1 from form 2. Initially i tried calling the form name and then the control like form1.chkCanada.checked = true, it did not work. And then i added a property in form 1
Public Property abc As Boolean
Get
Return chkCanadianStmtInd.Checked
End Get
Set(value As Boolean)
chkCanadianStmtInd.Checked = value
End Set
End Property
and then in form 2
Dim frm As New frm1
frm.abc = True 'Checked
And it still doesnt work. Am i missing anything here?
Alternatively you can pass a handle of form1 to form2 constructor
Form1:
Public Class Form1
Private Sub Button1_Click(sender As System.Object, e As System.EventArgs) Handles Button1.Click
Dim _form2 As New Form2(Me)
_form2.Show()
End Sub
End Class
Form2:
Public Class Form2
Public Sub New(ByVal _form1 As Form1)
' This call is required by the designer.
InitializeComponent()
' Add any initialization after the InitializeComponent() call.
_form1.CheckBox1.Checked = True
End Sub
End Class
Consolidating comments here:
In order to access controls on a form that shows another form to the user you have two options, if no interaction is needed with the first form while the second form is active you can use showdialog and do all of your logic after the second form has closed, if you ned to maintain the ability to interact with the first form while the second is still open then you need to use custom events.
Showdialog:
The simpler of the two options is to switch your form.show() function calls to form.showdialog(). This effectively tells the first form that it should stop processing at the form.showdialog() line and wait for the child form to close before proceeding. Once the second form is closed the first form will pick up where it left off and that would be where any processing that relies on the values of the second form would take place.
Custom Events:
If you want to allow the user to interact with both the first and second forms at the same time then you will need to use custom events. In order to do this you will need three things. The custom event, a raiseevent call and an event handler.
So in your Form2 class you will need to declare the custom event. In this case since you are trying to check(or uncheck I assume) a box your custom event declaration will look like:
public event ChangeCheckedValue(byref state as boolean)
Now on your button click event you will need to raise the event to the handler on Form1:
RaiseEvent ChangeCheckedValue(booleanValue)
Now that those statements are in place you will need to changed your form2 object that is being shown by Form1. What I normally do is make Form2 a form wide variable on Form1 and declare it like:
private withevents frm as Form2
Once you have the frm variable in your Form1 class you can add a handler for the ChangeCheckedValue event:
protected sub HandleCheckChanged(byref bln as boolean) handles frm.ChangeCheckedValue
'Set the checked state of your checkbox.
End sub
Once you have all that set up you should see what you expect.