Inside VSTS

Exploring Directed Graph Markup Language in VS2010

VS2010 Architecture Explorer Edition includes DGML, a powerful, yet easy to use, tool for visualizing almost anything.

One of the completely awesome new features of Visual Studio 2010 is the Architecture Explorer and the ability to render DGML -- Directed Graph Markup Language. It is a great tool for visualizing anything. One thing I've always wanted to be able to do was to visualize requirements -- and then it struck me -- DGML is supposed to be easy to use! This article provides guidance on visualizing work items which will not be available right at release, but who cares? You can do it today!

This is not an article about all of the intricacies of DGML, although I will cover some of the details. Cameron Skinner is a Program Manager for the Architecture Tools in Visual Studio 2010 and has a great blog post here and here on basic usage of DGML. Instead I'll be focusing on how to use it with work items. The sample code I wrote for doing this is available with this article.

To start with, a DGML document is very simple -- it contains a declaration, root node, a Nodes element and a Links element in its basic form (and the Nodes element isn't even required depending on what you want to do). To generate a DGML document you have to write XML, which is simple to do using the XmlTextWriter class. The basic structure is shown here:

<Nodes>
<Node Id="1" Category="A" Label="B" />
<Node id="2" Category="B" Label="C" />
</Nodes>
<Links>
<Link Source="1" Target="2" />
</Links>

Pretty simple. The Node, the actual oval that will appear on the diagram, is entered in the Nodes element and the links that connect the nodes are entered in the Links element. That's all there is to it (there's more and it isn't complicated, but I'll let you discover that on your own). So how do you get the Work Items and throw those into the DGML document?

Essentially the code needs to walk the work item structure. There are a variety of ways to do this but for this article I will show how to do it starting with a single root work item, which can be anything. This lets you get a pretty good view of, say, the potential impact of a change. First off, in the sample application there is a proxy for the work items with the class WorkItemStub. The stub holds the Work Item ID, Work Item Title and Work Item Type and a list of all related work items. The WorkItemRelationship class contains two properties -- ID and Relationship, where Relationship stores the type of link ("Child", "Tested By", "Affects", etc.). The main reason for this class is prevent you from having to access the Work Item system twice -- once to write the Nodes and once to write the Links. In addition, you want to make sure that your recursion routine doesn't go off the deep end!

Once you have this structure and a reference to the root work item, you can follow the trail. The bulk of the work is done by the ProcessWorkItem method, which takes a WorkItem as an argument (Shown in Listing 1).

/// <summary>
/// The work item to process
/// </summary>
/// <param name="wi"></param>
private void ProcessWorkItem(WorkItem wi)
{
//Get a reference to or add the wi to the workitemstub list
WorkItemStub s =
_workItemStubs.GetItem((int)wi.Fields[CoreField.Id].Value);
if ((s == null) && (wi.Fields[CoreField.WorkItemType].Value.ToString()
!= "Shared Steps"))
{
s = new WorkItemStub();
s.ID = (int)wi.Fields[CoreField.Id].Value;
s.Title = wi.Fields[CoreField.Title].Value.ToString();
s.WorkItemTypeName = wi.Fields[CoreField.WorkItemType].Value.ToString();
_workItemStubs.Add(s);
}
//At this point s is always populated

//Loop through the work items linked to this work item
for (int i = 0; i < wi.WorkItemLinks.Count; i++)
{
int targetID = wi.WorkItemLinks[i].TargetId;
//Check to see if the work item is in the list of work items and if not,
//add it by calling this method recursively. If it is in the list of work
//items then we've already seen this work item and we just have to figure
//out how it is linked to this work item
if (_workItemStubs.GetItem(targetID) == null)
{
if ((wi.WorkItemLinks[i].LinkTypeEnd.Name != "Successor") &&
(wi.WorkItemLinks[i].LinkTypeEnd.Name != "Predecessor"))
{
if (_wis.GetWorkItem(targetID).Fields[CoreField.WorkItemType].
Value.ToString() != "Shared Steps")
ProcessWorkItem(_wis.GetWorkItem(targetID));
}
}

//Determine what type of link this is (we are only handling
//children/tested by/related links)
switch (wi.WorkItemLinks[i].LinkTypeEnd.Name)
{
case "Child":
s.Children.Add(targetID);
break;
case "Tested By":
s.TestedBy.Add(targetID);
break;
case "Related":
s.Related.Add(targetID);
break;
}
}
}

The first step is to check to see if the work item is in the list of work item stubs. If it isn't, add it. Then process each of the links, which can be referenced from the WorkItem.WorkItemLinks collection. For each link, check to see if the target of the link is in the work item stub collection and if it is, don't process it. If it isn't, call the ProcessWorkItem method again. Then add all of the related work items to the related collection of the work item stub. Doing this will walk through every work item that is in any way connected with the root requirement.

Note that in this particular example the code is looking for very specific links -- Child, Tested By and Related and nothing else. It is purposely skipping Predecessor/Successor, Parent, Tests, and Affects/Affected By. It also skips Shared Steps. This is creating a straight tree graph where the graph will be such that circular references will be kept to a minimum. This is not required. It is quite easy to convert this code to follow any link in any direction -- the real key is making sure the recursion doesn't go on indefinitely.

Once all of the work items are processed it is time to write the DGML document. At this point it is a matter of simply looping through the work item stub collection -- the hard work has already been done. First, each work item is written as a Node to the DGML document. The layout that is used in this sample is:

<Node Id="[Work Item ID]" Category="[Work Item Type]" Label="[Work Item Title"] />

Note that the ID is used to identify the work item and will show up on the node in the diagram, when a label attribute is present the label will be displayed instead.

Once the nodes are written, the links need to be written. For the purposes of this sample the links are written as follows:

<Link Source="[Source Work Item ID]" Target="[Target Work Item ID]" Category="[Work Item Link Type End Name]" />

Source work item is the one that the arrow is going from and it is going to the Target. The category is the type of link. This will cause arrows to be written both ways for each type of work item as the link is described in both the Forward and Reverse directions and allows for a more discrete legend and better node navigation. For example, a Requirement is Tested By a Test Case and a Test Case Tests a Requirement.

The end result of this exercise is a requirements traceability matrix like. This lets you select any given requirement, or bug or task and see (depending on how you create the DGML) its impacts.

The last little bonus you get from writing out the nodes and links is that you can use this with the Architecture Explorer. If you open the Architecture Explorer, you can select File System > Select Files... and open your DGML. Then you can selectively hide or display or create new graphs based on the DGML you created.

But that's not all it is limited to. Take for instance Figure 1. This isn't even a set of work items -- it's the full history of the state transition for a single work item. If you look at some of the options available to you the code spends most of the time drawing graphics. Using DGML, only the work of getting the state transition information was necessary (the sample code for this is included in the download also).


[Click on image for larger view.]
Figure 1.

DGML is a simple markup language that when combined with Visual Studio 2010 Ultimate allows for very, very easy display and analysis of data, flow or virtually anything else you can think of that works as a graph. This article and the sample code should get you on your way to creating incredible graphs that maybe you've only thought of before. Explore DGML further because there is much more that you can do now and in the coming months

.

About the Author

Jeff Levinson is the Application Lifecycle Management practice lead for Northwest Cadence specializing in process and methodology. He is the co-author of "Pro Visual Studio Team System with Database Professionals" (Apress 2007), the author of "Building Client/Server Applications with VB.NET" (Apress 2003) and has written numerous articles. He is an MCAD, MCSD, MCDBA, MCT and is a Team System MVP. He has a Masters in Software Engineering from Carnegie Mellon University and is a former Solutions Design and Integration Architect for The Boeing Company. You can reach him at [email protected].

comments powered by Disqus

Featured

  • Compare New GitHub Copilot Free Plan for Visual Studio/VS Code to Paid Plans

    The free plan restricts the number of completions, chat requests and access to AI models, being suitable for occasional users and small projects.

  • Diving Deep into .NET MAUI

    Ever since someone figured out that fiddling bits results in source code, developers have sought one codebase for all types of apps on all platforms, with Microsoft's latest attempt to further that effort being .NET MAUI.

  • Copilot AI Boosts Abound in New VS Code v1.96

    Microsoft improved on its new "Copilot Edit" functionality in the latest release of Visual Studio Code, v1.96, its open-source based code editor that has become the most popular in the world according to many surveys.

  • AdaBoost Regression Using C#

    Dr. James McCaffrey from Microsoft Research presents a complete end-to-end demonstration of the AdaBoost.R2 algorithm for regression problems (where the goal is to predict a single numeric value). The implementation follows the original source research paper closely, so you can use it as a guide for customization for specific scenarios.

  • Versioning and Documenting ASP.NET Core Services

    Building an API with ASP.NET Core is only half the job. If your API is going to live more than one release cycle, you're going to need to version it. If you have other people building clients for it, you're going to need to document it.

Subscribe on YouTube