{S0}简介
现代解决方案包含几十甚至远远超过上百个项目,每个项目数百个文件组成。东方自己是很难在这样的空间。这是正常的,如果你是从项目的开始 - 你知道的每一个历史的和你掌握的很大一部分包含的文件。但它是另一回事,如果你是一个具有10年历史的项目新手。由于某种原因,微软还没有提供facilty搜索解决方案资源管理器树。
有一些方法来处理这种情况。其一是使用工具/ Visual Studio的原始特征:根据"解决方案资源管理器中跟踪活动项"Options... /项目和解决方案/总,在CPP代码的类型,是这样的:#包括"file.cppquot ,然后点击鼠标右键并选择"打开quot; file.cppquot;文件"从上下文菜单中的项目。但做什么,如果你是NET开发人员,和VS汽车项目跟踪的嗡嗡声,有时(no! - 不幸的是,合适的字是"常"在巨大的解决方案与数万项的情况下)。
此包允许项目在解决方案资源管理器搜索。发现某个项目时,默认操作执行(相同的,如果你双击一个项目,在解决方案资源管理器窗口)。此外,包建立一个额外的菜单项,打开文件的上下文菜单(源文件),在各自的UI项目的位置,在解决方案资源管理器树。
Visual Studio SDK中提供了一个对象的数量扩展的基本功能。此文章是专门讨论如何创建一个Visual Studio集成包(C#/菜单命令)的方法。这不是一个演练和它的想法,你有一个关于VS包的基本知识。在这里,我只是提供了我的很多时间,要解决的事情短的注释工作代码(安装)。
你可以在MSDN找到很多文档和教程,使用的Visual Studio软件开发工具包(SDK)"搜索条款。背景
编译和运行所提供的代码,你必须有您的计算机安装了Visual Studio SDK(我用我的电脑VsSDKFebruary2007,它的工作原理VS2005和VS2008以及)。如果你只想得到描述的功能,您可以下载MS安装程序。
作为一个包的基础上,我使用Visual Studio SDK中提供的示例:Example.SolutionHierarchyTraversal。 (和许多其他样本)位于$(程序文件)\ Visual Studio SDK中\(版本)\ VisualStudioIntegration \ SAMPLES。解决方案
MSDN教程帮助创建VS包。很清楚,但我花了很多时间,知道在那里我可以得到菜单和子菜单中的常量的名称,更重要的是 - 在这里我可以得到菜单组中的常量的名称(所有命令都必须属于某个团体)。 VS存储菜单层次结构,在反恐委员会的文件。ShellCmdDef.ctcShellCmdPlace.ctcSharedCmdDef.ctc
SharedCmdPlace.ctc
它们分别位于(程序文件)\ Visual Studio SDK中\(版本)\ VisualStudioIntegration \ COMMON \公司如果你想在某些地方在VS的命令,你应该找到这已经是在上述文件中有一个命令。然后,您创建的菜单组绑定到同一个组。要小心,出现的菜单命令坚实的字符串,但真正的文本代码包含一个"放大器";在中间的按钮标题,(但不强调)。
我遇到的第一混乱,存在着两种类型的解决方案层次:IVsHierarchy和UIHierarchy。首先是"自然"的层次结构,列举了解决方案对象。二是用户界面的层次结构,代表在解决方案资源管理器"窗口的树。好消息是,从微软的家伙优化的用户界面,如果你没有看到在解决方案资源管理器树(它是位于内倒塌的父节点)的节点,VS不为它分配内存)。例如,如果你有8K的文件,只有"选定"的UI层次结构中各自的项目。坏消息是,你不能旅行超过UIHierarchyItem,意图找到一个感兴趣的项目,因为,你有没有发现在UI层次结构中的项目的事实并不意味着它不存在的,这意味着,可能只是不会被加载到内存中。所以,你必须旅行过"真正的"解决方案层次结构中的对象,但没有一个方法来获得各自的UI项目使用真实的项目。要做到这一点,这是第一招。
我使用的方法,从上面的例子中,在分层次的项旅游:private bool FindItemInHierarchy(ref List<string> clew, string match,
IVsHierarchy hierarchy, uint itemid, int recursionLevel,
bool hierIsSolution, bool visibleNodesOnly)
{
Boolean isTermination;
lock(workerManager)
{
isTermination = workerManager.IsTermination;
}
if (isTermination)
{
return false;
}
int hr;
IntPtr nestedHierarchyObj;
uint nestedItemId;
Guid hierGuid = typeof(IVsHierarchy).GUID;
hr = hierarchy.GetNestedHierarchy(itemid, ref hierGuid,
out nestedHierarchyObj, out nestedItemId);
if (VSConstants.S_OK == hr && IntPtr.Zero != nestedHierarchyObj)
{
IVsHierarchy nestedHierarchy =
Marshal.GetObjectForIUnknown(nestedHierarchyObj)
as IVsHierarchy;
// we are responsible to release
// the refcount on the out IntPtr parameter
Marshal.Release(nestedHierarchyObj);
if (nestedHierarchy != null)
{
// Display name and type of the node in the Output Window
//if (
FindItemInHierarchy(
ref clew, match, nestedHierarchy, nestedItemId,
recursionLevel, false, visibleNodesOnly);
//)
//{
//return true;
//}
if (workerManager.IsTermination)
{
return false;
}
}
}
else
{
object pVar;
//Get the name of the root node in question
//here and push its value to clew
hr = hierarchy.GetProperty(itemid,
(int)__VSHPROPID.VSHPROPID_Name, out pVar);
clew.Add((string)pVar);
if (match.Length > 0)
{
//If we find match, terminate node enumerating
if (Regex.Match(match, clew[clew.Count - 1],
RegexOptions.IgnoreCase).Value != String.Empty)
{
SelectUIHItemAndWait(clew);
if (workerManager.IsTermination)
{
return false;
}
//return true;
}
}
else
{
// suppose this is just request for all items in solution
}
++recursionLevel;
hr = hierarchy.GetProperty(itemid,
((visibleNodesOnly || (hierIsSolution && recursionLevel == 1) ?
(int)__VSHPROPID.VSHPROPID_FirstVisibleChild :
(int)__VSHPROPID.VSHPROPID_FirstChild)), out pVar);
Microsoft.VisualStudio.ErrorHandler.ThrowOnFailure(hr);
if (VSConstants.S_OK == hr)
{
//We are using Depth first search so at each level
//we recurse to check if the node has any children
//and then look for siblings.
uint childId = GetItemId(pVar);
while (childId != VSConstants.VSITEMID_NIL)
{
//if (
FindItemInHierarchy(
ref clew, match, hierarchy, childId,
recursionLevel, false, visibleNodesOnly);
//)
//{
//return true
//}
if (workerManager.IsTermination)
{
return false;
}
hr = hierarchy.GetProperty(childId,
((visibleNodesOnly || (hierIsSolution && recursionLevel == 1)) ?
(int)__VSHPROPID.VSHPROPID_NextVisibleSibling :
(int)__VSHPROPID.VSHPROPID_NextSibling),
out pVar);
if (VSConstants.S_OK == hr)
{
childId = GetItemId(pVar);
}
else
{
Microsoft.VisualStudio.ErrorHandler.ThrowOnFailure(hr);
break;
}
}
}
if (match.Length > 0)
{
clew.RemoveAt(clew.Count - 1);
}
}
return false; //node is not found in current hierarchy
}
我改变了原生的工作方法,执行匹配:{C}
因此,我们可以键入任何正则表达式在解决方案资源管理器树中找到的东西。 "事"可能是一个项目,文件夹,源文件,或任何出现在解决方案资源管理器树中的项目。
真正的项目和UI项,将有一个名字和一个平等的祖先层次结构。而且,它是可以选择的UI项目,通过使用以下方法:
UIHierarchyItem GetItem (
[InAttribute] string Names
)
名称包含为了从根,导致随后的子节点的名称。数组中的最后一个名字是作为一个UIHierarchyItem对象返回的节点。
再次,从微软的家伙为你一个惊喜。如果确定的路径节点UI层次结构中不存在通过优化,它不会。通过UI层次结构中的父节点扩展中,您必须强制它的存在。再有隐患,因为它首先看起来像一个简单的任务。UIHierarchyItems.Expanded Property
正如MSDN说,设置或获取是否在层次结构中的节点是展开的。获取...也许,但不设置。编译和运行代码。但价值不会改变后,你必须分配一些值。它不扩大和所有。此外,它不改变它的值"真"。我花了很多时间来确定'为什么'。这似乎只是一个错误,MS不希望在VS版本修复版本。
解决方案是在每个路由节点有趣的节点来模拟用户点击。您可以执行此使用这对UIHierarchyItem.Select:(vsUISelectionType.vsUISelectionTypeSelect) - 选择节点,并UIHierarchy.DoDefaultAction() - 扩大在解决方案资源管理器窗口的层次结构中当前选中的节点。完整的代码如下:private void SelectUIHItem(List<string> nodesTree)
{
// Get an instance of the currently running Visual Studio IDE.
DTE dte;
DTE2 dte2;
dte = (DTE)GetService(typeof(DTE));
dte2 = dte as DTE2;
if (null == dte2)
return;
UIHierarchy UIH = dte2.ToolWindows.SolutionExplorer;
UIHierarchyItem UIHItem;// = UIH.GetItem(path);
StringBuilder path = new StringBuilder();
for (int i = 0; i < nodesTree.Count; ++i)
{
if (i > 0)
{
path.Append('\\');
}
path.Append(nodesTree[i]);
UIHItem = UIH.GetItem(path.ToString());
UIHItem.Select(vsUISelectionType.vsUISelectionTypeSelect);
if (!UIHItem.UIHierarchyItems.Expanded)
{
UIH.DoDefaultAction();
}
}
}
这是一个小窍门。
在旅行节点中,我使用列表中的每一个跟踪的路径。当我来到下一层,我一个项目添加到列表。当我去一个级别,我从列表中删除最后一个项目。这是像穿越迷宫的线索。出于这个原因,Listlt; stringgt;被评为提示。
我们有匹配的节点,我们有充分的路径,所以我们可以选择"点击"的方法,我在上面描述。这是第一招。
在这一点上,我们有办法找到第一个匹配。在早期版本中,找到第二,第三,第四,等等,火柴,我只是一个整数参数,告诉多少场比赛,它是必要的跳过的方法调用相同的旅行。所以每次用户启动"查找下一个"任务,该系统已开始从一开始所有的工作。我这样做是因为我不知道如何恢复状态的递归调用(递归执行搜索,遍历当前节点的每个孩子,每个孩子,每个孩子,等等)。我不知道如何做到这一点,即使在C更托管的C#。
我得到了一个新的想法,当我解决了一个完全无关的任务。我使用的线程。所以我决定等待恢复交换。的思路是:在一个单独的线程中执行搜索,发现项目时,工作线程睡着了;如果用户继续搜索,在UI线程恢复工作线程。
下面的方法处理在解决方案资源管理器中的树中找到的东西:private void MenuItemFindItemInSlnExplorer(object sender, EventArgs e)
{
//Get the solution service so we can traverse
//each project hierarchy contained within.
IVsSolution solution =
(IVsSolution)GetService(typeof(SVsSolution));
if (null != solution)
{
IVsHierarchy solutionHierarchy = solution as IVsHierarchy;
if (null != solutionHierarchy)
{
if (null == dlg)
{
dlg = new FormFindInSlnExplorer();
... //init dialog
}
//dead worker is the same as worker is absent
if (null != workerManager.Worker &&
false == workerManager.Worker.IsAlive)
{
workerManager.Worker = null;
}
System.Windows.Forms.DialogResult findDlgResult = dlg.ShowDialog();
if (System.Windows.Forms.DialogResult.OK ==
findDlgResult && dlg.ItemToFind.Length > 0)
{
if (null != workerManager.Worker)
{
lock (workerManager)
{
workerManager.IsTermination = true;
}
//resume working thread
workerManager.Worker.Resume();
//wait until working thread terminated
workerManager.Worker.Join();
}
StartSearchThread();
}
else if (System.Windows.Forms.DialogResult.Retry == findDlgResult)
{
if (null != workerManager.Worker)
{
workerManager.Worker.Resume();
//resume working thread
}
else
{
//if there was no searching before
//we consider that 'find' button was pushed
StartSearchThread();
}
}
}
}
}
/// Starts search thread
private void StartSearchThread()
{
lock (workerManager)
{
workerManager.Worker = new Std.Thread(new Std.ThreadStart(DoSearch));
workerManager.Worker.Start();
}
}
下面的方法使用一个工作线程执行并开始搜索:/// Search method
private void DoSearch()
{
//Get objects again
IVsSolution solution = (IVsSolution)GetService(typeof(SVsSolution));
if (null == solution)
{
return;
}
IVsHierarchy solutionHierarchy = solution as IVsHierarchy;
if (null == solutionHierarchy)
{
return;
}
//Search recursively
List<string> clew = new List<string>();
// used for path tracking
if(!FindItemInHierarchy(
ref clew, dlg.ItemToFind, solutionHierarchy,
VSConstants.VSITEMID_ROOT, 0, true, false)
&& !workerManager.IsTermination)
{
ShowMessageBox("Item not found");
//see implementation in source codes
}
//terminate termination
lock (workerManager)
{
workerManager.IsTermination = false;
}
}
私人BOOL FindItemInHierarchy(REF Listlt; stringgt;提示,字符串匹配,IVsHierarchy层次结构,UINT ITEMID,recursionLevel INT,hierIsSolution BOOL,BOOL visibleNodesOnly)出现以上。
下面是执行的UI项目的选择和部队当前线程睡眠的方法:private void SelectUIHItemAndWait(List<string> nodesTree)
{
SelectUIHItem(nodesTree);
Std.Thread.CurrentThread.Suspend();
}
要在一个单独的线程,而不是恢复状态,当找到一个匹配的搜索代码是第二招。其实,我利用一对:挂起/恢复,而不是其他一些微软推荐的方法,因为他们只说"糟糕的代码",或"过时",但没有提供任何可以理解的替代品。
接下来的任务是在解决方案资源管理器中选择当前打开的文档。正如我刚才说,你可以检查"追踪活动项目在解决方案资源管理器"选项nder工具/ Options... /项目和解决方案/总实现同样的效果。正如我早些时候说,它可能会导致一个时髦。
解决方案是未来的父从相对的ProjectItem,装配在全节点的路径的名称。然后,我们只是选择的节点,使用这个熟悉的函数:private void MenuItemSyncItemWithSlnExplorer(object sender, EventArgs e)
{
// Get an instance of the currently running Visual Studio IDE.
DTE dte;
DTE2 dte2;
dte = (DTE)GetService(typeof(DTE));
dte2 = dte as DTE2;
if (null == dte2)
{
return;
}
Document activeDocument = dte2.ActiveDocument;
if (null == activeDocument)
{
return;
}
dte.SuppressUI = false;
List<string> clew = new List<string>();
object item = activeDocument.ProjectItem; ;
while (null != item)
{
clew.Insert(0, GetNameProperty(item));
item = GetNextParent(item);
}
clew.Insert(0,
dte2.ToolWindows.SolutionExplorer.UIHierarchyItems.Item(1).Name);
SelectUIHItem(clew);
dte.SuppressUI = true;
}
这招是另一种形式的第一招。
的代码进行了测试,在XP和Vista与Visual Studio 2005和Visual Studio 2008。我希望微软添加此功能的VS版本十。
的源代码是3岁。事实上,为VS2005的插件,因为我开始在C#代码。因此,宽容有关的代码质量。通过这篇文章,我曾试图给读者带来思想。文章写于2009年12月24日。