Click here to Skip to main content
15,884,472 members
Articles / Programming Languages / C#

The Code Project Browser Add-in for Visual Studio 2005 and 2008

Rate me:
Please Sign up or sign in to vote.
4.90/5 (108 votes)
27 Mar 2008CPOL9 min read 316.2K   4.9K   296  
An add-in for browsing, downloading and managing CodeProject samples directly in Visual Studio
//  Copyright (c) 2007, SlickEdit, Inc
//  Email:  info@slickedit.com
//  All rights reserved.
//
//  Redistribution and use in source and binary forms, with or without modification, 
//  are permitted provided that the following conditions are met:
//
//  Redistributions of source code must retain the above copyright notice, 
//  this list of conditions and the following disclaimer. 
//  Redistributions in binary form must reproduce the above copyright notice, 
//  this list of conditions and the following disclaimer in the documentation 
//  and/or other materials provided with the distribution. 
//
//  THIS CODE AND INFORMATION IS PROVIDED "AS IS" WITHOUT WARRANTY OF ANY
//  KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE
//  IMPLIED WARRANTIES OF MERCHANTABILITY AND/OR FITNESS FOR A PARTICULAR
//  PURPOSE. IT CAN BE DISTRIBUTED FREE OF CHARGE AS LONG AS THIS HEADER 
//  REMAINS UNCHANGED.

using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Drawing;
using System.Data;
using System.Text;
using System.Windows.Forms;
using System.IO;
using System.Runtime.InteropServices;
using System.Diagnostics;
using System.Xml.Serialization;

namespace CPBrowser
{
    /// <summary>
    /// This user control implements the "Downloaded Projects" sidebar control. It
    /// shows the directories in the download root directory, and all projects 
    /// and solution files contained in each respectively.  This is where the user
    /// can manage their downloads.
    /// </summary>
    public partial class ProjectTreeView : UserControl
    {
        // OnClose declaration
        public delegate void ClosedDelegate(object sender, EventArgs e);
        public event ClosedDelegate OnClosed;

        // the reference back to the hosting CPBrowserCtl object
        private CPBrowserCtl m_cpBrowserCtl = null;

        /// <summary>
        /// Constructor.
        /// </summary>
        public ProjectTreeView()
        {
            InitializeComponent();
        }

        /// <summary>
        /// Initializes the instance of this control.  The tree control is also 
        /// populated with the user's downloaded project information.
        /// </summary>
        /// <param name="cpBrowserCtl">The host for this control.</param>
        public void Initialize(CPBrowserCtl cpBrowserCtl)
        {
            m_cpBrowserCtl = cpBrowserCtl;
            PopulateTree();
        }

        /// <summary>
        /// The download root directory is traversed and each sub-directory is added to 
        /// the tree.
        /// </summary>
        private void PopulateTree()
        {
            try
            {
                // disable rendering of the tree
                ProjectTreeCtl.BeginUpdate();
                // clear the tree
                ProjectTreeCtl.Nodes.Clear();
                // load any serialized project info
                LoadProjects();
                // get all sub-directories beneath the download root
                string[] directoryNames = Directory.GetDirectories(m_cpBrowserCtl.ProjectLoader.DownloadsRoot);
                foreach (string projectDir in directoryNames)
                {
                    ProjectEntry projectEntry = new ProjectEntry(projectDir, "");
                    AddProjectToTree(projectEntry);
                }
                // initially, collapse everything
                ProjectTreeCtl.CollapseAll();
            }
            catch { }
            finally
            {
                // enable rendering of the tree
                ProjectTreeCtl.EndUpdate();
            }
        }

        /// <summary>
        /// Deserializes the "projects.xml" file in the download root directory, if it 
        /// exists.
        /// </summary>
        private void LoadProjects()
        {
            FileStream fs = null;
            StreamReader sr = null;

            try
            {
                // create the XML serializer
                XmlSerializer xmlSerializer = new XmlSerializer(typeof(List<ProjectEntry>));
                string fileName = Path.Combine(m_cpBrowserCtl.ProjectLoader.DownloadsRoot, "projects.xml");
                // make sure the file exists
                if (File.Exists(fileName) == false)
                    return;
                // create a Stream Reader to read the file contents
                fs = new FileStream(fileName, System.IO.FileMode.Open, System.IO.FileAccess.Read);
                sr = new StreamReader(fs);
                // now deserialize it into a populated object
                List<ProjectEntry> projects = (List<ProjectEntry>)xmlSerializer.Deserialize(sr);
                foreach (ProjectEntry projectEntry in projects)
                {
                    AddProjectToTree(projectEntry);
                }
            }
            catch { }
            finally
            {
                // free the resources
                if (sr != null)
                    sr.Close();
                if (fs != null)
                    fs.Close();
            }
        }

        /// <summary>
        /// Serializes the list of ProjectEntry objects to the "projects.xml" file in 
        /// the download root directory.
        /// </summary>
        private void SaveProjects()
        {
            StreamWriter sw = null;

            try
            {
                // create the XML serializer
                XmlSerializer xmlSerializer = new XmlSerializer(typeof(List<ProjectEntry>));
                List<ProjectEntry> projects = new List<ProjectEntry>();
                string fileName = Path.Combine(m_cpBrowserCtl.ProjectLoader.DownloadsRoot, "projects.xml");
                // create the list of project entries
                foreach (TreeNode node in ProjectTreeCtl.Nodes)
                {
                    // all top level nodes should be project entry nodes
                    if (node.Tag is ProjectEntry)
                        projects.Add((ProjectEntry)node.Tag);
                }
                // create the StreamWriter object
                sw = new StreamWriter(fileName);
                // serialize the object
                xmlSerializer.Serialize(sw, projects);
            }
            catch { }
            finally
            {
                // free the resources
                if (sw != null)
                    sw.Close();
            }
        }

        /// <summary>
        /// Takes a project directory and traverses it and its subdirectories for  
        /// project or solution files.  The root directory is added to the tree view and
        /// any found project or solution files are added as children.
        /// </summary>
        /// <param name="projectDir">The directory to add to the tree.</param>
        public void AddProjectToTree(ProjectEntry project)
        {
            // make sure the project folder exists
            if (Directory.Exists(project.RootFolder) == false)
                return;

            // get the innermost folder name
            string name = Path.GetFileName(project.RootFolder);
            TreeNode projectFolderNode = null;
            // first, see if the project directory already exists in the tree.  If so, just reuse
            // that node and repopulate it
            foreach (TreeNode node in ProjectTreeCtl.Nodes)
            {
                if (String.Compare(node.Text, name, true) == 0)
                {
                    // we've found a matching project folder node
                    projectFolderNode = node;
                    // clean out it's child nodes, we'll repopulate them
                    projectFolderNode.Nodes.Clear();
                    break;
                }
            }
            // if we didn't find a project folder node, then create one
            if (projectFolderNode == null)
            {
                // add an entry to the tree for this
                projectFolderNode = new TreeNode(name);
                projectFolderNode.ImageKey = "Folder";
                projectFolderNode.SelectedImageKey = "Folder";
                // store the project entry object in the tag
                projectFolderNode.Tag = project;
                ProjectTreeCtl.Nodes.Add(projectFolderNode);
            }
            // now find any solutions in that directory (or subdirectories)
            List<string> solutionFiles = new List<string>();
            List<string> projectFiles = new List<string>();
            List<string> exeFiles = new List<string>();
            // now find any solutions or projects in that directory (or subdirectories)
            m_cpBrowserCtl.ProjectLoader.GetSignificantFiles(project.RootFolder, ref solutionFiles, ref projectFiles, ref exeFiles);
            foreach (string fileName in solutionFiles)
            {
                name = Path.GetFileName(fileName);
                TreeNode projectSolutionNode = new TreeNode(name);
                projectSolutionNode.ImageKey = GetImageKeyForFile(fileName);
                projectSolutionNode.SelectedImageKey = GetImageKeyForFile(fileName);
                // store the file name in the tag
                projectSolutionNode.Tag = fileName;
                projectFolderNode.Nodes.Add(projectSolutionNode);
            }
            foreach (string fileName in projectFiles)
            {
                name = Path.GetFileName(fileName);
                TreeNode projectSolutionNode = new TreeNode(name);
                projectSolutionNode.ImageKey = GetImageKeyForFile(fileName);
                projectSolutionNode.SelectedImageKey = GetImageKeyForFile(fileName);
                // store the file name in the tag
                projectSolutionNode.Tag = fileName;
                projectFolderNode.Nodes.Add(projectSolutionNode);
            }
            foreach (string fileName in exeFiles)
            {
                name = Path.GetFileName(fileName);
                TreeNode exeSolutionNode = new TreeNode(name);
                exeSolutionNode.ImageKey = GetImageKeyForFile(fileName);
                exeSolutionNode.SelectedImageKey = GetImageKeyForFile(fileName);
                // store the exe name in the tag
                exeSolutionNode.Tag = fileName;
                projectFolderNode.Nodes.Add(exeSolutionNode);
            }
            // select and expand the directory node
            ProjectTreeCtl.SelectedNode = projectFolderNode;
            projectFolderNode.Expand();
            // save the projects
            SaveProjects();
        }

        /// <summary>
        /// gets the extension of the file and determines if the icon for that extension
        /// has been looked up yet.  If so, then that image is returned.  If not, then 
        /// the Windows shell is queried for the icon for that file type.  That icon is 
        /// added to the image list and is returned to the caller.
        /// </summary>
        /// <param name="filename">The file name to get the icon for.</param>
        /// <returns>The image associated with that file type.</returns>
        private string GetImageKeyForFile(string filename)
        {
            try
            {
                // if the image list already contains the icon for this extension, just return it
                string extension = Path.GetExtension(filename);
                if (ImageListCtl.Images.ContainsKey(extension) == false)
                {
                    Win32.SHFILEINFO infoSmall = new Win32.SHFILEINFO(true);
                    int cbFileInfo = Marshal.SizeOf(infoSmall);
                    Win32.SHGFI flagsSmall = Win32.SHGFI.Icon | Win32.SHGFI.SmallIcon | Win32.SHGFI.UseFileAttributes;
                    int smallOK = Win32.SHGetFileInfo(filename, 256, out infoSmall, (uint)cbFileInfo, flagsSmall);
                    if (smallOK == 1)
                    {
                        Icon icoSmall = System.Drawing.Icon.FromHandle(infoSmall.hIcon);
                        ImageListCtl.Images.Add(extension, icoSmall);
                    }
                }
                return extension;
            }
            catch
            {
                // if we run into a problem getting the icon, just return the key for the generic one
                return "File";
            }
        }

        /// <summary>
        /// Event handler to detect delete key presses.  If the user hits delete and a 
        /// directory node is selected, then the directory is deleted.  If a file is 
        /// selected, then that file is deleted.
        /// </summary>
        private void ProjectTreeCtl_KeyUp(object sender, KeyEventArgs e)
        {
            // check for a delete key
            if (e.KeyCode == Keys.Delete)
                DeleteSelectedNode();
        }

        /// <summary>
        /// Deletes the currently selected node and any files in the corresponding 
        /// directory under My Code Project Downloads
        /// </summary>
        private void DeleteSelectedNode()
        {
            // get the selected node
            TreeNode selectedNode = ProjectTreeCtl.SelectedNode;
            if (selectedNode == null)
                return;

            string path = "";
            // check if this is a project entry or not
            if (selectedNode.Tag is ProjectEntry)
            {
                path = ((ProjectEntry)selectedNode.Tag).RootFolder;
                // ask if the user wants to remove the directory
                string msg = "Are you sure you want to delete the '" + selectedNode.Text + "' directory?";
                DialogResult dr = MessageBox.Show(msg, "Code Project Browser", MessageBoxButtons.YesNo, MessageBoxIcon.Question);
                if (dr == DialogResult.Yes)
                {
                    try
                    {
                        // delete the directory
                        Directory.Delete(path, true);
                        // remove the node from the tree
                        selectedNode.Remove();
                    }
                    catch (Exception ex)
                    {
                        msg = "Unable to delete the directory: " + ex.Message;
                        MessageBox.Show(msg, "Code Project Browser", MessageBoxButtons.OK, MessageBoxIcon.Error);
                    }
                }
            }
            else
            {
                path = (string)selectedNode.Tag;
                // ask if the user wants to remove the file
                string msg = "Are you sure you want to delete this file?";
                DialogResult dr = MessageBox.Show(msg, "Code Project Browser", MessageBoxButtons.YesNo, MessageBoxIcon.Question);
                if (dr == DialogResult.Yes)
                {
                    try
                    {
                        // delete the directory
                        File.Delete(path);
                        // remove the node from the tree
                        selectedNode.Remove();
                    }
                    catch (Exception ex)
                    {
                        msg = "Unable to delete the file: " + ex.Message;
                        MessageBox.Show(msg, "Code Project Browser", MessageBoxButtons.YesNo, MessageBoxIcon.Question);
                    }
                }
            }
        }

        /// <summary>
        /// Event handler for when the user clicks a directory, project or solution node
        /// in the tree, to load it in Visual Studio.
        /// </summary>
        private void ProjectTreeCtl_NodeMouseDoubleClick(object sender, TreeNodeMouseClickEventArgs e)
        {
            // get the full path
            string path = "";
            // check if this is a project entry or not
            if (e.Node.Tag is ProjectEntry)
            {
                path = ((ProjectEntry)e.Node.Tag).RootFolder;
                m_cpBrowserCtl.ProjectLoader.LoadProjectOrSolutionFromDirectory(path);
            }
            else
            {
                path = (string)e.Node.Tag;
                // if it's an executable, then launch it
                if (m_cpBrowserCtl.ProjectLoader.IsExecutableFile(path) == true)
                {
                    // try/catch here, Process.Start requires full trust, which the user might not have
                    try
                    {
                        Process.Start(path);
                    }
                    catch (Exception ex)
                    {
                        MessageBox.Show("Error running program: " + ex.Message, "Code Project Browser", MessageBoxButtons.OK, MessageBoxIcon.Exclamation);
                    }
                }
                else
                {
                    m_cpBrowserCtl.ProjectLoader.OpenProjectOrSolution(path);
                }
            }
        }

        /// <summary>
        /// Event handler for the CloseButton's Click event, which causes this sidebar 
        /// pane object to raise the OnClose event.
        /// </summary>
        private void CloseButtonCtl_Click(object sender, EventArgs e)
        {
            // raise the OnClose event
            if (OnClosed != null)
                OnClosed(this, new EventArgs());
        }

        /// <summary>
        /// Event handler for the menu to delete an articles downloaded files.
        /// </summary>
        private void MenuDelete_Click(object sender, EventArgs e)
        {
            DeleteSelectedNode();
        }

        private void ContextMenuCtl_Opening(object sender, CancelEventArgs e)
        {
            // get the selected node
            TreeNode selectedNode = ProjectTreeCtl.SelectedNode;
            if (selectedNode == null)
                return;

            // check to see if the selected node has an article URL associated with it
            bool menusVisible = false;
            bool articleMenusEnabled = false;
            if (selectedNode.Tag is ProjectEntry)
            {
                ProjectEntry projectEntry = (ProjectEntry)selectedNode.Tag;
                articleMenusEnabled = (projectEntry.Url != "");
                menusVisible = true;
            }
            // set the menu visibility
            MenuGoToArticle.Visible = menusVisible;
            MenuGoToArticle.Enabled = articleMenusEnabled;
            MenuOpenSeperateBrowser.Visible = menusVisible;
            MenuOpenSeperateBrowser.Enabled = articleMenusEnabled;
        }

        /// <summary>
        /// This handler needs to be implemented to capture the selected node when right
        /// clicking invokes the context menu.  When the right click occurrs, the 
        /// previously selected node is still reported.  This function sets the selected
        /// node when the right click happens.
        /// </summary>
        private void ProjectTreeCtl_MouseDown(object sender, MouseEventArgs e)
        {
            if (e.Button == MouseButtons.Right)
            {
                TreeNode clickedNode = ProjectTreeCtl.GetNodeAt(e.Location);
                if (clickedNode != null)
                    ProjectTreeCtl.SelectedNode = clickedNode;
            }

        }

        /// <summary>
        /// Event handler to browse the project's article
        /// </summary>
        private void MenuGoToArticle_Click(object sender, EventArgs e)
        {
            // get the selected node
            TreeNode selectedNode = ProjectTreeCtl.SelectedNode;
            if (selectedNode == null)
                return;

            // check to see if the selected node has an article URL associated with it
            if (selectedNode.Tag is ProjectEntry)
            {
                ProjectEntry projectEntry = (ProjectEntry)selectedNode.Tag;
                if (projectEntry.Url != "")
                    m_cpBrowserCtl.WebBrowserCtl.Navigate(projectEntry.Url);
            }
        }

        /// <summary>
        /// Event handler to browse the project directory
        /// </summary>
        private void MenuBrowseDirectory_Click(object sender, EventArgs e)
        {
            // get the selected node
            TreeNode selectedNode = ProjectTreeCtl.SelectedNode;
            if (selectedNode == null)
                return;

            string path = "";
            // check to see if the selected node has an article URL associated with it
            if (selectedNode.Tag is ProjectEntry)
                path = ((ProjectEntry)selectedNode.Tag).RootFolder;
            else
                path = Path.GetDirectoryName((string)selectedNode.Tag);
            // spawn the file explorer to that folder
            if (Directory.Exists(path) == true)
                Process.Start(path);
        }

        /// <summary>
        /// Event handler to open the article's page in a seperate browser
        /// </summary>
        private void MenuOpenSeperateBrowser_Click(object sender, EventArgs e)
        {
            // get the selected node
            TreeNode selectedNode = ProjectTreeCtl.SelectedNode;
            if (selectedNode == null)
                return;

            // check to see if the selected node has an article URL associated with it
            if (selectedNode.Tag is ProjectEntry)
            {
                ProjectEntry projectEntry = (ProjectEntry)selectedNode.Tag;
                if (projectEntry.Url != "")
                {
                    // spawn the default browser to that URL
                    Process.Start(projectEntry.Url);
                }
            }
        }

    }
}

By viewing downloads associated with this article you agree to the Terms of Service and the article's licence.

If a file you wish to view isn't highlighted, and is a text file (not binary), please let us know and we'll add colourisation support for it.

License

This article, along with any associated source code and files, is licensed under The Code Project Open License (CPOL)


Written By
Web Developer
United States United States
SlickEdit Inc. provides software developers with multi-language development tools and the most advanced code editors available. Power programmers, from Fortune 500 companies to individuals, have chosen SlickEdit as their development tool of choice for over 19 years. Proven on Windows, Linux, UNIX, and Mac OS X platforms, SlickEdit products enable even the most accomplished developers to write more code faster, and more accurately. For more information about SlickEdit and free trial downloads, please visit http://www.slickedit.com.
This is a Organisation

1 members

Comments and Discussions