Using a Helper class with Custom Maps

    Another approach that greatly enhances the custom maps functionality is to upgrade to a custom developed helper class. With this strategy, the helper exposes the required members the way they are used in the VB6.0 code, in a single encapsulated file or project. This methodology reduces the largest amount of manual work, yet it is time-consuming since it requires to manually fix all the references to a given type after the custom maps transformation is applied. It is important to be aware that both the previous and this approach require manual work; however, applying these techniques increases the quality of the output code, make it more maintainable and satisfies specific requirements of the target code.

    This section details the steps required to upgrade to a custom developed helper class.

    Identify the legacy types to be upgraded

    This stage is exactly the same as in the general methodology, the members to be upgraded have to be identified and extracted into the transformation table. The main difference here is that the resulting structures will not be directly the .NET version of the controls but custom classes from a helper namespace. For this example the transformation table is:

    VB6 original Name .NET Fully qualified name
    TrueOleDBGrid80.TDBGrid Helpers.TrueDBGrid
    XArrayDBObject.XarrayDB Helpers.XArray

    The helpers namespace is an additional project that needs to be created manually in the same Visual Studio solution as the migrated code. Its objective is to encapsulate in just one place all the manual fixes required to achieve functional equivalence.

    Identify the type members used throughout the code

    This part of the methodology is also the same as in the general methodology; the difference resides in the fact that it is necessary to use the names from the helper classes instead of the .NET targets.

    VB6 original Name .NET Fully qualified name
    TrueOleDBGrid80.TDBGrid Helpers.TrueDBGrid
    .Array .Array
    .Rebind .Rebind
    .Refresh .Refresh
    XArrayDBObject.XarrayDB Helpers.XArray
    .ReDim .ReDim
    .Value (Default indexed property) .Value

    As shown in the table, it can be safely assumed that the helper classes expose the same members as in the original VB6 code. This allows the implementation of only the required members, while leaving a stub for the remaining ones. This approach simplifies the construction of the Custom Maps file and result in less compilation errors.

    Creating a Custom Maps file for the transformation table

    As in the general methodology, the specific transformations to be applied over the resulting source code need to be entered in the Custom Maps editor. The main difference is that it is not necessary to delete any members since they are handled in one way or another on the helper class.

    The most important detail here is that there is no need to declare the transformations for the type members. At the moment that we add a transformation for a given type, all its references will be to the same member, but part of the helper class in the .NET code. For instance the following VB6 code:

              Dim XTrn As New XArrayDB
              XTrn.ReDim 1, LineNo, 1, 4

    Will result in a .NET code that will change the type of the Xtrn variable to a Helpers.XArray object, but the reference to the member ReDim will remain the same since there was no transformation to delete it or change it. The resulting code will assume that the ReDim member is exposed by the Helpers.XArray object.

    By just doing these simple transformations, the custom maps file will looks like the following screenshot:


    Manual fix/implementation stage

    After the automated migration stage has been completed, it is necessary to add the helper class functionality to the resulting solution. As the first step, this section shows how to implement a simple helper class to provide the legacy functionality on .NET, and then review the details on how to use these helpers on the resulting source code to obtain a fully functional product.

    Once the code has been upgraded using the Custom Maps file defined for the helper classes, the type declarations for the legacy constructions where replaced by types with the qualified namespace “Helpers.<Class>”.

    This means that it requires in the resulting solution a different project named “Helpers”. This project must be a class library and this library should contain definitions for each and every class used for the helpers. In this case, the helper library should contain definitions for the “TrueDBGrid” and “XArray” classes.

    Each and every class contained on the helper namespace is intended to expose the same members as the legacy member that was replaced; this guarantees all the appearances of these members will preserve functional equivalence. Also, when the helper classes are modeled to expose the same members as the legacy constructions, the amount of manual work is minimized since all the code that references these methods will remain intact. In other words, using a custom helper class isolates the largest part of manual work into a single project, thus increasing migration productivity and code maintainability.

    The exact implementation of each class inside the helper namespace is completely up to the requirements of the project. This means that the actual contents of these helper classes can be modeled as the requirements demand; nevertheless, these classes should follow these guidelines:

    • All the helper classes should expose the same members as the VB6 source code whenever possible. By following these guideline, all the other source code that reference these classes will not have compilation errors after the automatic migration stage.
    • The helper classes are intended to be specialized instances of the .NET counter parts of the original controls. This means that the “TrueDBGrid” and “XArray” classes present in the helper library of the example will very likely inherit from the “C1.Win.C1TrueDBGrid.C1TrueDBGrid” and the “System.Data.DataTable” classes. This ensures the resulting .NET code will be full .NET source code that just happens to have the same legacy members. They can be viewed as adapters for the interface expected in the migrated code, and the interface of the actual component.
    • The helper class approach should have the minimal size that offers functional equivalence. The main goal behind the “helper class” concept is to reduce the gap between VB6 and .NET without adding unnecessary overhead to the resulting application. For example, if the original application only uses 20% of the exposed members of a given control, the helper implementation should contain only that percentage instead of the complete set of members from the VB6.0 control.

    As shown in the general methodology, the declaration for each of these classes will remain the same, with the difference that the type used will no longer be the direct .NET counterpart of the control but a helper class.

    The most important difference is the actual usage of the helper classes in the source code. When using direct .NET control, it was necessary to re-factor the occurrences of the TrueDBGrid members to provide the desired functionality. On the other hand, the source code using the helper classes only required the manual effort to adapt the .NET control to expose the same members as the legacy VB6.0 control. The following two examples show this difference:

    .NET code using the .NET control:

    private void  Command1_Click( Object eventSender,  EventArgs eventArgs)
        Quant = LineNo;
        string PartNum = "Part Number " + LineNo.ToString();
        string Desc = "Description Number " + LineNo.ToString();
    //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;
        //XTrn.ReDim(1, LineNo, 1, 4);
        //XTrn.set_Value(LineNo, 1, PartNum);
        //XTrn.set_Value(LineNo, 2, Desc);
        //XTrn.set_Value(LineNo, 3, LineNo + 2);
        //XTrn.set_Value(LineNo, 4, LineNo + (LineNo / 5d));
    //UPGRADE_NOTE: (8001) Element TrueOleDBGrid80.TDBGrid.Array was removed More Information:
        //TDBGrid1.Array = XTrn.GetOcx;
    //UPGRADE_NOTE: (8001) Element TrueOleDBGrid80.TDBGrid.ReBind was removed More Information:
        Text2.Text = Text1.ToString() + " Times";

    As shown in the source code above, the resulting .NET code required modifications to achieve functional equivalence, but the final product boasts the .NET control equivalent without any type of modification. The following example shows the code using the Helper classes:

    .NET code using helper classes:

    private void  Command1_Click( Object eventSender,  EventArgs eventArgs)
        Quant = LineNo;
        string PartNum = "Part Number " + LineNo.ToString();
        string Desc = "Description Number " + LineNo.ToString();
        XTrn.ReDim(1, LineNo, 1, 4);
        XTrn.set_Value(LineNo, 1, PartNum);
        XTrn.set_Value(LineNo, 2, Desc);
        XTrn.set_Value(LineNo, 3, LineNo + 2);
        XTrn.set_Value(LineNo, 4, LineNo + (LineNo / 5d));
    //UPGRADE_WARNING: (1037) Couldn't resolve default property of object TDBGrid1.Array. More Information:
        TDBGrid1.Array = XTrn.GetOCX;
        Text2.Text = Text1.ToString() + " Times";

    This second approach offers .NET code that looks and feels much more like the original VB6.0 code, but without any ties to the original controls. It can also be observed that the member usage is preserved throughout the code. This exemplifies another advantage of the helper approach - this helper class will work against any other upgraded code that uses the TrueDBGrid legacy control.

    See also:

    Download VBUC Free Trial
    Download VBUC Now

    It's time to eradicate VB6
    ROI of eradicating VB6

    8 Proven Tips for
    Planning a Successful Migration

    8 Tips for migration