Hi,
I need to find a UIHierarchyItem from a ProjectItem. My current solution involves getting the UIHierarchy from the Solution Explorer, and searching depth-first for the hierarchy item with a corresponding project item. For a large project it takes 15-20 seconds to find it, and the Solution Explorer window flickers the whole time.
Is there a better way
Ivo

UIHierarchyItem from a ProjectItem
wfsCA
While you can go from a UIHierarchyItem to a ProjectItem, there is no way to go in reverse. There are a few reasons for this, such as a project can be hidden from the solution explorer window, and so going from the ProjectItem to the UIHierarchyItem is impossible, but mostly it was because the project objects were in place long before the UIHierarchyItem object was added to the object model, and adding new methods to the ProjectItem object would break compatibility with existing code, so we never added it.
Craig
lisa52
You can call ProjectItem.Collection to get to the collection containin the item (a ProjectItems object), then call Parent to get to the item representing the collection. Be careful with what you do with this object! It can be either a ProjectItem or a Project object, depending on where you are in the hierarchy.
Craig
The_Drewster
Do I do QueryInterface to find which one it is, or just compare the pointer to get_ContainingProject
Thanks
Ivo
Jordi73757
You will want to do a QI.
Craig
Daniel Bachler
I was able to do the same using much simpler solution (VS 2008):
item.ExpandView();
var hitem = _findUIHierarchyItem(items, item);
...
UIHierarchyItem _findUIHierarchyItem(UIHierarchyItems items, ProjectItem item) {
}
My only problem with this is when there are "Solution Folders" (i.e. virtual folders, VS lets you create) and they are collapsed - ExpandView() throws exception. Note that the solution above (by 280Z28) returns null in such case (the clause handling Project, returns no result because "items" collection is empty).
The only workaround I could find is to perform fake expand/collapse of "Solution Folders" using the following method. It's not as slow as expanding the whole tree, so it will perform OK in most cases
try {
item.ExpandView();
} catch {
_loadSolutionItems(items);
item.ExpandView();
}
var hitem = _findUIHierarchyItem(items, item);
...
void
_loadSolutionItems(UIHierarchyItems items) {var xitems = new List<UIHierarchyItem>();
foreach (UIHierarchyItem child in items) {
var pi = child.Object as ProjectItem;
var pis = child.Object as ProjectItems;
var project = child.Object as Project;
var expand = false;
if ((pi != null) && (pi.Kind == Constants.vsProjectItemKindSolutionItems)) expand = true;
if ((pis != null) && (pis.Kind == Constants.vsProjectItemsKindSolutionItems)) expand = true;
if ((project != null) && (project.Kind == Constants.vsProjectKindSolutionItems)) expand = true;
if (expand && (child.UIHierarchyItems != null) && (child.UIHierarchyItems.Expanded == false)) {
child.UIHierarchyItems.Expanded = true;
xitems.Add(child);
}
_loadSolutionItems(child.UIHierarchyItems);
}
foreach (var item in xitems) {
item.UIHierarchyItems.Expanded = false;
}
}
Looks ugly, but seems to work OK.
sParc134758
I don't think you realize quite how much speed difference the DFS is compared to the way I did it...
It's fine on small projects but it's death on big ones. It almost made me want to find the item myself...
I haven't needed to find solution items yet, but I was aware mine didn't find them. I'll take a look at that sometime.
SD Diver
Takes milliseconds on a solution with 15 projects/6000+ files. Pass a ProjectItem as the second parameter. (See below for a sample call.
public static EnvDTE.UIHierarchyItem FindHierarchyItem( EnvDTE.UIHierarchyItems items, object item )
{
if ( items == null || item == null )
return null;
EnvDTE.UIHierarchyItem hitem = ( from i in items.Cast()
where i.Object == item
select i )
.FirstOrDefault();
if ( hitem != null )
return hitem;
EnvDTE.UIHierarchyItem parent = null;
if ( item is EnvDTE.ProjectItem )
{
parent = FindHierarchyItem( items, ( item as EnvDTE.ProjectItem ).Collection.Parent );
}
else if ( item is EnvDTE.Project )
{
EnvDTE.UIHierarchyItem result = ( from child in items.Cast()
where !( child.Object is EnvDTE.Project )
where !( child.Object is EnvDTE.ProjectItems )
select FindHierarchyItem( child.UIHierarchyItems, item ) )
.FirstOrDefault();
return result;
}
else if ( item is EnvDTE.Solution )
{
// solutions have no parent
return null;
}
else
{
parent = ( from child in items.Cast()
where child != null
select FindHierarchyItem( child.UIHierarchyItems, item ) )
.FirstOrDefault();
}
if ( parent != null )
{
bool expanded = parent.UIHierarchyItems.Expanded;
if ( !expanded && parent.UIHierarchyItems.Count == 0 )
{
try
{
parent.UIHierarchyItems.Expanded = true;
}
catch
{
}
}
EnvDTE.UIHierarchyItem found = FindHierarchyItem( parent.UIHierarchyItems, item );
if ( !expanded && parent.UIHierarchyItems.Expanded )
{
try
{
parent.UIHierarchyItems.Expanded = false;
}
catch
{
}
}
return found;
}
return null;
}
Call the function like this:
// Find the project item for the document
EnvDTE.
ProjectItem item = applicationObject.Solution.FindProjectItem( filename );// Get the UIHierarchyItems
EnvDTE.
UIHierarchy uiHierarchy = applicationObject.ToolWindows.SolutionExplorer;EnvDTE.
UIHierarchyItems items = uiHierarchy.UIHierarchyItems;// Convert the ProjectItem into UIHierarchyItem
EnvDTE.
UIHierarchyItem hitem = SolutionFileListControl.FindHierarchyItem( items, item );pxgator
Thanks. I was expecting that...
Now another related question - if I have a ProjectItem how do I get the parent ProjectItem And can I assume that the hierarchy of the UIHierarchyItems is the same as the hierarchy of the ProjectItems
If I have both of these I can do a much smarter search.
Ivo