.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 theProjectItem.FileCodeModel.CodeElements
- Extracts the
InitializeComponent
andDispose
methods from the class by traversingCodeClass.Members
- Extracts all control fields: that is, all fields whose type derives from
System.Windows.Forms.Control
orSystem.ComponentModel.Container
or whose type name starts withSystem.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.
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
Big thanks. Works like a charm.
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: yes I think you are missing something. Namely the last point: “manually make the Form class partial”.
Hi Duncan,
it works perfectly now!
Thanks much,
Jose
Hi Duncan,
Download link seems to be broken. Could you email macro code to me?
Thanks,
Alper
Great work! Save my life!
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
Thanks a lot!
Very useful tool!
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
Thanks Adam, I’ll look into updating my code with your changes.
Very nice. I don’t know why VS doesn’t do this automatically to clean things up!
Thank you!
Thank you so much! Not only does it work for windows forms, but also third party tools such as Datadynamics’ Active Reports.
Thanks a lot! Worked as “advertised” … 😉
There’s a version for VB code here that also fixes a couple of bugs in this one.
Works with VS2010, too. Thanks !
Great job, thank you – this saved me so much time!
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.
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.
I am running Visual Studio 2019, so it cant run this macro, Is there any other automatic way to do that?
Sorry Flavio, looks like it would have to be a manual job seeing as they’ve removed macros.
i think u can bring macro back into vs 2019 if u add it as Extension from ‘manage Extensions’ screen, search for macro