Dotneteers.net
All for .net, .net for all!

LearnVSXNow! Part #39 - Working with Hierarchies Part 4 - Hierarchy Windows

It’s been a long time since I published the latest article in this mini-series. In the first part I gave an overview about the hierarchy basics and in the second part I treated the basic structure of hierarchies. The first time I introduced code samples was the third part dealing with property access and hierarchy traversal.

After that article I was thinking about creating a few small samples to build basic custom hierarchies. However, it was harder than I had ever guessed before! I did not find appropriate samples and documentations so I made many experiments to discover how hierarchies and hierarchy windows work in order to create my own solutions. After a few weeks of trying several ways and many implementation patterns I decided to create a basic framework that makes programming hierarchies a relatively straightforward task. This article is the first fruit of my efforts.

I can say that the framework I established is quite far away from a perfect one, but I carry on extending it with hierarchy support types. You may not be surprised if I tell you the hierarchy support types are built into the VSXtra framework. In this post I introduce you hierarchy windows and their co-operation with hierarchies. In the next posts we are going to create custom hierarchies.

Hierarchy Windows

Visual Studio uses Hierarchy Windows to display hierarchies and allow the user to interact with them. The most known of them are the Solution Explorer and the Server Explorer tool windows. Hierarchies hosted in hierarchy windows live in a strong synergy with Visual Studio. Just a few characterizing points about this synergy:

—  Hierarchy items (zero, one or more) can be selected in the hierarchy windows and Visual Studio is able to track this selection so that other packages and objects can access this selection context.

—  Nodes can be assigned to documents (just like as Solution Explorer nodes can be assigned to source files)

—  Hierarchy windows and so hierarchy nodes can be targeted by commands. The hierarchy and its hosting window can cooperate to execute (or even refuse) commands received.

—  Hierarchy items can notify the window about changes in their states and even other objects in Visual Studio can subscribe to these hierarchy events.

A hierarchy window is an object that implements the IVsUIHierarchyWindow interface. This interface defines all responsibilities of a window that can display a hierarchy and allows the user interacting with it. The interface has only a few methods:

public interface IVsUIHierarchyWindow

{

  int Init(IVsUIHierarchy pUIH, uint grfUIHWF, out object ppunkOut);

  int ExpandItem(IVsUIHierarchy pUIH, uint itemid, EXPANDFLAGS expf);

  int AddUIHierarchy(IVsUIHierarchy pUIH, uint grfAddOptions);

  int RemoveUIHierarchy(IVsUIHierarchy pUIH);

  int SetWindowHelpTopic(string lpszHelpFile, uint dwContext);

  int GetItemState(IVsUIHierarchy pHier, uint itemid, uint dwStateMask,

    out uint pdwState);

  int FindCommonSelectedHierarchy(uint grfOpt, out IVsUIHierarchy lppCommonUIH);

  int SetCursor(IntPtr hNewCursor, out IntPtr phOldCursor);

  int GetCurrentSelection(out IntPtr ppHier, out uint pitemid,

    out IVsMultiItemSelect ppMIS);

}

The interface defines nine operations that do not really seem enough to define all what can be done with a hierarchy. The secret is behind the method called ExpandItem that has a bit misleading name. Despite of its name that suggests we can use it to visually expand an item of the hierarchy, in reality this method allows about a dozen of operations like collapsing a hierarchy item, extending the current selection, highlighting the item with bold typeface or graying it imitating the “to be cut” state, etc.

I do not want to bore you with going along on the methods above one-by-one; we’ll see the most relevant ones later.

IVsUIHierarchyWindow is just an interface. To use it we need a service object implementing this behavior. You can imagine that it is not easy to create a window to carry out all the operations we expect from a hierarchy window. Fortunately, you do not have to write your own hierarchy window implementation from scratch, you can use the one implemented by Visual Studio. That implementation is a COM object that represents the hierarchy in a tree view control as you recognize it in Solution Explorer.

The beauty of IVsUIHierarchyWindow is that theoretically you can use it to produce your own visual representation of a hierarchy window, for example using WPF to make a twist on the “ordinal” user experience. Before starting thinking about it, please let time for yourself to understand how hierarchy windows act as a bridge among hierarchies, Visual Studio IDE and user interactions.

Understanding IVsUIHierarchy

If you take a closer look at the method definitions above almost all of them has a parameter with a type of IVsUIHierarchy. This interface is similar to IVsHierarchy not only in its name but also in its functionality. IVsUIHierarchy implements IVsHierarchy and adds two new methods:

public interface IVsUIHierarchy : IVsHierarchy

{

  int QueryStatusCommand(uint itemid, ref Guid pguidCmdGroup, uint cCmds,

    OLECMD[] prgCmds, IntPtr pCmdText);

  int ExecCommand(uint itemid, ref Guid pguidCmdGroup, uint nCmdID,

    uint nCmdexecopt, IntPtr pvaIn, IntPtr pvaOut);

}

IVsUIHierarchy actually allows hierarchy nodes being OLE command targets. It will be obvious if you compare the definition above with the definition of the IOleCommandTarget interface:

public interface IOleCommandTarget

{

  int QueryStatus(ref Guid pguidCmdGroup, uint cCmds, OLECMD[] prgCmds,

    IntPtr pCmdText);

  int Exec(ref Guid pguidCmdGroup, uint nCmdID, uint nCmdexecopt, IntPtr pvaIn,

    IntPtr pvaOut);

}

The IVsUIHierarchy methods have a first uint parameter that addresses the hierarchy item to send the command or command status notification for. So, what is role of IVsUIHierarchy in conjunction with a hierarchy window? To understand it we must take a short overview (or repeat) about commands and command routing.

A command represents a logical action (user interaction or other action raised by the code) that can result in execution of some code. The command itself is not necessarily owns the code to be invoked when the user (or code) initiates the action. A command target is an entity that knows how to execute a command received from its environment. It can route, execute or even refuse a command. The command target also provides information about the state of the command. So, IOleCommandTarget and the new methods of IVsUIHierarchy actually define the responsibilities of command targets.

Routing Hierarchy Commands

When a command is raised there might be zero, one or more command targets that can understand what the command means and how to carry out the related action. Visual Studio provides a routing algorithm to forward the command to the fitting target.

In the way the command “bubbles up” from the current level called the active command context. If the active command context is not a command target the command bubbles up to the upper level. If this context is a command target it has the chance to handle the command or to say “I do not know what to do with this command” and the bubble goes on accordingly.

The routing algorithm defines the following levels from the bottom to the top level:

—  Present Add-in. Commands first are offered to the registered and loaded Add-ins.

—  Context (shortcut) menus. If the user initiated the command from a context menu, the command target object belonging to this menu has the first chance to handle the command then the normal route (starting from Add-Ins) is applied.

—  Focus window. The window having the focus is the next entity that could undertake command handling. This can be either a tool window or a document window (e.g. a window related to an editor). The management of the command is different depending on what kind of window we have.

—  Current project. The current project gets the opportunity to process the command. If it does not handle, the command goes up in the hierarchy of projects till the level of solution. All nodes on this route can manage the command just like as other command target objects. (VS SDK allows creating subprojects called “flavors”. When we use subprojects, the upper level of a project is not necessary the solution.)

—  Environment. Each package should be able to handle commands owned (defined) by itself. Theoretically it is not mandatory but why to define custom commands that are not handled by the only entity that would know how to deal with them...

—  Global level. If a command is not handled during the previous levels, the environment attempts to forward it to the appropriate package (the package defining the command).

A custom hierarchy window generally gets commands when it has the focus. I said generally, because a hierarchy window can be set up to get commands even if that does not have the focus. After getting the command the hierarchy window examines it and if it cannot process directly it sends the command to the hierarchy (or hierarchies) hosted in the window.

There are commands resulted by a user interaction that are directly sent to the hierarchy through the IVsUIHierarchy interface. In this case the hierarchy window sends the identifier of the item as the subject of the command along with the command and so the hierarchy can address the appropriate item. Hierarchy windows have special commands with the command GUID defined by the GUID_VsUIHierarchyWindowCmds value of the VSConstants type. The command IDs within this command group are defined by VSConstants.VsUIHierarchyWindowCmdIds enumeration and can have the following values:

Value

Description

UIHWCMDID_DoubleClick

The command is sent when the user double-clicks anywhere within the hierarchy window. If double-click is right on one of the items the command is directly sent to that item. If the double-click hits on any other window area, the command is sent to the root hierarchy item (actually to the hierarchy itself).

UIHWCMDID_EnterKey

The command is sent when the user selects one or more hierarchy item and presses the Enter key. If a single item is selected, the command is directly sent to that item. If multiple items are selected the command is sent with the special command ID VSITEMID_SELECTION.

UIHWCMDID_RightClick

The command is sent when the user selects one or more hierarchy item and right-clicks on the selection. If a single item is selected, the command is directly sent to that item. If multiple items are selected the command is sent with the special command ID VSITEMID_SELECTION.

UIHWCMDID_StartLabelEdit

When a hierarchy node turns to an editable entry field this command is sent to the appropriate hierarchy item. This command always addresses one item.

UIHWCMDID_CommitLabelEdit

This command is sent to a hierarchy item when its editable entry field turns back to a static text item.

UIHWCMDID_CancelLabelEdit

When the user presses the Esc key during the editing of a hierarchy item label, this command is sent to the appropriate node.

Beside these commands, other commands also can be sent to the hierarchy by the environment. Of course, any other commands can be sent by program code to the hierarchy nodes.

Setting up Hierarchy Windows

A hierarchy window is only a blank window if we do not put a hierarchy (an IVsUIHierarchy instance) in it. So, first we have to create an object implementing the IVsUIHierarchy to have at least a simple root node in order it can be visualized. When we have it, we have to call the Init method of the IVsUIHierarchyWindow interface of the corresponding window. The Init method sets up the style of the hierarchy window and optionally adds an initial hierarchy to the window. The method has the following signature:

int Init(IVsUIHierarchy pUIH, uint grfUIHWF, out object ppunkOut);

The pUIH parameter represents the hierarchy to show in the window. It can have a null value meaning there is no hierarchy shown at the moment of the call. The ppunkOut output parameter retrieves a pointer to an IUnknow interface to the IVsWindowFrame object holding information about the frame hosting the hierarchy window. The grfUIHWF parameter is a set of flags determining the style (visual style and basic behavior) of the hierarchy window. It gets its value from the __UIHWINFLAGS enumeration.

A hierarchy window can host one or more hierarchies at the same time. For example, the Solution Explorer displays only one hierarchy, but the Server Explorer can show up more hierarchies. You can add hierarchies to the window by calling the AddUIHierarchy method and drop a hierarchy from the window with the RemoveUIHierarchy method.

Cooperation between the Hierarchy Window and the Hierarchy

In the first part and second part I treated the IVsHierarchy interface and the structural properties. I described how hierarchy is formed from a set of items and told that the IVsHierarchy abstraction is very different from the one approached by a tree view control.

Just to recall the most important statements:

Each node in the hierarchy is identified by a 32-bit unsigned integer called hierarchy item ID. The VSITEMID_ROOT represent the root node of the hierarchy. The value VSITEMID_NIL has the same role as the null value: it represents a reference to a non-existing (or missing) hierarchy item. IVsHierarchy uses four methods to query and set the properties of items in a hierarchy. These methods are GetProperty, GetGuidProperty, SetProperty and SetGuidProperty. The relationships among the items of the hierarchy (parent and child nodes) are defined with so-called structural properties. The most important of them are the followings: Parent, FirstChild, FirstVisibleChild, NextSibling and NextVisibleSibling. There are other properties that describe the relations between hierarchy nodes and nested (shortcut) hierarchies: ParentHierarchy and ParentHierarchyItemId.

IVsHierarchy does not provide you any operations like GetChildren, AddNode, RemoveNode, AddChild or RemoveChild! Clients of IVsHierarchy simply use the GetProperty method to obtain the structural properties (and of course other hierarchy related values) when displaying the hierarchy. It is the task of the object implementing the operations changing the hierarchy and to administer the structural properties accordingly. A hierarchy window is a client of the IVsUIHierarchy objects hosted within the window. But what kind of cooperation should be built between the hierarchy window and a hosted hierarchy in order the changes in the hierarchy can be displayed?

The key behind this mechanism is based on hierarchy change notification events. IVsHierarchy defines two methods to subscribe for notification events and to unsubscribe from them:

int AdviseHierarchyEvents(IVsHierarchyEvents pEventSink, out uint pdwCookie);

int UnadviseHierarchyEvents(uint dwCookie);

The IVsHierarchyEvents defines the notifications to be received when there are any changes in the hierarchy. The hierarchy window subscribes to these notifications through calling the AdviseHierarchyEvents method when a hierarchy is hosted in the window. Of course, it unsubscribes from the events when the hierarchy is removed. IVsHierarchyEvents is declared with the following notification methods:

public interface IVsHierarchyEvents

{

  int OnItemAdded(uint itemidParent, uint itemidSiblingPrev, uint itemidAdded);

  int OnItemsAppended(uint itemidParent);

  int OnItemDeleted(uint itemid);

  int OnPropertyChanged(uint itemid, int propid, uint flags);

  int OnInvalidateItems(uint itemidParent);

  int OnInvalidateIcon(IntPtr hicon);

}

So, when the object implementing the IVsUIHierarchy interface changes the hierarchy behind, it should raise the appropriate event (call the appropriate method of IVsHierarchyEvents). For example, when adding a child item to a node, it can either call OnItemAdd (inserting the new child somewhere in between the existing children) or OnItemsAppended (appending one or more child to the end of the list of children already parented by the node). If there are many changes in the children of a parent node, the OnInvalidateItems event should be raised. When only a single property has been altered, the OnPropertyChanged event is the one to fire.

The hierarchy window gets these notifications and decides which part of the hierarchy should be repainted. Then turns to the hierarchy object and asks for item properties required for the repaint operation using the GetProperty and GetGuidProperty methods of the IVsHierarchy interface. For example, if the OnInvalidateItems event is called, the hierarchy window receives the ID of the item where children changed and asks for all the visual properties of all children parented by the node with this ID.

The hierarchy window not only queries IVsUIHierarchy properties but sets a few of them! One of the most often used properties is Expanded that indicates if a node is expanded visually in the tree view representing the hierarchy. Why is it useful?

Often hierarchies are built on-demand. If producing the children of a parent is an expensive operation, it is a good strategy to enumerate them at the first time when the parent node is expanded. For example, if a node represents a folder and its children correspond to files in that folder, it is useful to obtain the list of files only when the user expands the folder node.

As the hierarchy window calls the SetProperty method of IVsUIHierarchy, we can recognize that a hierarchy node is about to be expanded (or collapsed) and we can use this information to set up the children nodes (or even in case of collapsing dispose the unused resources).

Implementing Custom Hierarchies

Using an existing hierarchy like the one we can see in the Solution Explorer is not a very difficult task while we handle it only in read-only manner. The IVsUIHierarchy interface itself is not the easiest tool we can use to obtain information about existing items and properties, but you can wrap its functionality into helper classes just as I did in the third part of this mini-series.

If you want to modify properties within the hierarchy, you must have a basic understanding of the specific hierarchy (the meaning and semantics of properties) you are about to manipulate. It is generally not as easy as a simple information query.

If you want to modify the structure of the hierarchy you must know subtle details about the hierarchy and this is a more complex and time-consuming task than the previous two. In this case you must have practice in consuming almost all interfaces mentioned in this post.

The most difficult task is to create your own custom hierarchies. Above the prerequisites of the aforementioned tasks you must implement (and not just simply consume) the relevant interfaces. It is a really complex task coming from the IVsHierarchy type of abstraction and the co-operation between the hierarchies and windows displaying them.

I think the best way to teach how to create custom hierarchies is to use samples. I will follow this approach and in the few next parts I will show you samples. Beside the samples I have created some core classes providing help for you by turning back the IVsUIHierarchy-like approach into the one similar to tree view controls.

Where are we?

To extend Visual Studio with functionality using hierarchies, it is not enough to know how hierarchies are built through the IVsHierachy interface. In order the hierarchies can be displayed and can respond to user interactions, those should be hosted in hierarchy windows represented by the IVsUIHierarchy interface.

You should not write your own hierarchy window (although you can do this), you can use the one provided by Visual Studio. This hierarchy window co-operates with the hosted hierarchy to offer the functionality you perceive. It forwards commands coming from the environment to the hierarchy. For specific user interactions (like pressing the Enter key while an item is selected) the hierarchy window directly addresses the corresponding item or the list of selected items. The hierarchy window paints its area by querying the visual properties of hierarchy nodes. The hierarchy notifies the window about its changes to allow refreshing the UI accordingly.

In the next posts we’ll see how to build a custom hierarchy that work seamlessly with hierarchy windows.


Posted Dec 03 2008, 07:18 AM by inovak
Filed under: ,

Comments

DiveDeeper's blog wrote LearnVSXNow! Part #40 - Working with Hierarchies Part 5 - Managed Classes for Custom Hierarchies
on Fri, Dec 5 2008 15:21

In the previous part of the series I promised to create a code sample to illustrate how to create custom

Visual Studio Hacks wrote Visual Studio Links #87
on Sat, Dec 6 2008 15:28

My latest in a series of the weekly, or more often, summary of interesting links I come across related to Visual Studio. Sara Ford's Tip of the Day #368 tells us that double clicking on the properties folder under a project in solution explorer will open

Sabaresh wrote re: LearnVSXNow! Part #39 - Working with Hierarchies Part 4 - Hierarchy Windows
on Thu, Jan 15 2009 13:52

The OnInvalidateItems event is called, the hierarchy window receives the ID of the item where children changed and asks for all the visual properties of all children parented by the node with this ID.

I didnt get the exact meaning of this line. Can you just explain it?

GuoHui Chen wrote re: LearnVSXNow! Part #39 - Working with Hierarchies Part 4 - Hierarchy Windows
on Sun, Mar 15 2009 7:18

When I create my Hierarchy Windows, the hierarchy window without response (hang) after click some of the hierachy nodes? no any message for this.

I have learned VSX some weeks,  I using VSXtra create my package, I think VSXtra should output some debug messages,  for some error implementation can raising exception stop package load succ. I get package load failed for a service class has no default construction.

thank you for work (good job)!

GuoHui Chen wrote re: LearnVSXNow! Part #39 - Working with Hierarchies Part 4 - Hierarchy Windows
on Sun, Mar 15 2009 7:24

I override the  ExecCommand method

but it does not been called by the system.

I want to impliment the context menu (shotcut menu).

OnExecute UIHWCMDID_RightClick

Can you  give me some examples  or extend the VSXtra function, export some function like OnContextMenu etc.

Thank you very much!

GuoHui Chen wrote re: LearnVSXNow! Part #39 - Working with Hierarchies Part 4 - Hierarchy Windows
on Mon, Mar 16 2009 9:31

Please modify the IVsUIHierarchy.ExecCommand in HierarchyManager.cs, I will create a context menu for HierarchyNode but the HierarchyManager hidden it, cannot override.

Please dont output so many logs in the general output pane, or supply a method stop this kind action.

inovak wrote re: LearnVSXNow! Part #39 - Working with Hierarchies Part 4 - Hierarchy Windows
on Mon, Mar 16 2009 9:37

Hi GuoHui,

It will take a few weeks while I can modify the HierarchyManager as I'm right busy now with other projects. However, If you download the VSXtra source code, you can modify it according your imagination and upload the changes as a patch, it would help a lot.

When I go back to progress with VSXtra, your patch could be very useful :-)

GuoHui Chen wrote re: LearnVSXNow! Part #39 - Working with Hierarchies Part 4 - Hierarchy Windows
on Mon, Mar 16 2009 10:56

Thank you! I will learn how to patch it.

I have found the app hang's  reason:  NestedHierarchy doesn't call nestedHierarchy's SetSite() method, so in the manager's IOleCommandTarget.Exec raise exception.

Thank you! I hope VSXtra go better.

inovak wrote re: LearnVSXNow! Part #39 - Working with Hierarchies Part 4 - Hierarchy Windows
on Mon, Mar 16 2009 11:09

Hey GuoHui,

Please send me an email through the "Contact" function of this page so that I can see your email address and contact you off-line in email instead of via comments :-)

Simon wrote re: LearnVSXNow! Part #39 - Working with Hierarchies Part 4 - Hierarchy Windows
on Tue, May 19 2009 15:33

Hi,

   I'm trying to implement a Hierarchy Window like the Object Explorer of SQL Server 2008 Management Studio which can open a Table Design document window, but I don't know how to do, can you help me ?

buy stendra online wrote re: LearnVSXNow! Part #39 - Working with Hierarchies Part 4 - Hierarchy Windows
on Thu, Feb 14 2013 17:42

CjZWCu Say, you got a nice blog article.Really looking forward to read more. Want more.

buy clomid no prescription wrote re: LearnVSXNow! Part #39 - Working with Hierarchies Part 4 - Hierarchy Windows
on Fri, Mar 1 2013 2:13

ciG0l4 Im grateful for the blog post. Will read on...