Click here to Skip to main content
15,894,720 members
Articles / Programming Languages / C#

Expression Control for TFS Work Items

Rate me:
Please Sign up or sign in to vote.
4.55/5 (8 votes)
5 Oct 2010CPOL16 min read 66.2K   2.1K   17  
Custom control for TFS Work Items that shows the result of calculating an expression based on the work item fields contents
<!--------------------------------------------------------------------------->  
<!--                           INTRODUCTION                                

 The Code Project article submission template (HTML version)

Using this template will help us post your article sooner. To use, just 
follow the 3 easy steps below:
 
     1. Fill in the article description details
     2. Add links to your images and downloads
     3. Include the main article text

That's all there is to it! All formatting will be done by our submission
scripts and style sheets. 

-->  
<!--------------------------------------------------------------------------->  
<!--                        IGNORE THIS SECTION                            -->
<html>
<head>
<title>The Code Project</title>
<Style>
BODY, P, TD { font-family: Verdana, Arial, Helvetica, sans-serif; font-size: 10pt }
H2,H3,H4,H5 { color: #ff9900; font-weight: bold; }
H2 { font-size: 13pt; }
H3 { font-size: 12pt; }
H4 { font-size: 10pt; color: black; }
PRE { BACKGROUND-COLOR: #FBEDBB; FONT-FAMILY: "Courier New", Courier, mono; WHITE-SPACE: pre; }
CODE { COLOR: #990000; FONT-FAMILY: "Courier New", Courier, mono; }
</style>
<link rel="stylesheet" type=text/css href="http://www.codeproject.com/styles/global.css">
</head>
<body bgcolor="#FFFFFF" color=#000000>
<!--------------------------------------------------------------------------->  


<!-------------------------------     STEP 1      --------------------------->
<!--  Fill in the details (CodeProject will reformat this section for you) -->

<pre>
Title:       Expression Control for TFS Work Items
Author:      konamiman34
Email:       konamiman@konamiman.com
Member ID:   3809825
Language:    C#
Platform:    Windows, .NET 4.0
Technology:  Visual-Studio, VS2010, TFS, Team-Explorer
Level:       Intermediate
Description: Custom control for TFS Work Items that shows the result of calculating an expression based on the work item fields contents
Section      Visual Studio
SubSection   Extensibility
</pre>

<!-------------------------------     STEP 2      --------------------------->
<!--  Include download and sample image information.                       --> 

<ul class="download" id="downloads">
<li><a href="ExpressionControl_installer.zip">Download control installer - 427 Kb </a></li>
<li><a href="ExpressionControl_source.zip">Download source solution - 76 Kb</a></li>
<li><a href="ExpressionControl_Risk.zip">Download example work item - 3 Kb</a></li>
</ul>

<!-- <p><img src="Article.gif" alt="Sample Image - maximum width is 600 pixels" width=400 height=200></p> -->


<!-------------------------------     STEP 3      --------------------------->
<!--  Add the article text. Please use simple formatting (<h2>, <p> etc)   --> 

<h2>Introduction</h2>

<p>This article presents a custom control for Team Foundation Server's Work Item forms.
The control is a textbox that displays the result of a mathematical expression in which other numeric fields
of the work item can participate.</p>

<h2>Background</h2>

<p><em><strong>NOTE:</strong> This article is not intended to be a comprehensive guide on how to create/modify TFS work items or custom controls.
For detailed information please visit the links on <a href="#references">the More Information section</a>.</em></p>

<p>Team Foundation Server is a great platform for Application Lifetime Management. One of the features it provides is the ability to create and manage <strong>Work Items</strong>,
which encapsulate single units of work. </p>

<p>TFS comes out of the box with standard work items defined for managing requirements, bugs, tasks, risks, and other artifacts commonly created
when managing a software development project (these items are defined within two <strong>Project Templates</strong>, MSF for Agile Software Development and MSF for CMMI Process Improvement).
However it is possible to define new work item types and to modify the existing work item types (and the same for the project templates).</p>

<p>Work items consist of <strong>Fields</strong> that store various kind of information; typical fields are the title, the name of the work item creator, the assigned priority,
or the estimated implementation effort. In order to edit the information of these fields, each work item defines also the layout of a <strong>Form</strong> with various editable <strong>Controls</strong> (there are also non-editable controls, such as the work item change history). These forms are displayed by Team Explorer (a component of Visual Studio)
when you create a new work item or when you edit and existing work item.</p>

<p>Team Explorer provides several ready made controls such as a simple one-line textbox, an HTML rich control, a date selector, a dropdown list, or a web browser. It is possible
to create new types of controls and to use them in the TFS work item forms, and tha's what you will see in this aticle.</p>

<h2>So, what is this?</h2>

<p>In this article you will meet <strong>ExpressionControl</strong>, a TFS custom control that shows the result of calculating a mathematical expression. The nice part is that you can reference other fields of the work item in the expression, and if you change the value of one of these fields, the expresion is re-evaluated and the value on the control is automatically updated.
If you associate a work item field to the control, the calculated value is stored within the work item itself, therefore being available in work item queries and reports.</p>

<p>As a proof of concept I provide a modified version of the Risk work item which comes with the MSF for CMMI Process Improvement project template.
In this version I have replaced the <em>Severity</em> field with <em>Impact</em>, which is a numeric field that can take any value. I have also added a <em>Exposure</em> field,
which is automatically calculated by ExpressionControl as probability multiplied by impact.</p>

<p>The control source code is provided as a Visual Studio 2010 solution which consists of one C# class library and one installer project. You can use the solution as a skeleton
for creating your own TFS custom controls.</p>

<h2>Installing and Using the Control</h2>

<p>If you want to install and use ExpressionControl right now, follow these steps:</p>

<ol>
<li>Pretty obvious, but ensure that you have Team Explorer 2010 installed.</li>
<li>Download the installer from <a href="#downloads">the downloads section</a>.</li>
<li>Run the installer. This will just copy two .dll files and one .wicc file (which is just a XML file) to one special subfolder inside the Visual Studio installation folder.</li>
</ol>

<p>Now you can upload the modified Risk work item definition to the server. You can do that by using the <code>witadmin</code> tool that comes with Team Explorer. Open
a Visual Studio command prompt and execute:</p>

<pre>witadmin importwitd /collection:<em>(URL_of_your_TFS_server_and_collection)</em> /p:<em>(name_of_the_team_project)</em> /f:Risk.wit</pre>

<p>Or, if you have <a href="http://visualstudiogallery.msdn.microsoft.com/en-us/c255a1e4-04ba-4f68-8f4e-cd473d6b971f">TFS Power Tools</a> installed on Visual Studio, select <code>Tools - Process Editor - Work Item Types - Import WIT</code> on the Visual Studio menu.</p>

<p>Now try to create a new work item of type Risk. You will see that when you modify either the Probability or the Impact fields, the Exposure field is updated automatically:</p>

<p><img src="ExpressionControl_ModifyingRiskFields.gif" alt="Animation on how modifying Probability or Impact cause the Exposure to be recalculated"></p>

<p>More generally, the way of using the control in a work item is by defining a new control of type ExpressionControl, and adding an attribute named <code>Expression</code>
whose value is the expression to be calculated. The expression can contain basic operations (+ - * /) and the operands can be either numbers, or field names enclosed in brackets, as in <code>[Priority]</code>. For example, in the modified Risk definition we can found the following line:</p>

<pre lang="XML">
&lt;Control
   FieldName = "Konamiman.TFS.Exposure"
   <strong>Type = "ExpressionControl"</strong>
   <strong>Expression = "[Probability]*[Impact]"</strong>
   Label = "Exposure:"
   LabelPosition = "Left"
   ReadOnly = "True"&gt;
</pre>

<p>Associating a field to the control is not mandatory, but it is necessary if you want the calculated value to be persisted to the work item in the TFS database.
For example, we can create a work item query that shows risks with priority, impact and exposure:</p>

<p><img src="ExpressionControl_RisksList.png" alt="Sample of risk list query"></p>

</p>For the modified Risk work item we have a backing field defined as follows:</p>

<pre lang="XML">
&lt;FIELD name="Exposure" refname="Konamiman.TFS.Exposure" type="Integer">
    &lt;HELPTEXT>Exposure of the risk, calculated as probability by impact.&lt;/HELPTEXT>
&lt;/FIELD>
</pre>

<p>You may want to define the field as double instead of integer, if you know that your calculation will give non-integer numbers.
For example you could have defined your expression as <code>([Probability]*[Impact])/100</code>.</p>

<p>In order to insert a new field and control on an existing work item, you need to download the work item definition file from the server. With the <code>witadmin</code> tool
you would do:</p>

<pre>witadmin exportwitd /collection:<em>(URL_of_your_TFS_server_and_collection)</em> /p:<em>(name_of_the_team_project)</em> /n:<em>(work_item_type_name)</em> /f:<em>(destination_file_name></em></pre>

<p>Or with TFS Power Tools, select <code>Tools - Process Editor - Work Item Types - Export WIT</code> on the Visual Studio menu.</p>

<h2>The Project</h2>

<p>A TFS custom control is just a Windows Forms user control that implements <a href="http://msdn.microsoft.com/en-us/library/microsoft.teamfoundation.workitemtracking.controls.iworkitemcontrol(v=VS.90).aspx">the IWorkItemControl interface</a>, accompanied by a descriptor file (.wicc) in XML format
(see the links on the <a href="#references">More Information section</a> for details). Therefore the steps needed to create a custom control are the following:</p>

<ol>
<li>Create a new Visual Studio solution, of type <em>Class Library</em>.</li>
<li>Add references to the <code>System.Windows.Forms</code> and <code>System.Drawing</code> system assemblies.</li>
<li>Add references to the following Team Explorer assemblies:
<p><code>%ProgramFiles%\Microsoft Visual Studio 10.0\Common7\IDE\PrivateAssemblies\Microsoft.TeamFoundation.WorkItemTracking.Controls.dll</code></p>
<p><code>%ProgramFiles%\Microsoft Visual Studio 10.0\Common7\IDE\ReferenceAssemblies\v2.0\Microsoft.TeamFoundation.WorkItemTracking.Client.dll</code></p>
<p>(Note: the root may be instead <code>%ProgramFiles(x86)%</code> on your system)</p>
<li>Add a new item of type <em>User Control</em>, and make it implement IWorkItemControl.</li>
<li>Add the appropriate controls on the User Control design surface.</li>
<li>Add the code for the IWorkItemControl (a few more details on this later).</li>
<li>Add a new XML file, name it <code>YourControlName.wicc</code> (the name of the file will be the name that TFS will use to refer to your control) and set its compilation action to <em>Content</em>. The contents of the file must be as follows:</li>
<pre lang="XML">
&lt;?xml version="1.0"?&gt;
&lt;CustomControl xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:xsd="http://www.w3.org/2001/XMLSchema"&gt;
  &lt;Assembly&gt;<em>YourClassLibraryFileName.dll</em>&lt;/Assembly&gt;
  &lt;FullClassName&gt;<em>YourControlFullClassName</em>&lt;/FullClassName&gt;
&lt;/CustomControl&gt;
</pre>
</ol> 

<p>Once you have compiled your code, you need to copy the resulting .dll file, together with the .wicc file, to the following location:</p>

<pre>%ProgramData%\Microsoft\Team Foundation\Work Item Tracking\Custom Controls\10.0</pre>

<p>And that's it, Team Explorer will recognize your control and display it, as long as you have declared it in a work item.</p>

<h2>The Installer</h2>

<p>Altough deploying your control is as easy as copying two files to the TFS custom control folder as shown above, it is not a bad idea to have an installer project to do the job.
<a href="http://nickhoggard.wordpress.com/2009/11/12/tfs-2010-beta-2-custom-work-item-controls-step-1-getting-started/">Nick Hoggart</a> explains how to create such project,
however since I have done a couple more things that him, I will describe here the full process I have followed for creating the installer:</p>

<ol>
<li>Create a new project in the solution, of type <em>Setup project</em>.</li>
<li>Open the <em>Filesystem Editor</em> tab, and add a new folder of type Custom Folder, named <code>CommonAppDataFolder</code>. Set its <em>DefaultLocation</em> property to <code>[CommonAppDataFolder]</code>.</li>
<li>Under the CommonAppData folder, create the hierarchy for the folder <em>Microsoft\Team Foundation\Work Item Tracking\Custom Controls\10.0</em>, one folder at a time. See the following screenshoot, borrowed from Hoggart's page itself:</li>
<p><img src="ExpressionControl_FileSystemHierarchy.png" alt="Folder hierarchy created in the setup project"/></p>
<li>Right click on the 10.0 folder, and select <em>Add project results</em>. Select <em>Primary Output</em> and <em>Content Files</em> from your class library project (optionally you can select <em>Debug Symbols</em> as well, this will be useful if you want to debug your code).</em>
<li>On the <em>Detected dependencies</em> folder of the setup project in Solution Explorer, select all the <em>Microsoft.TeamFoundation.*</em> dependencies that have been detected, right click and select <em>Exclude</em>, to avoid the installer copying them together with your library.</em>
<li>On the <em>User Interface Editor</em> pane, remove the second and the third screen. These are the install location selection screen (not necessary, since files are always installed at a fixed location) and the install confirmation screen (useless if the install location selection screen has been removed). Unless of course you want to let the user choose if the install will
be done for all users or only for the current user (I always set the <em>InstallAllUser</em> project property to True).</li>

<p>Now we will add a launch condition to avoid the installer to run if Team Explorer is not installed. This is strictly not necessary but will make our installer a bit neater.</p>

<li>Open the <em>Launch Conditions Editor</em> tab.</li>
<li>Right click on <em>Search on Target Machine</em>, then select <em>Add Registry Search</em>.</li>
<li>Select the registry search just added, open the propertieswindow, and set the peoerties as follow: Name = Team Explorer 2010, Property = TEAMEXPLORERINSTALLED, Root = vsdrrHKCU, RegKey = Software\Microsoft\VisualStudio\10.0_Config\InstalledProducts\Team Explorer. See the following screenshoot:</li>

<p><img src="ExpressionControl_RegistryCondition.png" alt="How the registry search condition is to be configured"></p>

<li>Right click on <em>Launch Conditions</em>, then select <em>Add Launch Condition</em>.</li>
<li>Select the launch condition just added, open the properties window, and set the peoerties as follow: Name = Team Explorer 2010, Condition = TEAMEXPLORERINSTALLED, Message = Error: Team Explorer 2010 is not installed. See the following screenshoot:</li>

<p><img src="ExpressionControl_LaunchCondition.png" alt="How the launch condition is to be configured"></p>
</ol>

<p>The last step would be to appropriately configure the setup project properties (Author, Description, URLs, and the like). You can pretty much adapt the provided setup project (and the class library project, for that matter) for your own use by changing "Konamiman" into your own name or company name.</p>

<h2>The code</h2>

<p>Now I'll explain the key points of the control code itself. You can <a href="#downloads">download the Visual Studio solution</a> if you want to take a closer look.</p>

<h3>The Expression Evaluator</h3>

<p>First of all, I must say that I have not crafted my own expression evaluator. Instead, I have used the excellent <a href="http://www.codeproject.com/KB/recipes/sota_expression_evaluator.aspx">Sebastien Ros' State of the Art Expression Evaluation</a>, which provides all the functionality I needed an even more. I recommend you to take a look at Sebastien's article, but here are the basic points you need to know in order to understand how the evaluator is used in ExpressionControl:</p>

<ul>
<li>The core of the evaluator is the <code>Expression</code> class. You pass a string with the expression to be evaluated when you instantiate the class, for example:</li>
<pre lang="C#">var expression = new Expression("([Priority] * [Impact]) / 100");</pre>
<li>You use the <code>Evaluate</code> method of the <code>Expression</code> objet in order to trigger the expression evaluation. The method returns an object with the result of the evaluation
(ExpressionControl handles numbers only, but the Expression class can handle strings, booleans and DateTimes as well).</li>
<li>The <code>Expression</code> class can handle parameters, that is, alphanumeric identifiers (enclosed in brackets) that are resolved dinamically when the expression is evaluated. When a parameter is found during the evaluation, an event named <code>EvaluateParameter</code> is fired. This event gives the parameter name, and expects the parameter value to be set on one of the properties of the associated <code>EventArgs</code>. For example:
<pre lang="C#">
Expression e = new Expression("2 * [Pi]");

e.EvaluateParameter += delegate(string name, ParameterArgs args)
{
    if (name == "Pi") {
        args.Result = 3.14;
    }
};

var result = e.Evaluate();
</pre>
</ul>

<p>The source code of the expression evaluator is embedded in the ExpressionControl project itself, under the folder <code>ExpressionEvaluator</code>. The evaluator code depends on a library, <code>Evaluant.Antlr.dll</code>, which is referenced by the project (and of course must be copied together with the control library at install time).</p>

<h3>The Control Code</h3>

<p>The ExpressionControl visual surface is just a single-line TextBox used to display the results of the expression evaluation. It will be read-only or not, depending on how you configure the ReadOnly property of control in the containing Work Item form (of course it is recommended to configure it as read-only). Team Explorer creates an instance of the control when you create a new work item or when you select an existing work item for edition, provided that the control is declared in the work item form definition.</p>

<p>I will now enumerate the main members of the ExpressionControl class, explaining the key points of each one so that you can get a general idea of how the control works.</p>

<ul>
<li><strong>IWorkItemControl.Properties:</strong> This is a StringDictionary which contains all the attributes of the control, as declared in the XML element <code>Control</code> on the work item form definition (all the XML attributes of the <code>Control</code> element are included here). When this property is set, we retrieve the <code>Expression</code> attribute and use it to instantiate an <code>Expression</code> object, whose <code>EvaluateParameter</code> event is assigned to a handling method (<code>expression_EvaluateParameter</code>).
<li><strong>IWorkItemControl.WorkItemDatasource:</strong> This is the work item which is being edited by the form that contains the control (altough the property is declared as <code>Object</code>, the value is always of type <code>WorkItem</code>). When this property is set, we assign a handler to the work item's <code>FieldChanged</code> event (method <code>OnFieldChanged</code>) so that we can appropriately re-evaluate the expression and update the control value if necessary.</li>
<li><strong>IWorkItemControl.FlushToDatasource:</strong> This method is invoked when the control value has to be backed up to the associated field on the work item (so the value is persisted together with the rest of the work item data in the TFS database). We simply check that the control is actually associated to a work item field (if not, we haven't where to persist) and that the field is of numeric type (string fields are accepted too, in this case the value is simply ToString-ized before it is set), then we set the new value for the field.</li>
<li><strong>GenerateNewTextboxValue:</strong> This method is invoked when the work item form is loaded (via the <code>IWorkItemControl.InvalidateDatasource</code> method) and when one of the fields referenced in the expression changes (via the <code>OnFieldChanged</code> method). It simply invokes the <code>Evaluate</code> method of the <code>Expression</code> object and returns the result as a string to be set on the TextBox (the result is also cached on the <code>calculationValue</code> class field, so that the <code>FlushToDatasource</code> method can use it). The method takes care of all possible error conditions, returning an appropriate error message instead of a calculation result if necessary.</li>
<li><strong>expression_EvaluateParameter</strong>: This is invoked by the expressione evaluator whenever it encounters a parameter in the expression. We assume that the parameter is a field name and query the work item for the field value. If the work item does not declare a field with such name, or if the field is not numeric, we throw an exception, which will be captured by the <code>GenerateNewTextboxValue</code> method and converted into an error message.</li>
</ul>

<p>And that's pretty much it. There are a few more auxiliary members but the core of the control is in the ones listed above.</p>

<h2>Debugging</h2>

<p>When I had the control finished, I found that it worked pretty well, except for one detail: when saving a work item that contained the control, Visual Studio would show me a <em>"Object reference not set to an instance of an object"</em> message box, otherwise saving the work item correctly. It turned out that the <code>FieldChanged</code> event is fired when the work item is about to be saved, but with the <code>Field</code> property of the <code>WorkItemEventArgs</code> argument set to null. I don't know if this is "bug or feature", but for what I see it seems that it is safe to just ignore the event when the Field property is null.</p>

<p>I would never have found the bug if I had not debugged the code. However you can't debug a TFS custom control directly as if it were an ordinary Visual Studio project (you can't "run" it). So, what can we do? <a href="http://social.msdn.microsoft.com/Forums/en-US/tfsworkitemtracking/thread/909c8474-6d5e-4520-9560-1fc08d90e7a4">MSDN has the answer</a> (scroll down until you see the accepted answer). Just a note: you have to skip steps 10 and 11 (you can't "start" a class library project). For completeness, I paste here the procedure (the original refers to VS 2008, I have changed the references to VS2010):

<ol>
<li>Open up VS 2010 and load your project.</li>
<li>Delete all breakpoints in your project.</li>
<li>Right click on the project and go to the project properties.</li>
<li>For the configuration, select Active (Debug).</li>
<li>Under the enable debuggers section, check Enable unmanaged code debugging and enable the Visual Studio hosting process.</li>
<li>Save.</li>
<li>Right click on your project and click Clean.</li>
<li>Right click on your project again and click Rebuild.</li>
<li>In Windows Explorer, go to debug folder and copy the output (.dll, .exe., etc.) to the C:\Documents and Settings\All Users\Application Data\Microsoft\Team Foundation\Work Item Tracking\Custom Controls\10.0 (this is where TF 2010 WI looks for the .dll and .wicc files).</li>
<li><em>(Skip this!) </em>On your toolbar with the drop down set to debug, click the arrow.</li>
<li><em>(Skip this!) </em>You will get a mini screen with your control on it. There should be a load button. Click on it and make sure you load the .dll/.exe from the path specified in step 9.</li>
<li>Leave it open and don�t close anything. Open up another instance of VS2010.</li>
<li>In Team Explorer, open the Work item type that contains your custom control (Add Work Item-&gt; . . . . . ).</li>
<li>Go back to the instance of VS2010 in step 1 and go to Debug-&gt;Attach to Process.</li>
<li>In the attach to Process Window, leave the Transport as Default and the Attach To field to have checked managed code and native code.</li>
<li>In the Available processes window, you will see the process devenv.exe with an ID and the title of the Work Item you just created. Select that row and click on Attach.</li>
<li>You will see some processing in the background as it is loading symbols. Once it is done, set your breakpoints accordingly.</li>
<li>Go back to your 2<sup>nd</sup> instance of VS2010 (step 12) and perform what you need to perform on the Work item you created in step 13 to make sure the break point gets hit.</li>
<li>Once your feel the breakpoint is hit, go back to the 1<sup>st</sup> instance of VS2010 (step 1 and step 17) and you will see the breakpoints you set hit. Now all you have to do is debug (sigh!).</li>
</ol>

<h2 id="references">More Information</h2>

<p>If you want to learn more about TFS work item customization and about TFS custom control, take a look at these resources:</p>

<ul>
<li><a href="http://msdn.microsoft.com/en-us/library/ms195025(VS.80).aspx">Customizing TFS Work Item Types</a> on MSDN.</li>
<li><a href="http://msdn.microsoft.com/en-us/library/bb286959.aspx">Work Item Tracking Custom Controls</a> on MSDN.</li>
<li><a href="http://blogs.msdn.com/b/greggboer/archive/2010/03/30/work-item-tracking-custom-controls.aspx">Work Item Tracking Custom Controls</a> on Gregg Boer's blog (recommended, contains some advanced information).</li>
<li><a href="http://nickhoggard.wordpress.com/2009/11/12/tfs-2010-beta-2-custom-work-item-controls-step-1-getting-started/">Getting Started on Custom Work Item Controls</a> on Nick Hoggard's blog (never mind that it refers to a beta version of TFS, the information provided is relevant for RTM version as well).</li>
<a href="http://witcustomcontrols.codeplex.com/">Custom Controls for TFS Work Item Tracking</a> on CodePlex (the available solution targets TFS 2008, but it will work on TFS 2010 by simply updating the project references of the Microsoft.TeamFoundation.* assemblies to their TFS 2010 counterparts).</li>
</ul>

<h2>Final Note</h2>

<p>This is my first TFS custom control, therefore the code may contain mistakes. If that's the case, or if you have improvement suggestions, please drop me a comment.</p>

<h2>History</h2>

<ul> 
<li><strong>1 October, 2010:</strong> first version </li> 
</ul> 


<!-------------------------------    That's it!   --------------------------->
</body>
</html>

By viewing downloads associated with this article you agree to the Terms of Service and the article's licence.

If a file you wish to view isn't highlighted, and is a text file (not binary), please let us know and we'll add colourisation support for it.

License

This article, along with any associated source code and files, is licensed under The Code Project Open License (CPOL)


Written By
Software Developer SunHotels
Spain Spain
Under the secret identity of a C# programmer, a freaky guy who loves MSX computers and japanese culture is hidden. Beware!

Comments and Discussions