NArrange .NET Code Organizer Adds StyleCop Compatibility

I just released NArrange Beta Version 0.2.7. This release features a default configuration that is compatible with the Microsoft StyleCop member ordering rules. If you haven't yet tried out StyleCop, I highly suggest taking a look at it. It provides fine grain source analysis for coding style and covers many areas outside the scope of NArrange. However, StyleCop only reports violations and does not fix them. This is where NArrange comes in to make quick work of formatting your source code to match the member ordering rules. As this is the first NArrange release with StyleCop compatibility, please report any issues to the SourceForge bug tracker.

Also, I'd like to thank Justin Dearing for getting things rolling with the MSI distribution. Your help is much appreciated.

For more background on NArrange, this CodeProject article may be helpful.

NArrange: Organize Your C# Source Code

So I've been throwing a few hours here and there over the last month into my first open source project...

"NArrange is a tool for arranging .NET source code. This code beautifier allows you to sort and organize C# code members into groups or regions. Currently, only C# source files are supported, but one goal will be to add support for other .NET languages."

If your interested in trying it out, I recommend setting it up as an External Tool for Visual Studio (see the Tools menu). If you pass $(SolutionFileName) as the arguments, it will arrange all writable C# source files in the solution. Otherwise you can supply an individual source file, project or solution to the command line executable. For more information, view the project's readme.txt.

As this tool is still in an Alpha stage, I highly recommend making a backup of your code, or committing any pending changes to revision control, prior to running the tool. One requested feature has been to add auto backup to the tool... I hope to find time to add this functionality in the near future.

NArrange at SourceForge

Looking forward to any feedback,

James

VS 2005 Designer Class Macro for C#

When projects are converted from VS 2003 to VS 2005, component classes are not automatically split out to have the new designer partial classes (e.g. MyForm.cs and MyForm.Designer.cs). 
 
So, I thought I'd throw together a Visual Studio macro that takes care of this for me...  Note: The macro expects the code view for the class to split to be the active VS document.
 
Here's the macro:


Imports System
Imports EnvDTE
Imports EnvDTE80
Imports System.Diagnostics
Imports System.Windows.Forms
Imports System.IO
Imports System.Text.RegularExpressions
Imports System.Collections.Generic
Public Module Components
    '~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
    ' Splits a form or user control in one source file into custom and
    ' designer partial classes.
    '~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
    Sub SplitToDesignerClass()
        Dim closeUndoContext As Boolean = False
        ' If necessary, create a new undo context
        If DTE.UndoContext.IsOpen = False Then
            closeUndoContext = True
            DTE.UndoContext.Open("SplitToPartialClass Macro", False)
        End If
        Try
            ' Get the project item name
            Dim projectItem As ProjectItem = DTE.ActiveDocument.ProjectItem
            Dim itemName As String = _
                Path.GetFileNameWithoutExtension(projectItem.Name)
            Dim fileName As String = projectItem.FileNames(1)
            ' Determine if the child designer project item already exists
            Dim designerItemName As String = _
                itemName & ".Designer"
            Dim childItems As ProjectItems = projectItem.ProjectItems
            Dim childItem As ProjectItem
            Dim designerFound As Boolean = False
            For Each childItem In childItems
                If (Path.GetFileNameWithoutExtension(childItem.Name) = _
                    designerItemName) Then
                    designerFound = True
                    Exit For
                End If
            Next
            ' If the designer file does not exist, then create it and add it as a
            ' child project item
            Dim designerFileName As String = _
                Path.Combine(Path.GetDirectoryName(fileName), _
                designerItemName & ".cs")
            If (Not designerFound) Then
                ' Create the file
                Dim designerFile As FileStream = File.Create(designerFileName)
                designerFile.Close()
                Dim designerItem As ProjectItem = _
                    projectItem.ProjectItems.AddFromFile(designerFileName)
                Dim originalElements As CodeElements = _
                    projectItem.FileCodeModel.CodeElements
                ' Add the namespace 
                Dim namesSpaceName As String
                Dim currentElement As CodeElement
                Dim origNamespace As CodeNamespace
                Dim designerNamespace As CodeNamespace
                For Each currentElement In originalElements
                    If (currentElement.Kind = vsCMElement.vsCMElementNamespace) Then
                        origNamespace = CType(currentElement, CodeNamespace)
                        designerNamespace = designerItem.FileCodeModel.AddNamespace( _
                            currentElement.FullName)
                        Exit For
                    End If
                Next
                Dim editPoint As EditPoint
                ' Find the original class 
                Dim origClass As CodeClass2
                For Each currentElement In origNamespace.Children
                    If (currentElement.Kind = vsCMElement.vsCMElementClass) Then
                        origClass = CType(currentElement, CodeClass2)
                        Exit For
                    End If
                Next
                ' Find the InitializeComponent method
                Dim childElement As CodeElement
                Dim initializeMethod As CodeFunction
                For Each childElement In origClass.Children
                    If ((childElement.Kind = vsCMElement.vsCMElementFunction) And _
                    (childElement.Name = "InitializeComponent")) Then
                        initializeMethod = CType(childElement, CodeFunction)
                        Exit For
                    End If
                Next
                If (Not initializeMethod Is Nothing) Then
                    Dim methodText As String = GetCodeElementBody(initializeMethod)
                    ' Find all component members
                    Dim memberAssignments As List(Of String) = New List(Of String)()
                    Dim componentMembers As List(Of CodeVariable) = New List(Of CodeVariable)()
                    Dim regex As Regex = New Regex("this\.(?.+?)\s+=\s+new")
                    Dim matches As MatchCollection = regex.Matches(methodText)
                    Dim match As Match
                    For Each match In matches
                        memberAssignments.Add(match.Groups("Member").Value)
                    Next
                    For Each childElement In origClass.Children
                        If (childElement.Kind = vsCMElement.vsCMElementVariable And _
                        memberAssignments.Contains(childElement.Name)) Then
                            componentMembers.Add(CType(childElement, CodeVariable))
                        End If
                    Next
                    ' Add the partial class
                    Dim namespaceBodyStart As TextPoint = _
                        designerNamespace.GetStartPoint(vsCMPart.vsCMPartBody)
                    editPoint = namespaceBodyStart.CreateEditPoint()
                    editPoint.Insert("public partial class " & _
                        origClass.Name & Environment.NewLine & "{" & _
                        Environment.NewLine & "}" & Environment.NewLine)
                    ' Get the partial class
                    Dim designerClass As CodeClass2 = _
                        CType(designerNamespace.Children.Item(1), CodeClass)
                    ' Add the initialize component method
                    Dim designFunction As CodeFunction = _
                        designerClass.AddFunction( _
                        initializeMethod.Name, _
                        initializeMethod.FunctionKind, initializeMethod.Type)
                    designFunction.Access = vsCMAccess.vsCMAccessPrivate
                    Dim methodBodyStart As TextPoint = _
                    designFunction.GetStartPoint(vsCMPart.vsCMPartBody)
                    editPoint = methodBodyStart.CreateEditPoint()
                    editPoint.Insert(methodText)
                    ' Add the component members
                    Dim componentMember As CodeVariable
                    For Each componentMember In componentMembers
                        Dim codeVariable As CodeVariable = _
                            designerClass.AddVariable( _
                            componentMember.Name, componentMember.Type)
                        codeVariable.Access = componentMember.Access
                    Next
                    ' Format the partial class
                    Dim namespaceBodyEnd As TextPoint = _
                        designerNamespace.GetEndPoint(vsCMPart.vsCMPartBody)
                    editPoint = namespaceBodyStart.CreateEditPoint()
                    editPoint.SmartFormat(namespaceBodyEnd)
                    ' Make the current class a partial class
                    Dim classNameStart As TextPoint = _
                       origClass.GetStartPoint(vsCMPart.vsCMPartHeader)
                    Dim classNameEnd As TextPoint = _
                        origClass.GetStartPoint(vsCMPart.vsCMPartBody)
                    editPoint = classNameStart.CreateEditPoint()
                    editPoint.ReplacePattern(classNameEnd, "class", "partial class")
                    ' Finally, remove the initialize method and component members 
                    ' from the original class
                    origClass.RemoveMember(initializeMethod)
                    For Each componentMember In componentMembers
                        origClass.RemoveMember(componentMember)
                    Next
                End If
            End If
        Catch ex As Exception
            MessageBox.Show(ex.Message + Environment.NewLine + ex.StackTrace)
        Finally
            ' If an error occured, then we need to make sure that the undo
            ' context is cleaned up.
            ' Otherwise, the editor can be left in a perpetual undo context
            If (closeUndoContext) Then
                DTE.UndoContext.Close()
            End If
        End Try
    End Sub
    '~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
    ' Gets a code element's body text
    '~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
    Private Function GetCodeElementBody( _
        ByRef codeElement As CodeElement) As String
        Dim startPoint As TextPoint = codeElement.GetStartPoint(vsCMPart.vsCMPartBody)
        Dim endPoint As TextPoint = codeElement.GetEndPoint(vsCMPart.vsCMPartBody)
        Dim editPoint As EditPoint = startPoint.CreateEditPoint()
        Dim text = editPoint.GetText(endPoint)
        Return text
    End Function
End Module

Originally posted at http://jamesnies.spaces.live.com/ 9/08/2006 4:15 AM

Converting VS 2003 User Control Projects to VS 2005

The Test Container for User Control Libraries in Visual Studio 2005 is awesome!  However, I noticed that when Visual Studio 2003 class library projects are converted up to 2005, they do not automatically get the new debugging functionality when they contain user controls.

After digging around in the VS program files directory I came across the Windows Control Library project template.  It can be found at Microsoft Visual Studio 8\Common7\IDE\ProjectTemplates\CSharp\Windows\1033\WindowsControlLibrary.zip.

Within the item group for the source files to compile (see below) I noticed that there is a service element.  It turns out that that the service with guid 94E38DFF-614B-4cbd-B67C-F211BB35CE8B is the service for the Test Container.  Adding the service XML element within the compilation section of your .csproj file will change it from a plain-old class library to a Windows User Control Library.  Now when the project is debugged through Visual Studio the Test Container pops up. 

Example .csproj compilation XML:

...
<ItemGroup> <Compile Include="UserControl1.cs"> <SubType>UserControl</SubType> </Compile> <Compile Include="UserControl1.Designer.cs"> <DependentUpon>UserControl1.cs</DependentUpon> </Compile> <Compile Include="Properties\AssemblyInfo.cs" /> <Service Include="{94E38DFF-614B-4cbd-B67C-F211BB35CE8B}" /> </ItemGroup> ...

Originally posted at http://jamesnies.spaces.live.com/ 07/29/2006 5:39 AM