General Methodology

i.Identify the legacy types to be upgraded

The first step is to identify and isolate the classes that will be upgraded with the Custom Maps functionality. The best way to ensure you are using the correct name is to open the Object Browser in the Visual Basic 6.0 IDE (View->Object Browser) and look for the library and class names. These names are important, since they are used to identify the type to transform.

For this example, the following screenshot shows the TrueDBGrid control Library and Class name.

Object Browser

Accordingly, the full name of this type will be TrueOleDBGrid80.TDBGrid. Once all the names are gathered, we should create a transformation table to identify the source and target constructs. For the test application they are as follows:

VB6 original Name.NET Fully qualified name
TrueOleDBGrid80.TDBGridC1.Win.C1TrueDBGrid.C1TrueDBGrid
XArrayDBObject.XarrayDBSystem.Data.DataTable

Note: The Visual Basic Upgrade Companion is able to upgrade the XArrayDB object to a custom helper class. This can be toggled from the Profile Manager window.

ii.Identify the type members used throughout the code

In addition to the types, it is necessary to include the members of the types that need to be transformed. This allows the creation of a realistic assessment of the amount of manual work required to transform one legacy control to its .NET counterpart. For the test application the type table can be extended as follows:

VB6 original Name.NET Fully qualified name
TrueOleDBGrid80.TDBGridC1.Win.C1TrueDBGrid.C1TrueDBGrid
.ArrayNo equivalent
.RebindNo equivalent
XArrayDBObject.XarrayDBSystem.Data.DataTable
.ReDimNo equivalent
.Value (Default indexed property)No equivalent

All the members used in the original code must be extracted from user code on the Visual Studio 6 IDE. This means that the types and members that can be transformed using Custom Maps must appear on the user modifiable code. By using the Find tool from Visual Studio we can easily look for variables of the types to replace and consequently look for references of their members. Once all the members are gathered, the next step is to compare those exposed members against the .NET counterpart (of the TrueDBGrid in this case). There is only one exposed member shared between the legacy and .NET versions of the TrueDBGrid control which is the .refresh method. This means that for this case, most of the functionality requires manual implementation.

It is necessary to fill the table with all the member references found on the user code. This means that the code included in the designer will not be considered. All the vendor-specific graphical properties that can be tweaked on the designer will not be upgraded since they are written on the upper part of the form that contains the control. These properties are not considered user code and will not be automatically upgraded. These properties, however, must be reviewed to ensure the target control will behave as closely as possible to the original, but they have to be manually added in the target code.

iii.Creating a Custom Maps file for the transformation table

After the table contains the most commonly used members of the type to upgrade; the next step is to add the custom maps in the Custom Maps Editor.

  • Enter the name of the Visual Basic 6.0 project (*.vbp) where the transformation will be applied. The first field of each Custom Mapping is the project where it will be applied. If no project is specified the transformation will be applied to all the projects in the upgrade solution.

    Note: Use the logical (internal) name of the project, not the physical name.

  • The second field relates to the name of the legacy type/member we are attempting to upgrade. This name needs to be fully qualified. For example, the type transformation for the TrueDBGrid must have the value TrueOleDBGrid80.TDBGrid.

  • The third field is a drop box containing options for the map type between type and member. As we explained before, a type is any class, form, module, control or enumeration and members are any of the subs, properties or fields.

  • a.The next field must be completed with the fully qualified name of the .NET target for the custom map. For the TrueDBGrid map it corresponds to C1.Win.C1TrueDBGrid.C1TrueDBGrid

  • a.The Custom Maps editor allows several Reference Action to be set:

    • Leave, ignores the specified transformation.
    • Modify, applies the transformation specified on that line
    • Remove, comments out all declarations of variables of the type specified on this line, or if member is selected as the map type, comments out all the occurrences of the specified member from the resulting code.
  • The New Declaration Name field can be used to make the Custom Maps editor transform the declaration of a source type/member to a different name without replacing its declaration code. This is useful to use the Custom Maps functionality to refactor a particular set of types and members but for graphical controls all the consequent options will not be used since the declaration for types and members are compiled into their original OCX binaries.

The following screenshot shows how the Custom Maps editor should look like once all the information is entered:

Custom Maps editor should look like once all the information

Note that the exposed members that were not shared between the legacy and .NET version of the control where set to be eliminated from the code. This will comment out the line and add a EWI for easy tracking of these changes.

After all the parameters have been entered into the Custom Maps editor, we just need to enable the Custom Maps in the Profile Manager and we are ready to start the automated migration stage.

iv.Manual Changes Stage

Once the code is migrated using the Custom Maps above, it is necessary to carefully review the output code of the migration tool to look for any detail that requires manual changes. To better understand the transformations done by the Custom Maps tool, the following paragraphs compare code segments from the original and resulting code.

The first comparison is between the declarations of the two types replaced using Custom Maps. The TrueDBGrid and the XArrayDB where successfully replaced by the target types, C1TrueDBGrid and the DataTable. The declaration of the TruDBGrid control is contained into the upper part of the of the Form1.frm file, which is reason why it is necessary to open it with a plain text editor such as Notepad. It is also possible to review all the properties mentioned in section ii.Identify the type members used throughout the code. The VBUC will extract the basic designer information (position, size) but the rest of the specific properties need to be manually entered in the .NET control using the designer. In this example, the most important properties that require manual intervention are the column names. Let’s take a look at the declaration code:

Original VB6 code

Begin TrueOleDBGrid80.TDBGrid TDBGrid1 
      Height          =   2655
      Left            =   720
      TabIndex        =   0
      Top             =   840
      Width           =   7575
      _ExtentX        =   13361
      _ExtentY        =   4683
      _LayoutType     =   4
      _RowHeight      =   -2147483647
      _WasPersistedAsPixels=   0
      Columns(0)._VlistStyle=   0
      Columns(0)._MaxComboItems=   5
      Columns(0).Caption=   "Part Number"
      Columns(0).DataField=   ""
      Columns(0)._PropDict=   "_MaxComboItems,516,2;_VlistStyle,514,3"
      Columns(1)._VlistStyle=   0
      Columns(1)._MaxComboItems=   5
      Columns(1).Caption=   "Description"
    (…)
End

Resulting .NET code

public  C1.Win.C1TrueDBGrid.C1TrueDBGrid TDBGrid1;

(…)

private void  InitializeComponent()
{
    (…)
    this.TDBGrid1 = new C1.Win.C1TrueDBGrid.C1TrueDBGrid();
    ((System.ComponentModel.ISupportInitialize) this.TDBGrid1).BeginInit();
    // 
    // TDBGrid1
    // 
    this.TDBGrid1.Location = new System.Drawing.Point(48, 56);
    this.TDBGrid1.Name = "TDBGrid1";

       //Manual Change - This property is not needed
    //this.TDBGrid1.OcxState = (System.Windows.Forms.AxHost.State) resources.GetObject("TDBGrid1.OcxState");

    this.TDBGrid1.Size = new System.Drawing.Size(505, 177);
    this.TDBGrid1.TabIndex = 0;

    (…)
}

Note that the TDBGrid1.OcxState was included into the resulting code since this control was supposed to be used in .NET by means of a COM Runtime Callable Wrapper. This property is not exposed by the new native .NET component, so is no longer needed. The comment preceding that line was manually added to track manual changes easily. Review the upper part of the form1.frm file for the complete list of all the graphical properties of the TDBGrid1 control. The most important properties will be added to the resulting code in the next stages.

The next step is to review the declaration of the XArrayDB instance:

Original VB6 code

Dim XTrn As New XArrayDB

Private Sub Command1_Click()
(…)

    XTrn.ReDim 1, LineNo, 1, 4
    XTrn(LineNo, 1) = PartNum
    XTrn(LineNo, 2) = Desc
    XTrn(LineNo, 3) = LineNo + 2
    XTrn(LineNo, 4) = LineNo + (LineNo / 5)

(…)
End Sub

Resulting .NET code

private DataTable _XTrn = null;
DataTable XTrn
{
    get
    {
        if (_XTrn == null)
        {
            _XTrn = new DataTable();
        }
        return _XTrn;
}
    set
    {
        _XTrn = value;
    }
}

private void  Command1_Click( Object eventSender,  EventArgs eventArgs)
{
    (…)
//Manual Change - The XarrayDB object usage needs to be manually adapted
Object[] tempArray = new Object[4];
      tempArray[0] = PartNum;
      tempArray[1] = Desc;
      tempArray[2] = LineNo + 2;
      tempArray[3] = LineNo + (LineNo / 5d);
      this.XTrn = (DataTable)this.TDBGrid1.DataSource;
      this.XTrn.Rows.Add(tempArray);
                    
    //XTrn.ReDim(1, LineNo, 1, 4);
(…)
}

For the XArrayDB instance called “Xtrn” the VBUC applied a special transformation that ensures the members of form1 will be exposed in a safe way. The VBUC renamed the instance as “_Xtrn” and created a property to get and set this instance. This singleton pattern reproduces the behavior of the VB6 code accurately. The data type of this variable is not an ActiveX wrapper for the legacy XArrayDB object but a .NET ArrayList. This part of the code has no manual modifications whatsoever.

The next step is to populate the XArrayDB objects. This data type is used to store multidimensional arrays of information. In most scenarios it is used to store a collection of data rows which are at the same time collections of fields. In this case it is used as a bi dimensional array of data. The .NET resulting code was manually modified to store Arrays of object data types for each row of the Xtrn data container. This code now features a native .NET construction designed to store collections of data rows. To obtain the .NET code displayed in the previous example, it was necessary to:

  • Comment out the ReDim statement; this method is not exposed by the DataTable class and is not necessary since it has dynamic size
  • The original code uses the default property of the XArrayDB type called “Value”, this property allows this type to be accessed like an array. To populate the Xtrn DataTable we have to create a temporary Array of Object data types (“tempArray”) representing each row.
  • The last two lines are the .NET equivalents on how to associate the Xtrn DataTable to the TrueDBGrid instance. This code will be discussed later in the document.

So far, it has been necessary to comment out some unnecessary properties and re-factor the XArrayDB usage to work as a .NET DataTable. However, we configured the VBUC to delete 2 different members of the TrueDBGrid type.

//UPGRADE_NOTE: (8001) Element TrueOleDBGrid80.TDBGrid.Array was removed More Information: http://www.vbtonet.com/ewis/ewi8001.aspx
    //TDBGrid1.Array = XTrn.GetOcx;

//UPGRADE_NOTE: (8001) Element TrueOleDBGrid80.TDBGrid.ReBind was removed More Information: http://www.vbtonet.com/ewis/ewi8001.aspx
    //TDBGrid1.ReBind();

These members added information to the grid and made it visible. It is now necessary to manually add this functionality to the .NET code.

Once the changes are done to the type declarations, the next step is to make sure that their invocations and functionality is also preserved. The next section shows the last details required to achieve functional equivalence.

Graphical Properties of the TrueDBGrid control: on this example, the columns have particular names that need to be manually configured into the resulting code. To make sure that the code will be as .NET-like as possible the code to set those properties is added directly in the designer code, specifically in the “InitializeComponents” method. It is necessary to add the following code after the properties of the grid:

//Manual Change - Adding the Columns with their original names
this.TDBGrid1.Columns.Add(new C1.Win.C1TrueDBGrid.C1DataColumn("Part Number", typeof(string)));
this.TDBGrid1.Columns.Add(new C1.Win.C1TrueDBGrid.C1DataColumn("Description", typeof(string)));
this.TDBGrid1.Columns.Add(new C1.Win.C1TrueDBGrid.C1DataColumn("Quantity", typeof(string)));
this.TDBGrid1.Columns.Add(new C1.Win.C1TrueDBGrid.C1DataColumn("Price", typeof(string)));

//Manual Change - set the data biding to null
this.TDBGrid1.SetDataBinding();

All the rest of details that refers to the graphical appearance of the TrueDBGrid control can be manually set from this method or if desired, from the Visual Studio Graphical Designer.

Display the information into the TrueDBGrid control: The VBUC was configured to delete the appearances of 2 members of the TrueDBGrid control. These members used to set the internal bi dimensional array that stores the rows of the grid and the method that updates the graphical representation of the grid against the stored data.

In order to make this functionality available it is necessary to add two additional lines of code after the temporary Array of object data types is created.

      this.XTrn = (DataTable)this.TDBGrid1.DataSource;
      this.XTrn.Rows.Add(tempArray);

The first line assigns the Xtrn DataTable instance as a reference to the current DataSource of the .NET TrueDBGrid instance. This is necessary to ensure the Xtrn property will match the internal data representation of the TDBGrid1 instance in the number of columns, their data types and many other parameters.

The second line adds a new row to the Xtrn DataTable. Since the previous line assigned the Xtrn property to the DataSource member of the TDBGrid1 instance, this will add a new row of data into the graphical control as well.

The final product is the .NET version of the original application using the .NET counterparts of the original controls, plus some important transformations that improves the code appearance.

Continue reading:


See also: