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:
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 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:
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 files types and multiple language project file exploration.
I am attaching code for further reference. I hope this code will be useful for all.