Click here to Skip to main content
65,938 articles
CodeProject is changing. Read more.
Articles
(untagged)

A Visual Studio add-in to move Windows Forms Designer generated code from a .vb file to a .Designer.vb file

0.00/5 (No votes)
19 Jan 2013 1  
Convert VB.NET 2003 Windows Forms Designer section of Windows Form Public Class files to Partial Class files.

Introduction

This article provides instructions for installing and using a Visual Studio 2012 add-in to move Visual Studio 2003 Windows Forms Designer generated code from the Public Class file to a Partial Class file. In addition, I tell the story of how I created the add-in.

When Visual Studio 2005 was released, the Visual Studio 2003 Windows Forms Designer generated code was removed from the .vb file and placed in a separate .Designer.vb file. Visual Studio 2012 still accepts the Windows Forms Designer generated code in the .vb file for an existing Project. As part of my Visual Studio 2012 learning process, I wanted to separate the Windows Forms Designer generated code into a Partial Class .Designer.vb file.

The Macro

I found several versions of a Visual Studio Macro for doing this at http://www.nathanpjones.com/wp/2010/02/converting-vb-net-2003-winforms-to-20052008-partial-classes. Unfortunately, I found that the Macros feature had been removed from Visual Studio 2012. After seeing the original author’s comment to “dig into it to get it to work the way you want", I decided to turn it into a Visual Studio Add-In. Something new to learn!

I want to express my thanks to the original Macro developer and to those that subsequently submitted their own amendments. That code gave me a jumpstart on this project. The original Macro traversed the Visual Studio automation object model of the selected Windows Form .vb file to locate variable declarations that were initialized by code within the Sub InitializeComponent procedure and to locate both the Sub InitializeComponent and Sub Dispose procedures. These along with a Partial Class clause are used to create a new .Designer.vb file. A subsequent version of the Macro, contributed by another programmer, selected variables derived from System.Windows.Forms.Control or System.ComponentModel.IContainer.

Background

Use this Visual Studio add-in to move Windows Form Designer generated code from a Public Class .vb file to a Partial Class .Designer.vb file.

The ConvertVBFiles VB.NET Project associated with his article is compiled to process all Windows Forms within a selected project. A conditional compilation option is provided to allow you to compile your version to only convert the currently selected Windows Form. Be sure to create a backup of your Visual Studio Solution/Project before attempting to use this Visual Studio Add-In.

Using the code

A compiled version of ConvertVBFiles.dll is included in the ZIP file. Skip steps 2 and 3 if you want to use the ConvertVBFiles.dll as-is with the conditional compilation declaration #CONVERT_ALL_FILES_IN_SELECTED_PROJECT = True.

  1. Extract the ConvertVBFiles Project source code into C:\Users\<USERNAME>\Documents\Visual Studio 2012\Projects.
  2. Edit the value of #Const CONVERT_ALL_FILES_IN_SELECTED_PROJECT = True to True or False.
    • True processes all Windows Forms within a Project.
    • False processes only the selected Windows Form.
  3. Compile your version of ConvertVBFiles.DLL.
  4. Extract the ConvertVBFile.AddIn file into C:\Users\<USERNAME>\Documents\Visual Studio 2012\Addins.
  5. Edit the ConvertVBFile.AddIn file replacing <USERNAME> with your username.
  6. <?xml version="1.0" encoding="UTF-16" standalone="no"?>
    <Extensibility xmlns="http://schemas.microsoft.com/AutomationExtensibility">
        <HostApplication>
            <Name>Microsoft Visual Studio</Name>
            <Version>11.0</Version>
        </HostApplication>
        <Addin>
            <FriendlyName>Convert VB 'Windows Form Designer generated code' Region to a separate *.Designer.vb file</FriendlyName>
            <Description>Convert VB 'Windows Form Designer generated code' Region to a separate *.Designer.vb file</Description>
            <Assembly>c:\users\<USERNAME>\documents\visual studio 2012\Projects\ConvertVBFiles\ConvertVBFiles\bin\ConvertVBFiles.dll</Assembly>
            <FullClassName>ConvertVBFiles.ConvertVBFiles</FullClassName>
            <LoadBehavior>0</LoadBehavior>
            <CommandPreload>1</CommandPreload>
            <CommandLineSafe>0</CommandLineSafe>
        </Addin>
    </Extensibility>

If you compiled your version with CONVERT_ALL_FILES_IN_SELECTED_PROJECT set to True, with your Solution open, select a Project and then click Tools, Convert to Designer Partial Class. The Add-In will iterate through all of the .VB forms, converting those that contain Windows Form Designer generated code and then display a Message Box detailing what it did.

If you compiled your version with CONVERT_ALL_FILES_IN_SELECTED_PROJECT set to False, with your Solution open, select Windows Form file within a Project and then click Tools, Convert to Designer Partial Class. The Add-In will convert that file and display a Message Box detailing what it did.

Removal

To remove the ConvertVBFiles add-in from Visual Studio, rename the ConvertVBFiles.AddIn extension in the C:\Users\<USERNAME>\Documents\Visual Studio 2012\Addins directory or delete the ConvertVBFile.Addin file.

Points of Interest

Conveniently, Visual Studio 2012 provides an Extensibility Project Type in the New Project dialog. I used this to start my new Add-In project. I pasted the Macro source into a new Sub ExtractWinFormsDesignerFile procedure in my new project and called it from the generated Sub Exec procedure.

I cleaned up a few syntax issues, added some Debug.WriteLine statements to help me learn the process flow, added a few breakpoints and proceeded to start testing. I found a few cases where the object model yielded Nothing values for an item. I added checks using IsNothing() to ignore these items. The version of the Macro code that I used moved some items that should not have been moved. For example, if I had declared a variable derived from something in System.Windows.Form outside of the Windows Form Designer generated code region, it got moved and it shouldn’t have been moved. An example is a Property declaration for a variable of type ListView.SelectedIndexCollection.

At this point, I had selected variable declarations using the following nested If statements.
If (member.Kind = vsCMElement.vsCMElementVariable) Then
    Dim objDeclaration As CodeVariable = DirectCast(member, CodeVariable)
    Dim objFieldType As CodeType = objDeclaration.Type.CodeType
    If Not IsNothing(objFieldType) Then
        If Not IsNothing(objFieldType.Namespace) Then
            Dim isControl As Boolean = objFieldType.Namespace.FullName.StartsWith("System.Windows.Forms")
            Debug.WriteLine("Namespace=" & objFieldType.Namespace.FullName)
            If isControl OrElse objFieldType.IsDerivedFrom("System.ComponentModel.IContainer") Then
                Debug.WriteLine("Added " & objFieldType.Namespace.FullName)
                strWindowsFormsControlsDeclarations.AppendLine(extractMember(member))
                intItemsConvertedCount += 1
            End If
        End If
    End If
End If

A Human Could Tell Which Code to Move

I decided to take advantage of the fact that the object model exposes the absolute character offset of each Sub procedure and each variable declaration. I used the offsets to determine if a variable declaration was within the Windows Form Designer generated code region. This strategy would take advantage of the order in which the Visual Studio 2003 Windows Form Designer wrote out the generated code.

This is the order in which the Visual Studio 2003 Windows Form Designer creates the generated code:

Public Class MainForm
    Inherits System.Windows.Forms.Form

    #Region " Windows Form Designer generated code "
 
    Public Sub New()
    …
    End Sub

    <System.Diagnostics.DebuggerNonUserCode()> _
    Protected Overloads Overrides Sub Dispose(ByVal disposing As Boolean)
    …
    End Sub

    …
    … Windows Form Designer generated variable declarations here …
    …

    <System.Diagnostics.DebuggerStepThrough()> Private Sub InitializeComponent()
    …
    End Sub

    Protected Overrides Sub Finalize()
    …
    End Sub

I added a check that the absolute character offset of a variable declaration was less than the absolute character offset of the Sub InitializeComponent procedure and greater than the character offset of either Sub New or Sub Dispose. If so, the variable declaration is moved to the new .Designer.vb file. If Dispose or New are not present, I use zero as their offset value. Doing this creates the small possibility that someone may have declared a System.Windows.Form-derived variable declaration before the Windows Form Designer generated code region. If they did, that variable declaration will be moved also but could easily be moved back with cut and paste.

If (member.Kind = vsCMElement.vsCMElementVariable) Then
    Dim objDeclaration As CodeVariable = DirectCast(member, CodeVariable)
    Dim objFieldType As CodeType = objDeclaration.Type.CodeType
    If Not IsNothing(objFieldType) Then
        If Not IsNothing(objFieldType.Namespace) Then
            Dim isControl As Boolean = objFieldType.Namespace.FullName.StartsWith("System.Windows.Forms")
            ' Character offset of this variable's declaration
            Dim intOffset As Integer = objDeclaration.StartPoint.AbsoluteCharOffset
            Debug.WriteLine("Namespace=" & objFieldType.Namespace.FullName)
            If isControl OrElse objFieldType.IsDerivedFrom("System.ComponentModel.IContainer") Then
                If intOffset_InitializeComponent > intOffset AndAlso (intOffset_Dispose < _
                            intOffset OrElse intOffset_New < intOffset) Then
                    Debug.WriteLine("Added " & objFieldType.Namespace.FullName)       
                    strWindowsFormsControlsDeclarations.AppendLine(extractMember(member))
                    intItemsConvertedCount += 1
                End If
            End If
        End If
    End If
End If

A Friend WithEvents

During testing, I found a few forms where I had declared some System.Windows.Forms-derived variables before the Windows Form generated code region. I made the following change to the Boolean statement that initializes the IsControl variable. The check of the Access property ensures that Friend and WithEvents are present. This further reduces the probability that a variable declaration from outside of the Windows Form Designer generated code region will be erroneously moved to the new .Designer.vb file.

Before

Dim isControl As Boolean = objFieldType.Namespace.FullName.StartsWith("System.Windows.Forms")

After

Dim isControl As Boolean = objFieldType.Namespace.FullName.StartsWith("System.Windows.Forms") AndAlso _
        CBool(objDeclaration.Access And (vsCMAccess.vsCMAccessWithEvents + vsCMAccess.vsCMAccessProject))

Visual Studio Tools Menu

I was experiencing an occasional problem where the Add-In would not put its menu item within the Visual Studio Tools menu. I think this was caused by running in Debug Mode, creating a second instance of Visual Studio (DEVENV.EXE) and not checking the checkboxes in Tools, Manage Add-Ins. I found that if I checked the check boxes, the DLL could not be replaced when I recompiled during testing. I changed one line of code within the Extensibility Project Type-provided code to allow the DLL another opportunity to create the Tools menu item. Within the Sub OnConnection procedure, I made the following change to cause the menu item add code to execute after startup.

Before

If connectMode = ext_ConnectMode.ext_cm_UISetup Then

After

If connectMode = ext_ConnectMode.ext_cm_UISetup OrElse connectMode = ext_ConnectMode.ext_cm_AfterStartup Then

Productivity Improvement

I continued to do testing and cleanup. My final enhancement was to use the conditional compilation feature to allow compiling a version of the Add-In that would iterate through all of the .vb files in a selected Project within the Solution.

Convert only the selected Windows Form within a Project

#Const CONVERT_ALL_FILES_IN_SELECTED_PROJECT = False

Convert all Windows Forms within the selected Project

#Const CONVERT_ALL_FILES_IN_SELECTED_PROJECT = True

PrintDocument

A few days after writing the initial version of this article, I found another type of Windows Form Designer generated variable declaration in one of my programs. To move this item from the .vb file to the .Designer.vb file, I changed the IsControl initialization to the following.

Dim isControl As Boolean = (objFieldType.Namespace.FullName.StartsWith("System.Windows.Forms") OrElse _
	objFieldType.Namespace.FullName.StartsWith("System.Drawing.Printing")) AndAlso _
	CBool(objDeclaration.Access And (vsCMAccess.vsCMAccessWithEvents + vsCMAccess.vsCMAccessProject))

History

  • 01-15-2013: Initial version.
  • 01-19-2013: Added check to handle PrintDocument declaration (Namespace: System.Drawing.Printing).

License

This article has no explicit license attached to it but may contain usage terms in the article text or the download files themselves. If in doubt please contact the author via the discussion board below.

A list of licenses authors might use can be found here