Error after disposing form in MDI Parent - vb.net

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

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?

Cannot view Designer

Please help! I can not view the Control Parameter Adjustment Form in my VB project, other three forms are normal.
How can I fix this problem?
Here is the Designer.vb code:
<Global.Microsoft.VisualBasic.CompilerServices.DesignerGenerated()> _
Partial Class Control_Parameter_Adjustment
Inherits System.Windows.Forms.Form
'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)
End Try
End Sub
'Required by the Windows Form Designer
Private components As System.ComponentModel.IContainer
'NOTE: The following procedure is required by the Windows Form Designer
'It can be modified using the Windows Form Designer.
'Do not modify it using the code editor.
<System.Diagnostics.DebuggerStepThrough()> _
Private Sub InitializeComponent()
Me.Label1 = New System.Windows.Forms.Label()
Me.GroupBox1 = New System.Windows.Forms.GroupBox()
Me.Label31 = New System.Windows.Forms.Label()
......
End Class
At first, I apologize for my bad english.
This worked for me: If you add an empty form to your project, you can double clik on it, and you can see the following:
Public Class Form1
Private Sub Form1_Load(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles MyBase.Load
End Sub
End Class
You can't see that class definition in the other form, probably because this class have dissapeared (I can't explain you why).
Also and probably, you can find the form_Load event in the form code.
Simply rewrite this class, changing the Form Name in class definition. Then, you can see the designer. The icon also change.

MDI Form issues with datasource for child forms

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.

How to Create MDI Child forms asynchronously?

I have Parent VB.Net form that has to create some MDI childs.
Each MDI has to connect to a database to do some staff which may take some time...
I'm trying to create the MDI child asynchronously using the following code but it stills execute synchronously :
ParentForm.BeginInvoke(Sub()
CreateMDIChildForm()
End sub)
This code executes but it's still synchronous, and the parent form freezes.
I tried to create the MDI in a separate thread but then I can't join the form created to the parent form.
Does anyone have an idea please ?
Thanks.
I can reproduce this with the following code:
Public Class Form1
Private Sub Open(sender As System.Object, e As System.EventArgs) Handles Button1.Click
Me.BeginInvoke(Sub() Me.CreateMDIChildForm())
End Sub
Private Sub CreateMDIChildForm()
Dim f As New Form2()
MsgBox("OK")
End Sub
Public Class Form2
Inherits Form
Sub New()
Thread.Sleep(5000)
End Sub
End Class
End Class
And the fix would be something like this:
Public Class Form1
Private Sub Open(sender As System.Object, e As System.EventArgs) Handles Button1.Click
If (Me.operationCompleted Is Nothing) Then
Me.operationCompleted = New SendOrPostCallback(AddressOf Me.CreateMDIChildFormCompleted)
End If
Me.asyncOperation = AsyncOperationManager.CreateOperation(Nothing)
Dim thread As New Thread(AddressOf Me.CreateMDIChildForm)
thread.Start()
End Sub
Private Sub CreateMDIChildForm()
Dim f As New Form2()
Me.asyncOperation.PostOperationCompleted(Me.operationCompleted, f)
End Sub
Private Sub CreateMDIChildFormCompleted(args As Object)
Dim f As Form = DirectCast(args, Form)
'TODO: Set mdi child, show window etc.
MsgBox("OK")
End Sub
Private asyncOperation As AsyncOperation
Private operationCompleted As SendOrPostCallback
Public Class Form2
Inherits Form
Sub New()
Thread.Sleep(5000)
End Sub
End Class
End Class
The recommended method to fix issues like this is not to try to load forms in separate threads, but, rather, to do only the time-consuming work in a separate thread.
In this circumstance, what that means is that you would load the MDI child forms normally, all on the same UI thread, but inside the child forms, they would each start a separate thread to perform the DB-related work which is causing the hangups. That way, the DB stuff won't cause the form-loading to hang up. The form will load and show itself quickly and then the database work will be done after the form is already visible.
You may need to disable some, or all, of your controls on the child form until the DB work is complete. You may also want the child form to display some sort of spinning animation to show that it is still loading. The easiest way to implement separate threads in your UI, for things like this, is to use a BackgroundWorker component. You will find it in the Components section of your form designer tool box.
Solution :
Dim Thread as new Thread(sub()
ParentForm.BeginInvoke(sub()
CreateMDIChild()
end sub)
end sub)
Thread.Start()
With this, the MDI child form is still created by the parent form but everything is done in a separate thread.

Non modal form that blocks its Owner

I was trying to get forms configuration for my project that can suit my needs here and didn't get to a solution. It seem's I find a solution and now I have to implement it application-wide.
Here I have main form and a few subforms (which have many subforms) and through the main form I should ALWAYS be able to close all opened forms, watch for universal keypresses, close application permanently and watch for critical events.
I find solution in this facts:
From main form I open main subforms instantiated but non modally.
From sub forms I open deeper subforms also instantiated and non modally.
In all those subforms _Load event handler I disable calling form and in _FormClosing handler I enable it again. That way non modal forms act like modal forms in order to caller but not in order to main form which stays responsive all the time!
In forms that have to block its own caller (Owner) I added property "Blocking" so that my code looks like this:
If Not formIsOpened(frm_myFirstChild) Then
Dim f As New frm_myFirstChild
f.Blocking = True
f.Show(Me)
f = Nothing
End If
In the frm_myFirstChild I have property:
<Browsable(True), _
DefaultValue(False)> _
Public Property Blocking() As Boolean
Get
Return _Blocking
End Get
Set(ByVal value As Boolean)
_Blocking = value
End Set
End Property
If boolean property "Blocking" is TRUE then under _Load this code has to be executed:
If Blocking And Me.Owner IsNot Nothing Then
Me.Owner.Enabled = False
End If
In _FormClosing that:
If Blocking And Me.Owner IsNot Nothing Then
Me.Owner.Enabled = True
Me.Owner.Activate()
End If
All of that work as expected so I try to implement that for all forms and use when needed in a subclass "cls_transform":
Protected Overrides Sub OnFormClosing(ByVal e As System.Windows.Forms.FormClosingEventArgs)
If Blocking And Me.Owner IsNot Nothing Then
Me.Owner.Enabled = True
Me.Owner.Activate()
End If
MyBase.OnFormClosing(e)
End Sub
Protected Overrides Sub OnLoad(ByVal e As System.EventArgs)
If Blocking And Me.Owner IsNot Nothing Then
Me.Owner.Enabled = False
End If
MyBase.OnLoad(e)
End Sub
Here I have problem that subclass doesn't understand the property Blocked (not declared).
How do you get a Form's property Blocked to subclass so I can use those subclasses for all forms, and switch the property Blocking from the outside regarding functionality needs?
Sounds to me that "cls_transform" is actually a class derived from Form, the only way that OnFormClosing could work. Which is okay, your "sub-forms" now need to derive from cls_transform instead of Form. Do pick a better name.
Then simply add the Blocking property to that cls_transform class to solve your problem.
Do note that there's a bug in your OnFormClosing method. It can be canceled and that will leave the form opened with its owner in the wrong state. You need to write it like this instead:
Protected Overrides Sub OnFormClosing(ByVal e As System.Windows.Forms.FormClosingEventArgs)
MyBase.OnFormClosing(e)
If Not e.Cancel And Blocking And Me.Owner IsNot Nothing Then
Me.Owner.Enabled = True
Me.Owner.Activate()
End If
End Sub