locked
An VS2010 Extension vor Source Control Explorer RRS feed

  • Question

  • Hello,

    I want to write an Extension for VS2010.

    My plan is to filter the entries in the folder window based on the mapped folders in the currently selected workspace.

    If something like this does already exist please tell me, if not I would be grateful if someone can show me the right point to start.

    An example where only project folders are shown that starts with the letter 'A' would be great, so that I can modify that to my needs.

    Regards

    Michael

    • Moved by CoolDadTx Monday, May 14, 2012 1:48 PM Extensibility related (From:Visual C# IDE)
    Monday, May 14, 2012 7:29 AM

Answers

  • I found a solution

    Thanks

    Friday, May 25, 2012 7:54 AM
  • And here is the source code for all who want's to add that functionally to or improve it.

    using System;
    using System.Collections.Generic;
    using System.Linq;
    using System.Reflection;
    using System.Threading;
    using System.Threading.Tasks;
    using System.Windows.Forms;
    using EnvDTE80;
    using Extensibility;
    using Microsoft.TeamFoundation.VersionControl.Client;
    using Microsoft.TeamFoundation.VersionControl.Controls;
    using Microsoft.VisualStudio.TeamFoundation.VersionControl;
    
    namespace TFSSControlExplorerAddin
    {
    	/// <summary>
    	/// 	The object for implementing an Add-in.
    	/// </summary>
    	/// <seealso class='IDTExtensibility2' />
    	public class Connect : IDTExtensibility2 //, IDTCommandTarget
    	{
    		private DTE2 _applicationObject;
    		private object _exlporerScc;
    		private VersionControlExplorerExt _explorer;
    		private TreeView _treeViewHatterasFolders;
    		#region IDTExtensibility2 Members
    		/// <summary>
    		/// 	Implements the OnAddInsUpdate method of the IDTExtensibility2 interface. Receives notification when the collection of Add-ins has changed.
    		/// </summary>
    		/// <param name='custom'> Array of parameters that are host application specific. </param>
    		/// <seealso class='IDTExtensibility2' />
    		public void OnAddInsUpdate(ref Array custom)
    		{
    		}
    
    		/// <summary>
    		/// 	Implements the OnBeginShutdown method of the IDTExtensibility2 interface. Receives notification that the host application is being unloaded.
    		/// </summary>
    		/// <param name='custom'> Array of parameters that are host application specific. </param>
    		/// <seealso class='IDTExtensibility2' />
    		public void OnBeginShutdown(ref Array custom)
    		{
    		}
    
    		/// <summary>
    		/// 	Implements the OnConnection method of the IDTExtensibility2 interface. Receives notification that the Add-in is being loaded.
    		/// </summary>
    		/// <param name='application'> Root object of the host application. </param>
    		/// <param name='connectMode'> Describes how the Add-in is being loaded. </param>
    		/// <param name='addInInst'> Object representing this Add-in. </param>
    		/// <param name="custom"> Array of parameters that are host application specific. </param>
    		/// <seealso class='IDTExtensibility2' />
    		public void OnConnection(object application, ext_ConnectMode connectMode, object addInInst, ref Array custom)
    		{
    			try
    			{
    				_applicationObject = (DTE2) application;
    				switch (connectMode)
    				{
    					case ext_ConnectMode.ext_cm_UISetup:
    
    						// Do nothing for this add-in with temporary user interface
    						break;
    
    					case ext_ConnectMode.ext_cm_Startup:
    
    						// The add-in was marked to load on startup
    						// Do nothing at this point because the IDE may not be fully initialized
    						// Visual Studio will call OnStartupComplete when fully initialized
    						break;
    
    					case ext_ConnectMode.ext_cm_AfterStartup:
    
    						// The add-in was loaded by hand after startup using the Add-In Manager
    						// Initialize it in the same way that when is loaded on startup
    						AddTemporaryUI();
    						break;
    				}
    			}
    			catch (Exception ex)
    			{
    				MessageBox.Show(ex.ToString());
    			}
    		}
    
    		/// <summary>
    		/// 	Implements the OnDisconnection method of the IDTExtensibility2 interface. Receives notification that the Add-in is being unloaded.
    		/// </summary>
    		/// <param name='disconnectMode'> Describes how the Add-in is being unloaded. </param>
    		/// <param name='custom'> Array of parameters that are host application specific. </param>
    		/// <seealso class='IDTExtensibility2' />
    		public void OnDisconnection(ext_DisconnectMode disconnectMode, ref Array custom)
    		{
    			try
    			{
    				switch (disconnectMode)
    				{
    					case ext_DisconnectMode.ext_dm_HostShutdown:
    					case ext_DisconnectMode.ext_dm_UserClosed:
    						break;
    				}
    			}
    			catch (Exception e)
    			{
    				MessageBox.Show(e.ToString());
    			}
    		}
    
    		/// <summary>
    		/// 	Implements the OnStartupComplete method of the IDTExtensibility2 interface. Receives notification that the host application has completed loading.
    		/// </summary>
    		/// <param name='custom'> Array of parameters that are host application specific. </param>
    		/// <seealso class='IDTExtensibility2' />
    		public void OnStartupComplete(ref Array custom)
    		{
    			AddTemporaryUI();
    		}
    		#endregion
    		private void AddTemporaryUI()
    		{
    			const string vsSourceControlExplorerExtender =
    				"Microsoft.VisualStudio.TeamFoundation.VersionControl.VersionControlExt";
    
    			try
    			{
    				var vce = (VersionControlExt) _applicationObject.GetObject(vsSourceControlExplorerExtender);
    				if (vce == null)
    				{
    					throw new NullReferenceException();
    				}
    				_explorer = vce.Explorer;
    				_explorer.SelectionChanged += ExplorerSelectionChanged;
    			}
    			catch (Exception e)
    			{
    				MessageBox.Show(e.ToString());
    			}
    		}
    
    		private void ExplorerSelectionChanged(object sender, EventArgs e)
    		{
    			if (_exlporerScc == null)
    			{
    				_exlporerScc = sender;
    				Assembly assembly = typeof (WritableFileDirectory).Assembly;
    				Type explorerSccType = assembly.GetType("Microsoft.TeamFoundation.VersionControl.Controls.ExplorerScc");
    				FieldInfo treeViewHatterasFoldersInfo = explorerSccType.GetField("treeViewHatterasFolders");
    				_treeViewHatterasFolders = (TreeView) treeViewHatterasFoldersInfo.GetValue(_exlporerScc);
    				_treeViewHatterasFolders.AfterExpand += TreeViewHatterasFoldersOnAfterExpand;
    			}
    		}
    
    		private void FilterFolderTreeNodes(IEnumerable<TreeNode> nodes, SynchronizationContext synchronizationContext)
    		{
    			VersionControlServer versionControlServer = _explorer.Workspace.VersionControlServer;
    			string collectionName = versionControlServer.TeamProjectCollection.Name;
    			List<string> filterFolder =
    				_explorer.Workspace.Folders.Select(mappedFolder => mappedFolder.ServerItem).Select(
    					si => si.Replace("$", collectionName)).ToList();
    			foreach (TreeNode tn in nodes)
    			{
    				TreeNode treeNode = tn;
    				if (treeNode.TreeView != null)
    				{
    					if (treeNode.Text != string.Format("{0}{1}", GuiResources.Working, "...") &&
    					    (treeNode.Parent != null &&
    					     !filterFolder.Any(
    					     	ff => treeNode.FullPath.StartsWith(ff.Substring(0, Math.Min(ff.Length, treeNode.FullPath.Length))))))
    					{
    						synchronizationContext.Post(state => treeNode.Remove(), null);
    						continue;
    					}
    				}
    				if (treeNode.Nodes.Count > 0)
    				{
    					FilterFolderTreeNodes(treeNode.Nodes.Cast<TreeNode>().ToList(), synchronizationContext);
    				}
    			}
    		}
    
    		private void TreeViewHatterasFoldersOnAfterExpand(object sender, TreeViewEventArgs treeViewEventArgs)
    		{
    			List<TreeNode> nodes = _treeViewHatterasFolders.Nodes.Cast<TreeNode>().ToList();
    			SynchronizationContext synchronizationContext = SynchronizationContext.Current;
    			Task.Factory.StartNew(() => FilterFolderTreeNodes(nodes, synchronizationContext));
    		}
    	}
    }

    Tuesday, May 29, 2012 5:57 AM

All replies

  • Swtrse, you can try to create a addin, in this addin you try to traverse your folder.

    and you can take a look at this article to learn how to iterate Through a Directory Tree http://msdn.microsoft.com/en-us/library/bb513869.aspx

    and take a look at this article to learn how to create a addin, http://msdn.microsoft.com/en-us/library/80493a3w.aspx

    Wednesday, May 16, 2012 7:42 AM
  • Maybe I was a bit unclear.

    I want to filter the Folder List in the Source Control Explorer (Left side of the Screen). It is unclear to me how iterate throug the File system could help me to achieve this.

    Wednesday, May 16, 2012 5:04 PM
  • Once again you have not understand what my question is, even after I mention that iterate through the File system does not solve my problem. I do know how to iterate the file system since 25 years in many different languages. To be clear I added a picture to prevent other misunderstandings.

    How does Iterating the File System help to filter the List of Team Projects in the Source Control Explorer?

    Monday, May 21, 2012 11:30 AM
  • I found a solution

    Thanks

    Friday, May 25, 2012 7:54 AM
  • And here is the source code for all who want's to add that functionally to or improve it.

    using System;
    using System.Collections.Generic;
    using System.Linq;
    using System.Reflection;
    using System.Threading;
    using System.Threading.Tasks;
    using System.Windows.Forms;
    using EnvDTE80;
    using Extensibility;
    using Microsoft.TeamFoundation.VersionControl.Client;
    using Microsoft.TeamFoundation.VersionControl.Controls;
    using Microsoft.VisualStudio.TeamFoundation.VersionControl;
    
    namespace TFSSControlExplorerAddin
    {
    	/// <summary>
    	/// 	The object for implementing an Add-in.
    	/// </summary>
    	/// <seealso class='IDTExtensibility2' />
    	public class Connect : IDTExtensibility2 //, IDTCommandTarget
    	{
    		private DTE2 _applicationObject;
    		private object _exlporerScc;
    		private VersionControlExplorerExt _explorer;
    		private TreeView _treeViewHatterasFolders;
    		#region IDTExtensibility2 Members
    		/// <summary>
    		/// 	Implements the OnAddInsUpdate method of the IDTExtensibility2 interface. Receives notification when the collection of Add-ins has changed.
    		/// </summary>
    		/// <param name='custom'> Array of parameters that are host application specific. </param>
    		/// <seealso class='IDTExtensibility2' />
    		public void OnAddInsUpdate(ref Array custom)
    		{
    		}
    
    		/// <summary>
    		/// 	Implements the OnBeginShutdown method of the IDTExtensibility2 interface. Receives notification that the host application is being unloaded.
    		/// </summary>
    		/// <param name='custom'> Array of parameters that are host application specific. </param>
    		/// <seealso class='IDTExtensibility2' />
    		public void OnBeginShutdown(ref Array custom)
    		{
    		}
    
    		/// <summary>
    		/// 	Implements the OnConnection method of the IDTExtensibility2 interface. Receives notification that the Add-in is being loaded.
    		/// </summary>
    		/// <param name='application'> Root object of the host application. </param>
    		/// <param name='connectMode'> Describes how the Add-in is being loaded. </param>
    		/// <param name='addInInst'> Object representing this Add-in. </param>
    		/// <param name="custom"> Array of parameters that are host application specific. </param>
    		/// <seealso class='IDTExtensibility2' />
    		public void OnConnection(object application, ext_ConnectMode connectMode, object addInInst, ref Array custom)
    		{
    			try
    			{
    				_applicationObject = (DTE2) application;
    				switch (connectMode)
    				{
    					case ext_ConnectMode.ext_cm_UISetup:
    
    						// Do nothing for this add-in with temporary user interface
    						break;
    
    					case ext_ConnectMode.ext_cm_Startup:
    
    						// The add-in was marked to load on startup
    						// Do nothing at this point because the IDE may not be fully initialized
    						// Visual Studio will call OnStartupComplete when fully initialized
    						break;
    
    					case ext_ConnectMode.ext_cm_AfterStartup:
    
    						// The add-in was loaded by hand after startup using the Add-In Manager
    						// Initialize it in the same way that when is loaded on startup
    						AddTemporaryUI();
    						break;
    				}
    			}
    			catch (Exception ex)
    			{
    				MessageBox.Show(ex.ToString());
    			}
    		}
    
    		/// <summary>
    		/// 	Implements the OnDisconnection method of the IDTExtensibility2 interface. Receives notification that the Add-in is being unloaded.
    		/// </summary>
    		/// <param name='disconnectMode'> Describes how the Add-in is being unloaded. </param>
    		/// <param name='custom'> Array of parameters that are host application specific. </param>
    		/// <seealso class='IDTExtensibility2' />
    		public void OnDisconnection(ext_DisconnectMode disconnectMode, ref Array custom)
    		{
    			try
    			{
    				switch (disconnectMode)
    				{
    					case ext_DisconnectMode.ext_dm_HostShutdown:
    					case ext_DisconnectMode.ext_dm_UserClosed:
    						break;
    				}
    			}
    			catch (Exception e)
    			{
    				MessageBox.Show(e.ToString());
    			}
    		}
    
    		/// <summary>
    		/// 	Implements the OnStartupComplete method of the IDTExtensibility2 interface. Receives notification that the host application has completed loading.
    		/// </summary>
    		/// <param name='custom'> Array of parameters that are host application specific. </param>
    		/// <seealso class='IDTExtensibility2' />
    		public void OnStartupComplete(ref Array custom)
    		{
    			AddTemporaryUI();
    		}
    		#endregion
    		private void AddTemporaryUI()
    		{
    			const string vsSourceControlExplorerExtender =
    				"Microsoft.VisualStudio.TeamFoundation.VersionControl.VersionControlExt";
    
    			try
    			{
    				var vce = (VersionControlExt) _applicationObject.GetObject(vsSourceControlExplorerExtender);
    				if (vce == null)
    				{
    					throw new NullReferenceException();
    				}
    				_explorer = vce.Explorer;
    				_explorer.SelectionChanged += ExplorerSelectionChanged;
    			}
    			catch (Exception e)
    			{
    				MessageBox.Show(e.ToString());
    			}
    		}
    
    		private void ExplorerSelectionChanged(object sender, EventArgs e)
    		{
    			if (_exlporerScc == null)
    			{
    				_exlporerScc = sender;
    				Assembly assembly = typeof (WritableFileDirectory).Assembly;
    				Type explorerSccType = assembly.GetType("Microsoft.TeamFoundation.VersionControl.Controls.ExplorerScc");
    				FieldInfo treeViewHatterasFoldersInfo = explorerSccType.GetField("treeViewHatterasFolders");
    				_treeViewHatterasFolders = (TreeView) treeViewHatterasFoldersInfo.GetValue(_exlporerScc);
    				_treeViewHatterasFolders.AfterExpand += TreeViewHatterasFoldersOnAfterExpand;
    			}
    		}
    
    		private void FilterFolderTreeNodes(IEnumerable<TreeNode> nodes, SynchronizationContext synchronizationContext)
    		{
    			VersionControlServer versionControlServer = _explorer.Workspace.VersionControlServer;
    			string collectionName = versionControlServer.TeamProjectCollection.Name;
    			List<string> filterFolder =
    				_explorer.Workspace.Folders.Select(mappedFolder => mappedFolder.ServerItem).Select(
    					si => si.Replace("$", collectionName)).ToList();
    			foreach (TreeNode tn in nodes)
    			{
    				TreeNode treeNode = tn;
    				if (treeNode.TreeView != null)
    				{
    					if (treeNode.Text != string.Format("{0}{1}", GuiResources.Working, "...") &&
    					    (treeNode.Parent != null &&
    					     !filterFolder.Any(
    					     	ff => treeNode.FullPath.StartsWith(ff.Substring(0, Math.Min(ff.Length, treeNode.FullPath.Length))))))
    					{
    						synchronizationContext.Post(state => treeNode.Remove(), null);
    						continue;
    					}
    				}
    				if (treeNode.Nodes.Count > 0)
    				{
    					FilterFolderTreeNodes(treeNode.Nodes.Cast<TreeNode>().ToList(), synchronizationContext);
    				}
    			}
    		}
    
    		private void TreeViewHatterasFoldersOnAfterExpand(object sender, TreeViewEventArgs treeViewEventArgs)
    		{
    			List<TreeNode> nodes = _treeViewHatterasFolders.Nodes.Cast<TreeNode>().ToList();
    			SynchronizationContext synchronizationContext = SynchronizationContext.Current;
    			Task.Factory.StartNew(() => FilterFolderTreeNodes(nodes, synchronizationContext));
    		}
    	}
    }

    Tuesday, May 29, 2012 5:57 AM
  • This functionality is now included in my Visual Studio 2010 / 2012 Extension TFSSCExplorerExtension. You are welcome to download it for free from Visual Studio Gallery, explore and use.
    Saturday, June 16, 2012 2:20 PM