20 Comments

.NET 2.0 introduced partial classes which enables ".designer" files in Visual Studio 2005 and later. That is, all of the visual designer-generated code (control declarations, the InitializeComponent method, etc) can be kept in a file separate from your regular code. When you open up a .NET 1.x Visual Studio 2003 WinForms project up in Visual Studio 2005/2008 it will upgrade your project to .NET 2.0 just fine, but unfortunately it doesn't migrate your WinForms classes over to the new ".designer" project structure.

Initially I thought this would be a job for a DXCore plug-in (the free framework upon which CodeRush is built) as it provides plug-ins with an object model of the code which could be used to grab all the right members and move them over into a designer file. Before I looked into this though I checked what the options were for simply implementing it as a Visual Studio Macro. I was fully expecting to have to use a regular expression to grep the code file to perform the task, but was pleasantly surprised to find that the Visual Studio extensibility API in available to macros provides a code model (based on the .NET CodeDom I presume) which you can traverse to inspect and manipulate the underlying code.

So, here's what the resulting "ExtractWinFormsDesignerFile" macro does:

  • Locates the first class in the selected project item (DTE.SelectedItems.Item(1).ProjectItem) by traversing the ProjectItem.FileCodeModel.CodeElements
  • Extracts the InitializeComponent and Dispose methods from the class by traversing CodeClass.Members
  • Extracts all control fields: that is, all fields whose type derives from System.Windows.Forms.Control or System.ComponentModel.Container or whose type name starts with System.Windows.Forms
  • Puts all the extracted code into  a new "FormName.Designer.cs" file.

This is currently C# only - it could easily be converted to generated VB.NET code or adapted use the FileCodeModel properly and perhaps create the code in an language-agnostic way when generating the designer file. I took a shortcut in just generating the designer file as a string and writing it directly to a file.

To "install": download the macro text and copy the methods into a Visual Studio Macro Module (use ALT+F11 to show the Macro editor).

To use:

  • Select a Windows Form in the Solution Explorer
  • Run the macro by showing the Macro Explorer (ALT+F8) and double-clicking the 'ExtractWinFormsDesignerFile' macro. (Obviously you can hook the macro up to a toolbar button if you like.)
  • You will then be prompted to manually make the Form class partial (another bit I was too lazy to work out how to get the macro to do): i.e. change public class MyForm : Form to public partial class MyForm : Form

Please leave a comment if this helps you.

Comments

Comment by Jose A. Ramirez

Hi Duncan,
it works perfectly now!
Thanks much,
Jose

Jose A. Ramirez
Comment by Duncan Smart

@Jose: yes I think you are missing something. Namely the last point: "manually make the Form class partial".

Comment by Jose A. Ramirez

I run the macro and it creates the designer class but the form in designer mode shows no controls just a blank grey form..

Please let me know if I am missing something.

Thanks,
Jose A. Ramirez

Jose A. Ramirez
Comment by Jaggy

Big thanks. Works like a charm.

Jaggy
Comment by Mike Smith

Hi Duncan
I came across your blog today and have just converted a project with over 100 forms to partial classes using your macro.
Thanks a lot for saving me a lot of work.

Mike Smith

Mike Smith
Comment by Alper

Hi Duncan,

Download link seems to be broken. Could you email macro code to me?

Thanks,
Alper

Alper
Comment by Diana

Great work! Save my life!

Diana
Comment by lakshmipathy

Thank you. you save a lot of time. i want to generate a boiler plate template so i used your code for getting the namespace and class name. thank you

lakshmipathy
Comment by Max

Thanks a lot!
Very useful tool!

Max
Comment by Duncan Smart

Thanks Adam, I'll look into updating my code with your changes.

Comment by Adam Best

Hey man,

Awesome macro, really helped me out a lot!

I made a few enhancements, like only creating the designer if one doesn't exist already, and automatically making the original class partial...

Sub ExtractWinFormsDesignerFile()
...
...
Dim newItemPath As String = dir & "\" & bareName & ".Designer.cs"

'Check if designer file already exists
'If it does, don't create one -- Adam Best
Dim childItems As ProjectItems = item.ProjectItems
Dim childItem As ProjectItem
Dim designer As Boolean = False

For Each childItem In childItems
If (childItem.FileNames(1) = newItemPath) Then
designer = True
Exit For
End If
Next

If (Not designer) Then
Dim codeClass As CodeClass = findClass(item.FileCodeModel.CodeElements)
...
...
makeOriginalClassPartial(item.FileCodeModel.CodeElements)

End If
End Sub

And the added function is...

'Make original class partial -- Adam Best
Function makeOriginalClassPartial(ByVal items As System.Collections.IEnumerable)

Dim text As String = String.Empty
Dim newText As String = String.Empty

For Each codeEl As CodeElement In items
If codeEl.Kind = vsCMElement.vsCMElementClass Then
Dim start As EditPoint = codeEl.GetStartPoint().CreateEditPoint()
Dim endPoint As TextPoint = codeEl.GetStartPoint(vsCMPart.vsCMPartBody)
text = start.GetText(endPoint)
If (Not text.Contains("partial class")) Then
newText = text.Replace("class", "partial class")
start.Delete(endPoint)
start.Insert(newText)
End If
ElseIf codeEl.Children.Count > 0 Then
Dim partialOrigClass As Boolean = makeOriginalClassPartial(codeEl.Children)
End If
Next

End Function

Adam Best
Comment by Guy

Very nice. I don't know why VS doesn't do this automatically to clean things up!

Thank you!

Comment by David C

Thank you so much! Not only does it work for windows forms, but also third party tools such as Datadynamics' Active Reports.

David C
Comment by Harald Mühlhoff

Thanks a lot! Worked as "advertised" ... ;)

Comment by jay

i have a win forms application developed in VS 2003, I have converted it to VS 2005 . after conversion we are having problems with some of the win forms, when i try open the design of win forms i see all html code. i tried doing like what you mentioned here but i m getting error on this line of macros code : "Dim namespaceName As String = codeClass.Namespace.FullName " and the error message i get is "object reference not set to an instance of an object"....please help me if i m doing something wrong.

jay
Comment by Craig Larsen

Works with VS2010, too. Thanks !

Craig Larsen
Comment by Kai Schings

Great job, thank you - this saved me so much time!

Kai Schings
Comment by Duncan Smart

The error suggests that there no namespace in the file - perhaps that the issue? You may need to adapt it to get it to work for you - it's pretty straightforward code.