Archive

Archive for the ‘Visual Studio 2010’ Category

Adding support for nested files in custom Visual Studio project types with MPF

March 10, 2011 1 comment

MPF, short for Managed Package Framework, is a great wrapper library to develop custom Visual Studio packages in C#, but it does not seem to cover everything that is possible within Visual Studio 2010. One of the things it does not provide out of the box is support for so called nested files in custom project types: having a “.xaml” file and below it having the corresponding “.xaml.cs” file. The official Visual Studio C# project type has support for this.

related files

Being a motivated programmer, this of course did not prevent me from implementing this functionality myself. In this article I will explain what you need to do in MPF to support this in your custom project types.

First, we need to override the method “AddNewFileNodeToHierarchy” of our ProjectNode implementation.

protected override void AddNewFileNodeToHierarchy(HierarchyNode parentNode, string path)
{
    if (IsRelatedFile(path) && (parentNode is FolderNode || parentNode is ProjectNode))
    {
        for (HierarchyNode child = parentNode.FirstChild; child != null; child = child.NextSibling)
        {
            string childPath = child.Url;
            if (IsParentOfRelatedFile(childPath, path))
            {
                base.AddNewFileNodeToHierarchy(child, path);
                child.Collapse(); // By default the node would be expanded, we don't want that...
                return; // Correct parent found
            }
        }
    }

    // Not a related file or the parent was not found, add using base functionality
    base.AddNewFileNodeToHierarchy(parentNode, path);
}

This new implementation uses a couple of helper functions.

“IsRelatedFile” should return true if the file is related:

private bool IsRelatedFile(string path)
{
    string extension = Path.GetExtension(path).ToLowerInvariant();

    switch (extension)
    {
        case ".diagram":
        case "your related file extensions ...":
            return true;
        default:
            return false;
    }
}

“IsParentOfRelatedFile” should return true if a node is the matching parent for a given related file:

private bool IsParentOfRelatedFile(string parentPath, string path)
{
    string extension = Path.GetExtension(path);

    return path.Equals(parentPath + extension, StringComparison.InvariantCultureIgnoreCase);
}

“Collapse” is an extension method I have created to collapse nodes in Visual Studio. We have to do this, because otherwise the parent of our related node will always be expanded when we add the nodes to the project. This is visually not appealing.

        public static void Collapse(this HierarchyNode node)
        {
            IVsUIHierarchyWindow uiWindow = UIHierarchyUtilities.GetUIHierarchyWindow(node.ProjectMgr.Site,
                HierarchyNode.SolutionExplorer);
            if (uiWindow != null)
            {
                ErrorHandler.ThrowOnFailure(uiWindow.ExpandItem(node.ProjectMgr, node.ID, EXPANDFLAGS.EXPF_CollapseFolder));
            }
        }

With this in place Visual Studio will already add a parent file and is related file correctly in in the Solution explorer of Visual Studio. But you will notice a very annoying behavior: Visual Studio gives our parent file the behavior of folder; double clicking on it will expand or collapse it. Yet in order to mimic the behavior of the standard C# project type, we only want that our parent file is opened in the editor of Visual Studio, not expanded or collapsed. So we have to intercept the double click and enter behavior of our parent node.

To do this, we have to override the method “ExecCommandOnNode” of our FileNode implementation:

protected override int ExecCommandOnNode(Guid cmdGroup, uint cmd, uint nCmdexecopt, IntPtr pvaIn, IntPtr pvaOut)
{
    if (cmdGroup == VsMenus.guidVsUIHierarchyWindowCmds)
    {
        switch (cmd)
        {
            case (uint)VSConstants.VsUIHierarchyWindowCmdIds.UIHWCMDID_DoubleClick:
            case (uint)VSConstants.VsUIHierarchyWindowCmdIds.UIHWCMDID_EnterKey:
                this.DoDefaultAction();
                return VSConstants.S_FALSE; // We do not want that the node is expanded in the solution view
        }

    }
    return base.ExecCommandOnNode(cmdGroup, cmd, nCmdexecopt, pvaIn, pvaOut);
}

Now our custom project supports nested files! Glimlach

Advertisements