Converting a VB6 Application to VB.NET

Visual Basic logo

Recently I was tasked with stabilizing an old Windows Forms app written in Visual Basic 6.  VB6 was a great language for simple applications in its heyday, but now that the .NET Framework is 10 years old and new PCs all have 64-bit CPUs, it’s high time for an upgrade.

Fortunately, all versions of Visual Studio from VS.NET to VS 2008 provided a Visual Basic Upgrade Wizard that could be used to convert a VB6 app to a VB.NET application.  Unfortunately, this process is rarely without problems.  (Microsoft did not include the VB Upgrade Wizard in VS 2010 or VS 2012. If you use either of these, a newer version of the tool can be purchased from ArtinSoft, which also provided the one included with the earlier VS versions.)
The process should go like this: You have a VB6 project with project file (.vbp extension).  You can open and compile this project on VB6.  You then close VB6 and open the project file with Visual Studio 200x, and the Upgrade Wizard converts the project to .NET.
How the process usually goes: You have a VB6 project with project file.  You can open and compile this project on VB6.  You then close VB6 and open the project file with Visual Studio 200x, and the Upgrade Wizard hangs while parsing a form or during some other conversion process.  You may be forced to kill the VS task in Task Manager if the Cancel button doesn’t work, and will likely not get an error log as to what or why it wouldn’t upgrade.  You then begin disabling references to third-party (and even some MS) ActiveX controls or other DLLs, one at a time, to figure out which one(s) are causing the problem.  Much wailing and gnashing of teeth ensues as you discover that the Upgrade Wizard often behaves more like the Sorcerer’s Apprentice rather than Merlin.   The process can be painless, but it usually isn’t.

You will need to have both VB6 and a version of Visual Studio that includes the Upgrade Wizard.  It has been my experience that VB6 must be installed first for the Wizard to be installed.  I had VS 2008 installed on my development PC, and later installed VB6.  No dice; VS 2008 didn’t recognize files with “.vbp” as project files.  I then installed VS 2005, where the Wizard was eventually used by me on this project.  (I probably could have uninstalled and reinstalled VS 2008, but I didn’t see the need.)

To begin, I opened the project in VB6, and with a few tweaks here and there, it compiled and ran with no errors.  I closed VB6.  Next, I opened the project using VS 2005.  After the standard “Next–>Next–>…–>Finish” set of dialog boxes the conversion process began.  After less than a minute, it hung with no error dialogs.  After killing the task and trying a couple more times, I found out that indeed there was an error dialog popping up — behind Visual Studio — where I couldn’t see it while VS was hung.

Fortunately, the dialog was written by the maker of the third-party ActiveX control that was causing the problem.  I manually edited the project file with Notepad to remove the reference, and was able to complete the conversion to VB.NET in about 20 minutes.  Afterwards, I loaded up the new project file — the one created with VS 2005 — in Visual Studio 2010 and changed the .NET Framework from 2.0 to 4.0.

(At this point, those ActiveX components and DLLs that were disabled before the conversion can be re-referenced, assuming they work with the new environment.  Otherwise, recoding may be necessary.)

That was just the beginning.  From here, I found that there were several VB6 methods that don’t have direct equivalents in the .NET Framework, which Visual Studio changed to “VB6.<<method name>>“.  These methods will still work, but raise Warnings (the yellow exclamations) as they have been deprecated and will not be supported in some future version of VS.  They also would not be supported if the app ran as a 64-bit process, so until the methods are manually changed, the compiled program must be run as a 32-bit process.

Since I had hundreds of these Warnings, and VS will only display about 100 of them, I refactored those methods by creating a class with deprecated methods and called the new method, which then referenced the old one.  (This is done so that you can see more, hopefully all, of the Warnings before actually beginning to fix them.  It gave me a good idea of how much more work was to be done before testing was necessary.)

In cases where an entire class is deprecated (like ADODC), I created a new class that inherited from the deprecated base class.  In this way, the number of Warnings can be minimized, and the new classes and methods can later be rewritten using the .NET Framework rather than relying on backwards compatibility with VB6.  This did not work  for methods that are part of the deprecated class; each call to the method from the inherited class also raised a Warning.

In some cases, the calls to these methods and the instances of these classes will require the calling code to be rewritten, though it will not always be necessary; sometimes just a new definition of the class or method will allow the old code to work just fine.

I am not finished with this upgrade as yet, though things are moving more smoothly now.  Apparently, the backwards compatibility with the VB6 classes and methods will remain through .NET Framework 4.5, but will likely be removed with .NET 5.0.

Creating a Custom GridView Object

ASP.NET logo

An earlier post dealt with creating a GridView programmatically.  In some cases, this GridView might need to be used multiple times throughout a site, either using the same data, or perhaps a different set of data.  Several options exist when determining how to implement this.

The simplest, of course, would be simply to copy the code and then make changes in each copy as needed.  This clearly violates the DRY principle, and will only lead to heartache (and possibly carpal tunnel syndrome) in the long run.  As soon as the customer wants the look and feel of the GridView to change, you would have to change each instance where the code was copied.

The second, and slightly more desirable option, would be to refactor this code into a method that creates the GridView by calling the method.  While this is much preferable to having 20 verbatim copies of code in your site, it’s still not ideal.  This methodology contributes to spaghetti programming, and should be avoided when feasible.

The ideal method is to create an object that inherits from the GridView class, and then setting the attributes in the object.  Once the object is instantiated, any attributes that need to be added or overridden can be at runtime, prior to a databind event.

Here is an example of a class that inherits from GridView:

Public Class MyGridView
Inherits System.Web.UI.WebControls.GridView
Public Sub New(ByVal strID As String)
  ID = strID
  CssClass = "gridview"
  AutoGenerateColumns = False
  CellPadding = 4
  DataKeyNames = New String() {"ExampleID"}
  ForeColor = Drawing.ColorTranslator.FromHtml("#2a2723")
  GridLines = GridLines.None
  Width = Unit.Percentage(100)
  AllowSorting = True
  AllowPaging = False

  Dim strHeadBack As String = "#ffcb00"
  Dim strPagerBack As String = "#009ddb"
  Dim strForeColor As String = "#000000"

  HeaderStyle.BackColor = Drawing.ColorTranslator.FromHtml(strHeadBack)
  HeaderStyle.Font.Bold = True
  HeaderStyle.ForeColor = Drawing.ColorTranslator.FromHtml(strForeColor)

  RowStyle.BackColor = Drawing.ColorTranslator.FromHtml("#FFFBD6")
  RowStyle.ForeColor = Drawing.ColorTranslator.FromHtml("#2a2723")
  RowStyle.HorizontalAlign = HorizontalAlign.Center

  AlternatingRowStyle.BackColor = Drawing.Color.White

  BorderColor = Drawing.ColorTranslator.FromHtml("#d80073")
  BorderStyle = BorderStyle.Groove

  Dim ViewButton As New ButtonField
  ViewButton.HeaderText = "View"
  ViewButton.ButtonType = ButtonType.Button
  ViewButton.Text = "View"
  ViewButton.CommandName = "ViewExample"
  Columns.Add(ViewButton)

  Dim EditButton As New TemplateField
  EditButton.HeaderText = "Edit"
  EditButton.ItemTemplate = New MyButtonTemplate ' This is a user-defined class that creates this Button
  EditButton.Visible = bEditVisible
  Columns.Add(EditButton)

  Dim Voided As New CheckBoxField
  Voided.HeaderText = "Voided"
  Voided.DataField = "Voided"
  Voided.ReadOnly = True
  Voided.Visible = bVoidVisible
  Columns.Add(Voided)

  Dim ExampleDate As New TemplateField
  ExampleDate.HeaderText = "Date/Time"
  ExampleDate.SortExpression = "Date_and_Time"
  ExampleDate.ItemTemplate = New MyLabelTemplate ' This is a user-defined class that creates this Label
  ExampleDate.ItemStyle.Wrap = False
  Columns.Add(ExampleDate)

  Dim ShortColumn As New BoundField
  ShortColumn.ItemStyle.CssClass = "left"
  ShortColumn.HeaderText = "Short Column"
  ShortColumn.SortExpression = "Short_Column"
  ShortColumn.DataField = "Short_Column"
  ShortColumn.ItemStyle.Wrap = True
  ShortColumn.ItemStyle.Width = 150
  Columns.Add(ShortColumn)

  Dim LongColumn As New BoundField
  LongColumn.ItemStyle.CssClass = "left"
  LongColumn.HeaderText = "Long Column"
  LongColumn.SortExpression = "Long_Column"
  LongColumn.DataField = "Long_Column"
  LongColumn.ItemStyle.HorizontalAlign = HorizontalAlign.Left
  LongColumn.ItemStyle.Wrap = True
  LongColumn.ItemStyle.Width = 200
  Columns.Add(LongColumn)

  Dim CreatedBy As New BoundField
  CreatedBy.HeaderText = "Created By"
  CreatedBy.SortExpression = "CreatedBy"
  CreatedBy.DataField = "CreatedBy"
  Columns.Add(CreatedBy)
End Sub
End Class

To instantiate the object, simply declare the GridView as “MyGridView”, as below:

Private WithEvents gv1 As New MyGridView("gv1")

'...

gv1.DataSource = dvDataView  'A previously defined DataView

'...any other attributes to be added or overridden

' To remove a column use:

gv1.Columns.RemoveAt(x) ' where x is the index of the column

' Bind the data to the GridView

gv1.DataBind()

phPlaceHolder.Controls.Add(gv1)

And that’s it!  The custom GridView is like any other GridView; DataBind() can be called to rebind data, it can be hidden or made visible, etc.

Implementing a Factory Pattern in ASP.NET

factory pattern diagram

A question posed by @Ramsharma1234 on Twitter this morning asked how to implement Factory Patterns in ASP.NET.

(definition of Factory Pattern)

Below is what would be considered a very basic form of Factory Pattern.  This method will essentially instantiate a generic Object, which is the parent of all types of Objects, and later be treated as if it were the type of child object that is used as a parameter in calling the method.  On a Web site where I needed to build Web controls dynamically, based on values from a database query, I created a method called “AddControl” that would add a generic Object to a Placeholder on my Web form:

Private Sub AddControl(ByVal oControl As Object)
   Placeholder1.Controls.Add(oControl)
End Sub

To call this method, I would instantiate a Web control such as a Button and add it to the Placeholder with the method:

Dim btnPrint As New Button
btnPrint.Attributes.Add("onclick", "javascript:window.print();")
btnPrint.Visible = False
AddControl(btnPrint)

Any type of Web control (TextBox, Label, Literal, etc.) could be instantiated and then added using this method.  For instance, if the table value for a particular field indicated the creation of a TextBox, this would be how that could be accomplished:

Dim oControl As New Object
'...
oControl = New TextBox
With oControl
.ID = strFieldName & "_mltxt"
.TextMode = TextBoxMode.MultiLine
.MaxLength = 2000
.Style("overflow") = "hidden"
.Height = 300
.Width = 500
.BorderStyle = BorderStyle.None
.Enabled = True
.ReadOnly = True
End With
AddControl(oControl)