Customized Solution & Project Explorer in .NET Using C# and Windows Forms

.NET has been in action for the past so many years. But Project and Solution files are still not familiar to most programmers. The reason behind this is Visual Studio manages the project and solution files for us. But if the project or solution contents are large then it will take a long time just to load and show the contents of them in the Solution Explorer of VS.


Usually, we just need to know what the files, references and other contents are present in a project or list of projects present in a solution. This application will allow us to see any projects or solutions contents as in the Solution Explorer view without loading the heavy process VS.NET. I designed this application using C# and Windows Forms in .NET.


Before explaining its design and functionality, I will explain a little bit about its features.


Features of this Application:

  • Loads any C# Project and allows exploration of its contents.
  • Loads any Solution and allows to see the list of the projects and its contents.
  • Built-in support for displaying XML data in both XML and Data (Grid) View.
  • Support to explore references (.dll) like an object browser in VS.
  • Fast loading compared to the Solution Explorer of VS.
  • Ability to maintain a list of recent files opened through this application. 

Steps to create:

Create a new Windows Forms Project in C# and name it SolutionExplorer.


Then place the controls as shown in the following figure:

ImageNew1.jpg

I will explain the purpose of each control on the startup form (Form1).

I used a TreeView control and ImageList to display the selected project or solution contents with images on each node.

Then I added a TabControl, in one tab I placed a RichTextBox and in another tab, a datagrid control. Later, add menuitems to MainMenu as shown in Figure. Add context Menu with the following items:

  1. Expand,
  2. Collapse,
  3. Full Expand,
  4. Grid View

And set the context menu property of the TreeView to this control.

Finally add an OpenFileDialog and tooltip control.

By using a FileDialog control, we can select any C# project file or Solution file to analyze and display its contents in a TreeView.

A ContextMenu for a TreeView is used to expand, collapse and display XML in a Grid.

I will explain what I have done in a Click of the Browse menuitem:

 

if(DialogResult.OK == dglopensoln.ShowDialog())

{

    string selectedprjfile = dglopensoln.FileName.ToString();

    txtsolnpath.Text = selectedprjfile;

    toolTip1.SetToolTip(txtsolnpath,selectedprjfile);

    MenuItem testitem = new MenuItem(selectedprjfile);

    testitem.Click += new EventHandler(testitem_Click);

    mnurecent.MenuItems.Add(testitem);

    obj1.Text = selectedprjfile;

    menuItem7_Click(sender,e);

}


Using the above code, we can browse the selected project file (.csproj) or solution (.soln) and than we are creating a new menuitem with text as the selected file path and a handler for the click of that menuitem. Then, we are internally calling the click handler of the analyze menuitem.

Here, I will explain the logic to load the Solution and its contents into the treeview control. I almost followed a similar logic to load the project also.

 

string prevselfilecontents = selfilecontents.Text;

try

{

    selfilecontents.Text = "";

    treeView1.Nodes.Clear();

    menuItem7.Enabled = false;

    TreeNode mainnode = null;

    string httpprjs = null;

    if(txtsolnpath.Text.EndsWith(".sln"))

    {

        string tmpsolndata = @"c:\tempsoln.xml";

        string solnname = Path.GetFileNameWithoutExtension(txtsolnpath.Text);

        mainnode = new TreeNode(solnname);

        treeView1.Nodes.Add(mainnode);

        if(Directory.Exists(@"C:\"+solnname))

        {

            Directory.Delete(@"C:\"+solnname,true);

        }

        Directory.CreateDirectory(@"C:\"+solnname);

        prjsdetailsinsoln = new Hashtable();

        if(!File.Exists(tmpsolndata))

        {

            File.Create(tmpsolndata);

        }

        string solncontents = "";

        StreamReader solnreader = new StreamReader(txtsolnpath.Text.Trim());

        while(solncontents != null)

        {

            solncontents = solnreader.ReadLine();

            if(solncontents != null)

            {        
                try

                {

                    if(solncontents.StartsWith("Project"))

                    {

                        string[] prjprops = solncontents.Split(',');

                        string prjdispname = prjprops[0].Substring(prjprops[0].LastIndexOf("=")+1).Replace("\"","").Trim();

                        string prjpath1   = prjprops[1].ToString().Replace("\"","").Trim();

                        prjsdetailsinsoln.Add(prjdispname,prjpath1);

                    }

                }

                catch(Exception ex)

                {

                    MessageBox.Show(ex.Message);

                }

            }

        }

        IDictionaryEnumerator enprj = prjsdetailsinsoln.GetEnumerator();

        while (enprj.MoveNext())

        {

            string strprjname = enprj.Key.ToString();

            if(enprj.Value.ToString().IndexOf("http://") == -1 && enprj.Value.ToString().EndsWith(".csproj"))

            {

                string prjpath = txtsolnpath.Text.Substring(0,txtsolnpath.Text.LastIndexOf(@"\")+1)+enprj.Value.ToString();

                if(File.Exists(prjpath))

                {

                    StreamReader prjreader = new StreamReader(prjpath);

                    string prjcontents = prjreader.ReadToEnd();

                    prjreader.Close();

                    //Format Project File into XML file...

                    tmpxmlfileforsolndata = @"C:\"+solnname+"\\"+enprj.Key.ToString()+".xml";

                    if(prjcontents.Length != 0)

                    {

                        StreamWriter solntoxmlconverter = new StreamWriter(tmpxmlfileforsolndata,false);

                        solntoxmlconverter.Write("<?xml version=\"1.0\"?>");

                        solntoxmlconverter.Write(prjcontents);

                        solntoxmlconverter.Close();

                    }

                    else

                    {

                        MessageBox.Show("Selected Project File is empty...");

                        txtsolnpath.Text = "";

                    }

                    //General project's Details....

                    TreeNode rootnode = new TreeNode();

                    rootnode.Text = strprjname;

                    mainnode.Nodes.Add(rootnode);

                    //Project's references information...

                    TreeNode referencenode = new TreeNode();

                    referencenode.Text = "References";

                    rootnode.Nodes.Add(referencenode);

                    //aspx Files Included in the Project...

                    TreeNode includedaspxfilesnode = new TreeNode();

                    includedaspxfilesnode.Text = "ASPX Files";

                    rootnode.Nodes.Add(includedaspxfilesnode);

                    //aspx.cs Files Included in the Project...

                    TreeNode includedaspxcsfilesnode = new TreeNode();

                    includedaspxcsfilesnode.Text = "ASPX.CS Files";

                    rootnode.Nodes.Add(includedaspxcsfilesnode);

                    //Class  Files Included in the Project...

                    TreeNode includedcsfilenode = new TreeNode();

                    includedcsfilenode.Text = "Class Files";

                    rootnode.Nodes.Add(includedcsfilenode);

                    //Class  Files Included in the Project...

                    TreeNode includedusercntrlfilenode = new TreeNode();

                    includedusercntrlfilenode.Text = "User Controls";

                    rootnode.Nodes.Add(includedusercntrlfilenode);

                    //Web config Files Included in the Project...

                    TreeNode includedwebconfignode = new TreeNode();

                    includedwebconfignode.Text = "Web Config Files";

                    rootnode.Nodes.Add(includedwebconfignode);

                    //Javascript Files Included in the Project...

                    TreeNode includedjsfilenode = new TreeNode();

                    includedjsfilenode.Text = "JavaScript Files";

                    rootnode.Nodes.Add(includedjsfilenode);

                    //CSS  Files Included in the Project...

                    TreeNode includedcssfilenode = new TreeNode();

                    includedcssfilenode.Text = "CSS Files";

                    rootnode.Nodes.Add(includedcssfilenode);

                    //Image  Files Included in the Project...

                    TreeNode includedimgfilenode = new TreeNode();

                    includedimgfilenode.Text = "Image Files";

                    rootnode.Nodes.Add(includedimgfilenode);

                    //XML  Files Included in the Project...

                    TreeNode includedxmlfilenode = new TreeNode();

                    includedxmlfilenode.Text = "XML Files";

                    rootnode.Nodes.Add(includedxmlfilenode);

                    //XSL  Files Included in the Project...

                    TreeNode includedxslfilenode = new TreeNode();

                    includedxslfilenode.Text = "XSL Files";

                    rootnode.Nodes.Add(includedxslfilenode);

                    //Other  Files present in unknown format Included in the Project...

                    TreeNode includedunknownfilenode = new TreeNode();

                    includedunknownfilenode.Text = "Unknown Files";

                    rootnode.Nodes.Add(includedunknownfilenode);

                    //To Load all items of the Project into the Treeview...

                    XPathDocument prjfile = new XPathDocument(tmpxmlfileforsolndata);

                    XPathNavigator nav = prjfile.CreateNavigator();

                    //TO Load all references of the selected Project...

                    XPathNodeIterator referenceiterator = nav.Select

                    (@"/VisualStudioProject/CSHARP/Build/References/Reference");

                    while(referenceiterator.MoveNext())

                    {

                        string reffile = referenceiterator.Current.GetAttribute("Name","").ToString();

                        TreeNode refnode = new TreeNode(reffile);

                        if(reffile.StartsWith("System"))

                        {

                            refnode.ForeColor  = Color.Green;

                        }

                        else

                        {

                            refnode.ForeColor = Color.Orange;

                        }

                        referencenode.Nodes.Add(refnode);

                    }

                    //TO Load all ASPX of the selected Project...

                    XPathNodeIterator aspxfileiterator = nav.Select(@"/VisualStudioProject/CSHARP/Files/Include/File");

                    LoadselectedDetails("aspx",includedaspxfilesnode,aspxfileiterator);

                    RemoveUnwatedNodes(includedaspxfilesnode);

                    //TO Load all ASPX.CS(Code-Behind) of the selected Project...

                    XPathNodeIterator aspxcsfileiterator = nav.Select(@"/VisualStudioProject/CSHARP/Files/Include/File");

                    LoadselectedDetails("aspx.cs",includedaspxcsfilesnode,aspxcsfileiterator);

                    RemoveUnwatedNodes(includedaspxcsfilesnode);

                    //TO Load all Assembly Config of the selected Project...

                    XPathNodeIterator webconfigfileiterator = nav.Select(@"/VisualStudioProject/CSHARP/Files/Include/File");

                    LoadselectedDetails("config",includedwebconfignode,webconfigfileiterator);

                    RemoveUnwatedNodes(includedwebconfignode);

                    //TO Load all Assembly Config of the selected Project...

                    XPathNodeIterator jsfileiterator = nav.Select(@"/VisualStudioProject/CSHARP/Files/Include/File");

                    LoadselectedDetails("js",includedjsfilenode,jsfileiterator);

                    RemoveUnwatedNodes(includedjsfilenode);

                    //TO Load all CSS files of the selected Project...

                    XPathNodeIterator cssfileiterator = nav.Select(@"/VisualStudioProject/CSHARP/Files/Include/File");

                    LoadselectedDetails("css",includedcssfilenode,cssfileiterator);

                    RemoveUnwatedNodes(includedcssfilenode);

                    //TO Load all Image files of the selected Project...

                    XPathNodeIterator imgfileiterator = nav.Select(@"/VisualStudioProject/CSHARP/Files/Include/File");

                    LoadselectedDetails("gif",includedimgfilenode,imgfileiterator);

                    RemoveUnwatedNodes(includedimgfilenode);

                    //TO Load all XML files of the selected Project...

                    XPathNodeIterator xmlfileiterator = nav.Select(@"/VisualStudioProject/CSHARP/Files/Include/File");

                    LoadselectedDetails("xml",includedxmlfilenode,xmlfileiterator);

                    RemoveUnwatedNodes(includedxmlfilenode);

                    //TO Load all XSL files of the selected Project...

                    XPathNodeIterator xslfileiterator = nav.Select(@"/VisualStudioProject/CSHARP/Files/Include/File");

                    LoadselectedDetails("xsl",includedxslfilenode,xslfileiterator);

                    RemoveUnwatedNodes(includedxslfilenode);

                    //TO Load all Class files of the selected Project...

                    XPathNodeIterator csfileiterator = nav.Select

                    (@"/VisualStudioProject/CSHARP/Files/Include/File");                      

                    LoadselectedDetails("cs",includedcsfilenode,csfileiterator);

                    RemoveUnwatedNodes(includedcsfilenode);

                    //TO Load all User Controls files of the selected Project...

                    XPathNodeIterator usercntrlsfileiterator = nav.Select(@"/VisualStudioProject/CSHARP/Files/Include/File");

                    LoadselectedDetails("ascx.cs",includedusercntrlfilenode,usercntrlsfileiterator);

                    RemoveUnwatedNodes(includedusercntrlfilenode);

                    //TO Load all Unknown files of the selected Project...

                    XPathNodeIterator unknownsfileiterator = nav.Select(@"/VisualStudioProject/CSHARP/Files/Include/File");

                    while(unknownsfileiterator.MoveNext())

                    {

                        string filename = unknownsfileiterator.Current.GetAttribute("RelPath","").ToString().ToLower();

                        if(!filename.EndsWith(".aspx") && !filename.EndsWith(".aspx.cs") && !filename.EndsWith(".cs") && !

                            filename.EndsWith(".ascx.cs") && !filename.EndsWith(".js") &&  !filename.EndsWith(".xml") && !

                            filename.EndsWith(".xsl") && !filename.EndsWith(".css") &&  !filename.EndsWith(".gif") &&  !

                            filename.EndsWith (".config"))

                        {

                            includedunknownfilenode.Nodes.Add(filename.Substring(filename.LastIndexOf(@"\")+1));

                        }

                    }

                    RemoveUnwatedNodes(includedunknownfilenode);

                }

                else

                {

                    TreeNode httpprjnode = new TreeNode(enprj.Value.ToString());

                    httpprjnode.ForeColor = Color.Red;

                    mainnode.Nodes.Add(httpprjnode);

                }

            }

            if(enprj.Value.ToString().IndexOf("http://") != -1)

            {

                httpprjs+=enprj.Value.ToString()+"#";

            }

        }

        foreach(string httpprj in httpprjs.Split(new char[]{'#'}))

        {

            if(httpprj.Trim() != "")

            {

                TreeNode node = new TreeNode(httpprj.Trim());

                mainnode.Nodes.Add(node);

            }

        }

    }

}

catch(Exception ex)

{

    MessageBox.Show(ex.Message);

    selfilecontents.Text = prevselfilecontents;

}

finally

{

    menuItem7.Enabled = true;

}

 

In this click event, we are first determining whether the selected file is a project file or solution file by using its extension. Since the Solution file will not be in XML format. So, I am using some text search patterns to get project details present in the solution file.

I am looping foreach project present in the solution, creating a temporary XML file to store project contents and then create a structure in a treeview with nodes named aspx, user controls like that we can add any number of nodes based on types of files present in your project. I am using XPath to navigate through project file present in XML format.

I am using a Xpath statement as @"/VisualStudioProject/CSHARP/Build/References/Reference" to get all project's references and @"/VisualStudioProject/CSHARP/Files/Include/File" to get all files included in project.

Based on their extension, I am adding each item to a specific node like adding .aspx files to ASPX Node. Then, I am removing nodes which don't have any items in it. Finally, I am formatting a treeview for a good look.

Then we are required to display the contents of selected files in the treeview. So, I am using the AfterSelect event of treeview, to display its contents.

Here, I am going to get the absolute physical path of the selected file using a HashTable which is created at that time of creating nodes foreach file type present in the project or solution. I am getting an absolute path of the selected file using the selected item's text and searching its path attribute in its corresponding project file. After getting its path, I am reading its entire contents and displaying in a richtextbox.

Then add another form to get internal details of the selected reference. And create a UI as shown in the figure:

Img2.jpg

Here, I placed a menu, FileDialog and tooltip followed by a treeview to display the selected reference details.

In Load Click, I am getting the selected reference path and using reflection, we are displaying its namespaces, classes, events and methods in the treeview.

The final output will be like this:


Img3.jpg

Img4.jpg

We can still enhance this application with better UI and support of all files types and multiple language project file exploration.

I am attaching code for further reference. I hope this code will be useful for all.

Next Recommended Readings