If the Form_Initialize event handler contains one or more statements that access a form’s property or one of the controls on the form, the VB.NET code throws a NullReference exception.
The reason for this exception is trivial: in VB6 the Initialize event fires when no form resource has been created yet. Migrated VB.NET forms replicate this behavior by firing the Form_Initialize_VB6 method before any control variable has been assigned a reference to a non-Nothing object, hence the exception.
This exception is the symptom of bad programming practice in VB6. In fact, the code in a Form_Initialize event should never access a control on the form, because this action triggers the load of the form itself. You can prove this detail by running the following piece of VB6 code:
Private Sub Form_Initialize()
Debug.Print "Enter Initialize"
Me.Label1.Caption = "Test"
Debug.Print "Exit Initialize"
End Sub
Private Sub Form_Load()
Debug.Print "Enter Load"
Debug.Print "Exit Load"
End Sub
This is the output in the Debug window:
Enter Initialize
Enter Load
Exit Load
Exit Initialize
In other words, the Load event fires in the middle of the Initialize event, which isn’t probably what the VB6 developer expected. In some cases, this unintended behavior is the causes of subtle bugs, for example when the code in the second half of the Form_Initialize method relies on values and properties that have been set inside the Form_Load method.
There is no easy way to migrate VB6 code that relies on this undocumented behavior. Under VB.NET a reference to a control doesn’t automatically fire the Load event. However, you can use the HasBeenLoaded variable (defined in VB6Form base class) to detect whether the form has been already loaded or not, and rearrange the code as follows:
Protected Overrides Sub Form_Initialize_VB6()
If Not MyBase.HasBeenLoaded Then Exit Sub
End Sub
Private Sub Form_Load()
Call Form_Initialize_VB6()
End Sub
In some, very rare circumstances, you have to ideally split the code in Form_Initialize in two portions, the statements that don’t access any form property or control and the remaining statements. In this case you need an If…Then…Else block in Form_Initialize to exactly replicate VB6 behavior:
Protected Overrides Sub Form_Initialize_VB6()
If Not MyBase.HasBeenLoaded Then Exit Sub
Else
Me.Label1.Caption = "XXX"
End If
End Sub
Private Sub Form_Load()
Call Form_Initialize_VB6()
End Sub
Notice that the undocumented VB6 behavior affects also the UserControl_Initialize event: if the code in this event references a constituent control on the UserControl’s surface, then the VB6 runtime anticipates the allocation of Windows resources to be allocated to the UserControl.
UserControls are dealt with slightly differently than forms in converted VB.NET programs. In a converted UserControl, the UserControl_Initialize method fires after all constituent controls have been created,therefore no runtime exception occurs even if the code in the method references a constituent control.
If it is essential that the UserControl_Initialize event fires immediately after creating the UserControl (and before any other event), you can invoke the special FireInitializeEvent method (defined in the VB6UserControl base class) from inside the constructor of your UserControl class, which you can find in the code-behind portion of that class (i.e. the *.Designer.vb file):
<System.Diagnostics.DebuggerNonUserCode()> Public Sub New()
MyBase.New()
InitializeComponents()
FireInitializeEvent()
End Sub