Migrating VB6 Control Arrays with the VBUC

In Visual Basic 6.0, Control Arrays were a useful feature that allowed programmers to group a series of controls into an array structure, created and initialized by VB6. Event handling code for events raised by these controls could be written in a single event handler procedure for each event, and the index of the array element that triggered the event would be passed as a parameter to the handler procedure.

Visual Basic .NET and C# do not have the concept of Control Arrays as a native structure, maintained by the language itself. In order to convert this feature, the VBUC declares a new Array containing the controls that were included in the original VB6 Control Array and then adds extra coding to the Form Designer File to initialize those controls. The following example shows a VB6 application that has a Control Array called “Buttons”, which contains VB6 CommandButton objects, and how it is converted to C#.

VB6 Application showing the CommandButton controls contained in the Buttons array

Figure 1. VB6 Application showing the CommandButton controls contained in the “Buttons” array

The C# version of this form will contain the following code in the Form1.Designer.cs file, which declares all the Buttons contained in the original VB6 Control Array:

private  System.Windows.Forms.Button _Buttons_4;
private  System.Windows.Forms.Button _Buttons_3;
private  System.Windows.Forms.Button _Buttons_2;
private  System.Windows.Forms.Button _Buttons_1;
private  System.Windows.Forms.Button _Buttons_0;
public System.Windows.Forms.Button[] Buttons = new System.Windows.Forms.Button[5];

The VBUC will also include a new method that will explicitly add all these Button controls to the “Buttons” array:

void  InitializeButtons()
{
    this.Buttons[4] = _Buttons_4;
    this.Buttons[3] = _Buttons_3;
    this.Buttons[2] = _Buttons_2;
    this.Buttons[1] = _Buttons_1;
    this.Buttons[0] = _Buttons_0;
}

This InitializeButtons method will be called right after InitializeComponent is called from the Form’s constructor. InitializeComponent will create the actual instances of the controls.

public Form1():base()
{
    Additional code removed for clarity purposes
    //This call is required by the Windows Form Designer.
    InitializeComponent();
    InitializeButton();
}

As mentioned before, in VB6 a single method could handle a specific event for all the members of the Control Array. In this example, an event handler is used to respond to the Click event of all the VB6 CommandButton controls contained in the “Buttons” Control Array:

Private Sub Buttons_Click(Index As Integer)
         Select Case Index
        Case 0
            MsgBox ("Buttons(0) was clicked")
        Case 1
            MsgBox ("Buttons(1) was clicked")
        Case 2
            MsgBox ("Buttons(2) was clicked")
        Case 3
            MsgBox ("Buttons(3) was clicked")
        Case 4
            MsgBox ("Buttons(4) was clicked")
    End Select
End Sub 

In order to convert this Event Handler, the VBUC adds an “Index” variable, which corresponds to the actual index of the Button that raises the event. This completes the migration of the “Buttons” Control Array:

private void  Buttons_Click( Object eventSender,  EventArgs eventArgs)
{
    int Index = Array.IndexOf(Buttons, eventSender);
    switch(Index)
    {
        case 0 : 
            MessageBox.Show("Buttons(0) was clicked", …); 
            break;
        case 1 : 
            MessageBox.Show("Buttons(1) was clicked", …); 
            break;
        case 2 : 
            MessageBox.Show("Buttons(2) was clicked", …); 
            break;
        case 3 : 
            MessageBox.Show("Buttons(3) was clicked", …); 
            break;
        case 4 : 
            MessageBox.Show("Buttons(4) was clicked", …); 
            break;
    }                
}

VB6 Application showing the CommandButton controls contained in the Buttons array migrated

Figure 2. C# Application showing the Button controls contained in the “Buttons” array and responding to the Click event

ISSUE #2064

1% 2% 3%. 4% was not upgraded.

Description

The VBUC converts members of a VB6 control library to their .NET equivalents whenever possible. The corresponding .NET elements are chosen to provide functional equivalence on the target platform.

In particular scenarios, some class properties may not have a direct equivalent in .NET or may have not be mapped in the current release of the VBUC. The VBUC generates the 2064 EWI to inform the user about these specific cases.

If the Stub Generation optional feature is enabled the VBUC will create dummy declarations for these properties and reference them instead of the original reference to the not-supported property. This feature reduces the amount of compilation errors and provides the user with the possibility to manually implement the property's functionality in the stub-declaration.

Recommendations

It will be necessary to implement the original property functionality through other means. This goal can be achieved through one of the following approaches:

  • Implement the functionality from scratch.
    • If the stub generation feature is enabled it can be implemented in one place, the generated stub declaration.
    • If the stub generation feature is not used, it is necessary to change all references to the new .NET implementation will be required.
  • Use an already existing method/property.
    • From .NET core libraries: Depending on what functionality is expected from a property, a particular VBUC user might be satisfied with a .NET equivalent which while not 100% equivalent, is sufficient for the specific case.
    • From third party libraries: Some vendors might provide functionality previously available in VB6 and not in the .NET core libraries.
  • Refactor the code to use another method/property. This strategy makes sense when the underlying .Net object models are very different to the VB6 ones. In these cases it is likely that a new approach will be the key.
  • The Custom Mappings feature might be quite useful to help customize some VBUC property conversions. It allows the user to specify the .NET equivalents for VB6 library elements.
    See Custom Mappings Feature.

Public Sub SetComboDataSource(ByVal combo As ComboBox)

  Dim rs As ADODB.Recordset

  rs = CreateRecordset()

  combo.DataSource = rs

End Sub

Target VB.NET

Public Sub SetComboDataSource(ByRef combo As ComboBox)

  Dim CreateRecordset() As Object

  Dim rs As ADODB.Recordset = CreateRecordset

  'UPGRADE_ISSUE: (2064) ComboBox property combo.DataSource was not upgraded.

  UpgradeStubs.VB_Control.setDataSource(combo, rs.Fields)

End Sub

Expected VB.NET

In this particular case the Stub Generation functionality was enabled. So there are two options, implement logic in the setDataSource stub created for this case.
 
So we define an implementation for setDataSource, which will vary according to the application's needs.
 

Public Sub SetComboDataSource(ByRef combo As ComboBox)

  Dim rs As ADODB.Recordset = TestCreateRecordset()

  UpgradeStubs.VB_Control.setDataSource(combo, rs.Fields)

End Sub

 

Public Shared Sub setDataSource(ByVal instance As Control, ByVal DataSource As msdatasrc.DataSource)

  ' Include relevant implementation for application

End Sub

 

Target C#

static public void  SetComboDataSource( ComboBox combo)

{

  ADODB.Recordset rs = TestDataSource.TestCreateRecordset();

  //UPGRADE_ISSUE: (2064) ComboBox property combo.DataSource was not upgraded.

  UpgradeStubs.VB_Control.setDataSource(combo, (msdatasrc.DataSource) rs);

}

Expected C#

In this particular case the Stub Generation functionality was enabled. So there are two options, implement logic in the setDataSource stub created for this case.
 
So we define an implementation for setDataSource, which will vary according to the application's needs.
 

If the target framework is .Net 3.5 an Extension Method may be used instead of the generated stub to provide cleaner looking code that more closely matches the original.

 

static public void  SetComboDataSource( ComboBox combo)

{

  ADODB.Recordset rs = TestDataSource.TestCreateRecordset();

  UpgradeStubs.VB_Control.setDataSource(combo, (msdatasrc.DataSource) rs);

}

 

public static void  setDataSource( Control instance,  msdatasrc.DataSource DataSource)

{

  // Include relevant implementation for application

}

Talk To An Engineer