.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.
Create a new Windows Forms Project in C# and name it SolutionExplorer.
Then place the controls as shown in the following figure:
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:
-
Expand,
-
Collapse,
-
Full Expand,
-
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 then 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();
-
- 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 = "";
- }
-
- TreeNode rootnode = new TreeNode();
- rootnode.Text = strprjname;
- mainnode.Nodes.Add(rootnode);
-
- TreeNode referencenode = new TreeNode();
- referencenode.Text = "References";
- rootnode.Nodes.Add(referencenode);
-
- TreeNode includedaspxfilesnode = new TreeNode();
- includedaspxfilesnode.Text = "ASPX Files";
- rootnode.Nodes.Add(includedaspxfilesnode);
-
- TreeNode includedaspxcsfilesnode = new TreeNode();
- includedaspxcsfilesnode.Text = "ASPX.CS Files";
- rootnode.Nodes.Add(includedaspxcsfilesnode);
-
- TreeNode includedcsfilenode = new TreeNode();
- includedcsfilenode.Text = "Class Files";
- rootnode.Nodes.Add(includedcsfilenode);
-
- TreeNode includedusercntrlfilenode = new TreeNode();
- includedusercntrlfilenode.Text = "User Controls";
- rootnode.Nodes.Add(includedusercntrlfilenode);
-
- TreeNode includedwebconfignode = new TreeNode();
- includedwebconfignode.Text = "Web Config Files";
- rootnode.Nodes.Add(includedwebconfignode);
-
- TreeNode includedjsfilenode = new TreeNode();
- includedjsfilenode.Text = "JavaScript Files";
- rootnode.Nodes.Add(includedjsfilenode);
-
- TreeNode includedcssfilenode = new TreeNode();
- includedcssfilenode.Text = "CSS Files";
- rootnode.Nodes.Add(includedcssfilenode);
-
- TreeNode includedimgfilenode = new TreeNode();
- includedimgfilenode.Text = "Image Files";
- rootnode.Nodes.Add(includedimgfilenode);
-
- TreeNode includedxmlfilenode = new TreeNode();
- includedxmlfilenode.Text = "XML Files";
- rootnode.Nodes.Add(includedxmlfilenode);
-
- TreeNode includedxslfilenode = new TreeNode();
- includedxslfilenode.Text = "XSL Files";
- rootnode.Nodes.Add(includedxslfilenode);
-
- TreeNode includedunknownfilenode = new TreeNode();
- includedunknownfilenode.Text = "Unknown Files";
- rootnode.Nodes.Add(includedunknownfilenode);
-
- XPathDocument prjfile = new XPathDocument(tmpxmlfileforsolndata);
- XPathNavigator nav = prjfile.CreateNavigator();
-
- 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);
- }
-
- XPathNodeIterator aspxfileiterator = nav.Select(@"/VisualStudioProject/CSHARP/Files/Include/File");
- LoadselectedDetails("aspx",includedaspxfilesnode,aspxfileiterator);
- RemoveUnwatedNodes(includedaspxfilesnode);
-
- XPathNodeIterator aspxcsfileiterator = nav.Select(@"/VisualStudioProject/CSHARP/Files/Include/File");
- LoadselectedDetails("aspx.cs",includedaspxcsfilesnode,aspxcsfileiterator);
- RemoveUnwatedNodes(includedaspxcsfilesnode);
-
- XPathNodeIterator webconfigfileiterator = nav.Select(@"/VisualStudioProject/CSHARP/Files/Include/File");
- LoadselectedDetails("config",includedwebconfignode,webconfigfileiterator);
- RemoveUnwatedNodes(includedwebconfignode);
-
- XPathNodeIterator jsfileiterator = nav.Select(@"/VisualStudioProject/CSHARP/Files/Include/File");
- LoadselectedDetails("js",includedjsfilenode,jsfileiterator);
- RemoveUnwatedNodes(includedjsfilenode);
-
- XPathNodeIterator cssfileiterator = nav.Select(@"/VisualStudioProject/CSHARP/Files/Include/File");
- LoadselectedDetails("css",includedcssfilenode,cssfileiterator);
- RemoveUnwatedNodes(includedcssfilenode);
-
- XPathNodeIterator imgfileiterator = nav.Select(@"/VisualStudioProject/CSHARP/Files/Include/File");
- LoadselectedDetails("gif",includedimgfilenode,imgfileiterator);
- RemoveUnwatedNodes(includedimgfilenode);
-
- XPathNodeIterator xmlfileiterator = nav.Select(@"/VisualStudioProject/CSHARP/Files/Include/File");
- LoadselectedDetails("xml",includedxmlfilenode,xmlfileiterator);
- RemoveUnwatedNodes(includedxmlfilenode);
-
- XPathNodeIterator xslfileiterator = nav.Select(@"/VisualStudioProject/CSHARP/Files/Include/File");
- LoadselectedDetails("xsl",includedxslfilenode,xslfileiterator);
- RemoveUnwatedNodes(includedxslfilenode);
-
- XPathNodeIterator csfileiterator = nav.Select
- (@"/VisualStudioProject/CSHARP/Files/Include/File");
- LoadselectedDetails("cs",includedcsfilenode,csfileiterator);
- RemoveUnwatedNodes(includedcsfilenode);
-
- XPathNodeIterator usercntrlsfileiterator = nav.Select(@"/VisualStudioProject/CSHARP/Files/Include/File");
- LoadselectedDetails("ascx.cs",includedusercntrlfilenode,usercntrlsfileiterator);
- RemoveUnwatedNodes(includedusercntrlfilenode);
-
- 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 a 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 that don't have any items in them. 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 it in a richtextbox.
Then add another form to get internal details of the selected reference. And create a UI as shown in the figure:
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:
We can still enhance this application with better UI and support of all file types and multiple language project file exploration.
I am attaching the code for further reference. I hope this code will be useful for all.