Saturday, September 22, 2007

Custom work item controls

[Update, March 31, 2010: Gregg Boer writes here on work item custom controls and covers new things introduced in VS 2010.]
Introduction
Custom work item controls are used to implement the functionalities not offered by the set of standard controls used in the work item form (edit box, combo box, datetime, html control and the special ones like area/iteration). They are available in Team System as of SP1 for TFS 2005.
In this article we will describe what the custom controls are and how they can be implemented and used. We will develop a custom control, the Checkboxed options control, which will accept and show multiple values by showing a row of checkboxes.



Checkboxed options controlRelated reading
There are no many sources of information on custom work item controls on the Internet. So, I might as well list all currently available relevant sources:
The
official msdn documentation[1] is rather brief, so you might like to start with the first ever published article on custom controls [2] written by Naren Datha of Microsoft. It is not too long either but covers everything from the reasoning for the custom controls to the deployment. Recently, a colleague of mine, VSTS MVP Neno Loje published the Custom work item controls primer [3] on his blog, a description of the first steps needed to build and use a work item custom control. Last but not least, you should visit the semi-official place to find publicly available custom controls on Codeplex [4]

Why would you build a custom control?There are many reasons why you would use custom controls. If you want to provide richer UI or provide some additional functionality in the work item form, custom controls are the way to go. One case of such better UI would be graphical representation of the severity level data in the form of traffic lights instead of just numbers from 1 to 3. Custom controls can enable more elegant way to insert screenshots as attachments [5] or to do some mathematical operation with the work item’s data, evaluate a formula e.g. sum the estimates, different parts of the team have given for a task, in order to get the total estimate. For some further ideas on possible uses of custom controls see [7].
Currently there is no way to put multiple data in one work item field other than to use a custom control. One such case is the Checkboxed options control we demonstrate in this article.
You can implement custom controls using Windows Form controls or even use ActiveX controls wrapped as Windows Form controls.
In which cases would it be wrong to use custom controls? You shouldn’t try to use them in order to implement additional work item rules [6], since Excel or MS Project don’t support custom controls. Your rules would not be evaluated in at least these two environments and it is thus better not to raise false expectations and to refrain from implementing rules through custom controls.

Restrictions of the custom controlsThere are several restrictions you should have in mind before deciding to use the work item custom controls:
The binaries for the custom control must be locally installed on each client computer. Visual Studio assumes the binaries are already available and does nothing itself to provide for them. If the required files are missing you will simply get an empty box instead of the control. Hint: There is no requirement for Admin rights to install custom control binaries.
This is the major problem, since it hinders easy adoption. Until a good way to centrally manage and easily distribute the binaries is found, custom controls will have only limited importance.
· Only Visual Studio and other environments that use standard work item edit form support the custom controls. See the section “Custom controls and different client environments (TSWA, Excel, MS Project)” below.

Basic interfaces/basics of a custom controlsCustom controls must derive from

System.Windows.Forms.Control class, have a no-argument constructor and implement the IWorkItemControl interface.
The IWorkItemControl interface looks like this:

public interface IWorkItemControl
{
StringDictionary Properties { get; set; }
bool ReadOnly { get; set; }
object WorkItemDatasource { get; set; }
string WorkItemFieldName { get; set; }

event EventHandler AfterUpdateDatasource;
event EventHandler BeforeUpdateDatasource;

void Clear();
void FlushToDatasource();
void InvalidateDatasource();
void SetSite(IServiceProvider serviceProvider);
}
The Properties property gives access to all the attributes defined for the control in the work item type definition. Custom, control specific attributes are allowed here as well. This is the way to set the configuration data for your control.
The ReadOnly property is used to tell the control that it should be displayed as read/only. See also the section “Making a custom control read-only” below.
The WorkItemDataSource property is the reference to the work item object. Should be cast to WorkItem class. Using the Fields property of the work item you can access any field of the current work item.
The WorkItemFieldName property holds the name of the work item field this control is associated with. A control can be associated with 0 or 1 field.
The AfterUpdateDatasource and BeforeUpdateDatasource events should be raised when you update the fields in the work item so that other controls can refresh themselves if needed.
Clear should reset the content of the control.
FlushToDatasource can usually be safely ignored since you’ll directly change the work item object as soon as the control’s content is edited.
InvalidateDatasource – in this method the control should use the data from the work item object and other possible data sources to redraw itself.
SetSite makes IServiceProvider interface available that can be used to access VS services.
So, the life of a custom control in terms of properties and methods of the IWorkItemControl interface can be summarized like this:
A control edits a field named in WorkItemFieldName of the work item object whose reference is in WorkItemDataSource. In InvalidateDatasource the control redraws itself, using the dana from the work item and probably using one or more attributes from the Properties property bag. As soon as the user edits the control's content, events AfterUpdateDatasource and BeforeUpdateDatasource should be raised. If the ReadOnly is set, the control should make itself read only.
Once you've implemented this interface you're ready to use the control in the xml work item type definition.


Using custom controls in the work item type xml definitionsOnce you've developed a custom control, it is time to use it in the work item definition. In order to do that, it needs to be included in the work item's layout. In the work item's xml definition, the design of the work item form is defined by the
Layout element [8].



Each control on the work item form is defined by the Control element [9] below the Layout element.
The only required attribute of the Control element is the Type attribute. Originally, there was a fixed list of possible values this attribute can have: FieldControl (meaning: the standard control, edit box or the combo box, depending on the content), DateTimeControl, HtmlFieldControl, WorkItemClassificationControl (used for Areas and Iterations) and the three special “controls” that are in fact the whole tabs in the standard work item layouts - LinksControl, AttachmentControl and WorkItemLogControl.
With the introduction of custom controls the definition of the Type attribute was extended. If it contains something other than one of previously listed predefined values, this value is expected to be the name of a custom control class.
Usually you will set at least one optional attribute – FieldName. It defines the name of the work item field bound to the control; the value defined with this attribute will be given to the custom control in its WorkItemFieldName property.
Other optional attributes are Label, LabelPosition, ReadOnly, Dock, Padding and Margin.
As we will see later, whatever other attributes you set, they will be available to your control through IWorkItemControl.Properties property bag. Once the Type attribute is used to instantiate the custom control, it is the responsibility of the control (that means, your custom code) to interpret the meaning of all other attributes. Since it is the sole responsibility of your control to interpret the attributes, as long as the control understands them, custom attributes can be used as well. In our case we will use the attribute Options to define the names of the options the Checkboxed options control will display.

There is a bad news related to editing the xml definitions of the work item types – you will need to edit them by hand. Since the
Team System Process Editor (PTE) [10] doesn't support custom controls, there is no other choise. If you really like PTE and would still like to work with custom controls, one possible workaround [11], would be to have two versions of the work item definition, the working one and the final one. The working copy contains a placeholder standard control for each instance of a custom control. This version can be edited and previewed in PTE. Once, when you are satisfied with this version, using find and replace and the text editor or some script you replace each placeholder with appropriate custom control Control tag, thus creating the final version. The final version is the one that should be uploaded (witimported) to the server and actually used.
That said, I prefer editing work item xml definitions by hand, since I am usually more efficient that way, but if you’re unsure what exactly needs to be done or just feel more secure doing it using a tool, with the above workaround, PTE can be of use.


Work Item Custom Control (.wicc) files and deploymentHow does Visual Studio know where to find the implementation of the custom control?
It uses .wicc (Work Item Custom Control) files. These contain the fully qualified name of your control class and the name of the assembly containing the implementation. The name of the file is the name you should use as the control’s type name (Type attribute of the Control element) in the work item definition.
The file used for our Checkboxed options control looks like this:



But where should I put the .wicc file and the implementation of the custom control?
Visual Studio looks for them in the “Microsoft\Team Foundation\Work Item Tracking\Custom Controls“ folder under Environment.SpecialFolder.CommonApplicationData folder first, then under Environment.SpecialFolder.LocalApplicationData.



In order to enable side by side installation of custom controls compiled with VS 2005 and those compiled with VS 2008 libraries, in Orcas, the search path was extended, as described in [
12]. In Orcas, VS will first look in the subfolder 9.0 of the Custom controls folder in order to find Orcas specific versions. So the Orcas versions of the custom controls (these are the ones compiled with Orcas versions of the Work Item object model .dlls) should be put in “Microsoft\Team Foundation\Work Item Tracking\Custom Controls\9.0“.
You can xcopy deploy your files to above folders or make a .msi file that does that. Attached “
Checkboxed options.zip” file contains the VS setup project that generates the appropriate .msi file. The .msi contains only two files – the .wicc file and the .dll containing the implementation of the custom control.



Checkboxed options controlThe Checkboxed options control uses the CheckedListBox to show the list of checkboxes.
Let’s go through the control’s initialization code:
We check whether there is an Option attribute in the xml definition for the control, parse the content of the attribute and initialize the ListBox entries
//insert the list entries
if (!m_properties.ContainsKey("options"))
{
throw new InvalidFieldValueException(
"Entries for the Checkboxed options control in the work item not defined. " + Environment.NewLine +
"Add Options attribute to the appropriate element in the work item definition.", null);
}
string[] names = m_properties["Options"].Split(m_Separator);
this.m_listBox.Items.AddRange(names);
m_listBox.ColumnWidth = MeasureListItemWidth(names);
Then we ignore the standard control attributes while parsing all other custom attributes and use them to initialize the control:

//set other properties
foreach (string propName in m_properties.Keys)
{
switch (propName)
{
//ignore the non custom properties
case "fieldname":
case "label":
case "labelposition":
case "options":
case "type":
break;
default:
{
try
{
string propValue = m_properties[propName];
PropertyDescriptorCollection properties = TypeDescriptor.GetProperties(m_listBox);
//names of the properties are case insensitive
PropertyDescriptor pd = properties.Find(propName, true);
if (pd == null)
Trace.WriteLine("Property " + propName + " with value " + propValue + " not found.");
else
{
pd.SetValue(m_listBox, pd.Converter.ConvertFrom(propValue));
}
}
catch (Exception e)
{
Trace.WriteLine("Setting " + propName + " failed:\r\n" + e.Message);
}
}
break;
}
Members m_workItem, m_fieldName, m_properties are bound to the IWorkItemControl.WorkItemDatasource , IWorkItemControl.WorkItemFieldName and IWorkItemControl.Properties respectively.
When the user clicks on one of the checkboxes the new value for the complete “encoded” content of the control is calculated and saved directly into the work item:


void m_listBox_ItemCheck(object sender, System.Windows.Forms.ItemCheckEventArgs e)
{
...
StringBuilder sb = new StringBuilder();
for (int i = 0; i < m_listBox.Items.Count; i++) { if (i == e.Index) { if (e.NewValue == CheckState.Checked) sb.AppendFormat("{0}{1}", GetInternalRep(m_listBox.Items[i].ToString()), m_Separator); } else { if (m_listBox.GetItemCheckState(i) == CheckState.Checked) sb.AppendFormat("{0}{1}", GetInternalRep(m_listBox.Items[i].ToString()), m_Separator); } } string value = sb.ToString(); OnBeforeUpdateDatasource(EventArgs.Empty); m_workItem.Fields[m_fieldName].Value = value; OnAfterUpdateDatasource(EventArgs.Empty); }



The function GetInternalRep adds angle brackets around the value of a field. In the end, if the control looks as in the picture at the beginning of the article, the value "[WXP Pro SP2];[Vista];" is saved in the work item field.

Querying
Use "Contains" clause in the work item query tool to query for one specific option set. Following query returns all work items having "Vista" checkbox checked.


Debugging

The easiest way to debug the control is to use another instance of Visual Studio and to attach it’s debugger to the first VS instance where the work item form containing the custom control is displayed.

Making a custom control read-only

In order to make your custom control read only, check value of the work item field’s IsEditable property in InvalidateDatasource event and set your control’s edit state accordingly. This is how the native controls like FieldControl, DateTimeControl handle read-only state.

Custom controls and the different client environments (TSWA, Excel, MS Project)
MS Excel and MS Project don’t support custom controls. Excel and Project will show the textual representation of the data instead, which is ugly but sometimes still usable. Usually though, editing the custom control field data in Excel or Project directly, might damage the data consistency, so it should be avoided. Current version of the TSWA (Team System Web Access) doesn’t support the custom controls, at least not in a documented way. Good news is that the next version of the TSWA (the one coming with the Orcas wave) will support custom controls. That said, you need to have in mind that TSWA will not be able to reuse the WinForms implementation of a custom control; it will need a custom web forms based one.
Checkboxed options control: Source, Setup

References:
[1] Official MSDN documentation,
http://msdn2.microsoft.com/en-us/library/bb286959(VS.80).aspx [2] Naren Datha’s blog entry on custom work item controls, http://blogs.msdn.com/narend/archive/2006/10/02/How-to-use-Custom-Controls-in-Work-Item-Form.aspx
[3] Neno Loje’s article: Getting started using Custom Work Item Controls,
http://msmvps.com/blogs/vstsblog/archive/2007/07/07/starting-using-custom-work-item-controls.aspx
[4] Codeplex project TFS Work Item Tracking Custom Controls,
http://www.codeplex.com/WitCustomControls
[5] TFS Work Item Screenshot Custom Control,
http://olausson.net/blog/PermaLink,guid,8d7a24a9-b1b4-463c-a037-769c33aabfb9.aspx
[6] MSDN documentation: Available Field Rules,
http://msdn2.microsoft.com/en-us/library/ms194953(VS.80).aspx
[7] Naren Datha's introduction to the custom controls,
http://blogs.msdn.com/narend/archive/2006/09/27/773025.aspx
[8] Layout Element (Work Item Type Definition Schema),
http://msdn2.microsoft.com/en-us/library/aa337635(VS.80).aspx
[9] Control Element (Work Item Type Definition Schema),
http://msdn2.microsoft.com/en-us/library/aa337625(VS.80).aspx
[10] Team System Process Editor as one of the tools in the Microsoft Visual Studio 2005 Team Foundation Server Power Tool,
http://www.microsoft.com/downloads/details.aspx?familyid=7324c3db-658d-441b-8522-689c557d0a79&displaylang=en
[11] Developer discussion on Codeplex: Problems with new Process template Editor and Custom Control,
http://www.codeplex.com/WitCustomControls/Thread/View.aspx?ThreadId=7676
[12] How to port Whidbey custom controls to Orcas ,
http://blogs.msdn.com/narend/archive/2007/08/23/how-to-port-whidbey-custom-controls-to-orcas.aspx



3 Comments:

Blogger Cartic said...

Hi Ogy,
Many thanks for your wonderful blog..!!
I have gone through your blog and have downloaded the source code.
I am unclear about the setup process of the CheckboxedOptions.wicc.
I have actually copied the file into my ApplicationData\Microsoft\TeamFoundation\CustomControls folder.
Is that all that is required for deploying the CheckboxedOptions control? or have I missed any steps in the deployment?
Thanks!!!

Thu Mar 25, 12:55:00 PM GMT+1  
Blogger Ognjen Bajic said...

Hi Karthik,
you should deploy both the wicc and the dlls implementing the cutom control in either
Environment.SpecialFolder.CommonApplicationData\Microsoft\Team Foundation\Work Item Tracking\Custom Controls\9.0\
or

Environment.SpecialFolder.LocalApplicationData\Microsoft\Team Foundation\Work Item Tracking\Custom Controls\9.0\

Wed Mar 31, 04:50:00 AM GMT+2  
Blogger Eduardo7095 said...

This worked for me to at least centralize the controls to use from VS:

1. Go to command line
2. CD into \Microsoft\Team Foundation\Work Item Tracking
3. Copy all the content of "Custom COntrols" into a shared folder (i.e. \\server\tfscc
4. Delete Custom Controls folder
5. MKLINK /D "Custom Controls" \\server\tfscc

Open VS and your custom controls render ok.

Thu May 05, 10:34:00 PM GMT+2  

Post a Comment

<< Home