Getting started using Custom Work Item Controls

Since Service Pack 1 [1] we have the ability to create our own controls to display field value within a work item form which gives us great flexibility.


However there are two caveats with custom work item controls right now that aren’t solved:



  • They do not work with Web Access [2] so it’s not a choice for people that heavily rely on the web client.
  • They need to be deployed to the machine of every user accessing TFS.


Setting the stage


As an first realistic example we could make the priority field a little prettier. I mean the allowed values “1”, “2”, and “3” are not very descriptive. We don’t want to create a new field or change the values since we used this field already. So the solution would be to create a custom work item control on top of the existing values that displays something else.


I plan to create a new control that has a combobox on it and will display something nicer than the real values that are stored in the database. The complete source code is attached for reference.


1. Create the Work Item Control


A custom work item control is essentially a regular Windows Forms control. It has to derive from Control and implement IWorkItemControl.


The loading takes place in IWorkItemControl.InvalidateDatasource, the saving in IWorkItemControl.FlushToDatasource. The bold code indicates peaces that were necessary to implement my wishes. The rest is infrastructure and is repeated for every control you’ll write.


Here’s the code:


using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Drawing;
using System.Data;
using System.Text;
using System.Windows.Forms;
using Microsoft.TeamFoundation.WorkItemTracking.Controls;
using Microsoft.TeamFoundation.WorkItemTracking.Client;

namespace TeamSystemPro.Samples.WorkItemControls
{
    /// 
    /// Work Item Control for displaying the priority (can be 1,2, or 3 in MSF) in a more descriptive way then just showing the numbers.
    /// 
    /// 
    /// Custom controls need to derive from Control.
    /// Typically custom controls are built from UserControl.
    /// The control needs to implement IWorkItemControl.
    /// This is defined in Microsoft.TeamFoundation.WorkItemTracking.Controls.dll assembly that can be found from VS installation folder, typically ":\Program Files\Microsoft Visual Studio 8\Common7\IDE\PrivateAssemblies".
    /// Add reference to that assembly and line "using Microsoft.TeamFoundation.WorkItemTracking.Controls;" in code.  
    /// How it works:
    ///   After the control is loaded, a reference to work item is passed to the control using IWorkItemControl.WorkItemDatasource property.
    ///   The control is asked to display its data using IWorkItemControl.InvalidateDatasource method.
    ///   The control is required to update any values directly in WorkItem object itself when user changes data. 
    /// 
    public partial class PriorityControl : UserControl, IWorkItemControl
    {
        private WorkItem workItem;
        private string workItemFieldName;
        private IServiceProvider serviceProvider;
        private System.Collections.Specialized.StringDictionary properties;
        private bool readOnly = false;

        public PriorityControl()
        {
            InitializeComponent();
        }

        #region IWorkItemControl Members

        /// 
        /// Raise this events before updating WorkItem object with values. When value is changed by a control, work item form asks all controls (except current control) to refresh their display values (by calling InvalidateDatasource) in case if affects other controls 
        /// 
        public event EventHandler BeforeUpdateDatasource;

        /// 
        /// Raise this event after updating WorkItem object with values. When value is changed by a control, work item form asks all controls (except current control) to refresh their display values (by calling InvalidateDatasource) in case if affects other controls 
        /// 
        public event EventHandler AfterUpdateDatasource;

        /// 
        /// Control is asked to clear its contents
        /// 
        void IWorkItemControl.Clear()
        {
            cboPriorities.SelectedIndex = -1;
        }

        /// 
        /// Control is requested to flush any data to workitem object. This usually happens during save operation or when the form is left. In most cases data will be written to workitem immediately on change and hence this will not need implementation. Some customers want a way to do operations during save, and this is the closest thing we got. If you do need a way to react to before-save & after-save events, pls let us know in forums given below and we'll consider for future revision.
        /// 
        void IWorkItemControl.FlushToDatasource()
        {
            this.BeforeUpdateDatasource(this, EventArgs.Empty);
            workItem.Fields[workItemFieldName].Value = Convert.ToString(cboPriorities.SelectedIndex + 1);
            this.AfterUpdateDatasource(this, EventArgs.Empty);
        }

        /// 
        /// Asks control to invalidate the contents and redraw. At this point, control can read from work item object and display/refresh data.
        /// 
        void IWorkItemControl.InvalidateDatasource()
        {
            if (IsInitialized())
            {
                int currentValue = (int)workItem.Fields[workItemFieldName].Value;
                cboPriorities.SelectedIndex = currentValue - 1;
            }
        }

        /// 
        /// A property bag of all attributes specified in work item form xml for this control. Custom attributes are allowed and can be used to pass parameters specific for this control from work item type xml.
        /// 
        System.Collections.Specialized.StringDictionary IWorkItemControl.Properties
        {
            get { return properties; }
            set { properties = value; }
        }

        /// 
        /// Tells the control to display in readonly mode.
        /// 
        bool IWorkItemControl.ReadOnly
        {
            get { return readOnly; }
            set { readOnly = value; OnReadOnlyChanged(); }
} /// /// Gives pointer to IServiceProvider if you intended to access Document service or VS Services. If services are not needed, do nothing in this method. /// /// void IWorkItemControl.SetSite(IServiceProvider serviceProvider) { this.serviceProvider = serviceProvider; } /// /// This passes reference to source work item object. Cast this object to WorkItem type. /// object IWorkItemControl.WorkItemDatasource { get { return workItem; } set { workItem = value as WorkItem; } } /// /// The field name if the control is associated with a field name in work item form xml. A custom control can be associated with 0 or 1 work item field. /// string IWorkItemControl.WorkItemFieldName { get { return workItemFieldName; } set { workItemFieldName = value; } } #endregion private bool IsInitialized() { return (!this.IsDisposed && workItem != null && !string.IsNullOrEmpty(workItemFieldName) && workItem.Fields.Contains(workItemFieldName)); } private void OnReadOnlyChanged() { cboPriorities.Enabled = !readOnly; } } }

Additionally you need to create an XML file with the .wicc extensions which contains the name of your assembly file as well as the full-qualified name (i.e. namespace and classname) of your control class:


CustomWorkItemControls5


So you have your control as well as the .wicc-file (which is necessary for registration):


CustomWorkItemControls2


2. Deploy the Work Item Control


Although you could simply copy the two files (.dll and .wicc) to the “Microsoft\Team Foundation\Work Item Tracking\Custom Controls” under Environment.SpecialFolder.CommonApplicationData (we could also use the folder unter then under Environment.SpecialFolder.LocalApplicationData), but I decided to create a new MSI setup with Visual Studio which might look like this:


CustomWorkItemControls3


3. Reference the new control in a Work Item Type


After the control is deployed just change the type attribute for the control in the work item type XML. The Type should reference the name of the .wicc-file.


CustomWorkItemControls4


Now you can import your modified work item type XML (you need to use witimport as the Process Template Editor does not support custom controls right now), refresh the cache by right-clicking the Work Items node in Team Explorer and press “Refresh”.


CustomWorkItemControls1


Further recommended reading:


  • How to use Custom Controls in Work Item Form [3]
  • Communicating between custom controls: Building parent/child controls [4]

Enjoy increasing your Work Item UI experience through custom controls!


-Neno


[1] http://msmvps.com/blogs/vstsblog/archive/2006/12/15/download-visual-studio-2005-team-suite-team-foundation-server-service-pack-1-sp1.aspx


[2] http://www.devbiz.com


[3] http://blogs.msdn.com/narend/archive/2006/10/02/How-to-use-Custom-Controls-in-Work-Item-Form.aspx


[4] http://blogs.msdn.com/narend/archive/2006/10/19/communicating-between-custom-controls-building-parent-child-controls.aspx


File Attachment: WorkItemCustomControlDemo.zip (25 KB)

One thought on “Getting started using Custom Work Item Controls”

  1. Hello,

    First of all thank you for this article.

    I have a problem to deploy custom work item control.

    I’m able to create custom work item control. No problem about it.

    But, I don’t know how to deploy it. I use Windows Server 2008. If I use one custom work item control from internet, I’m also not sure how to deploy it. I copied both DLL and WICC files to this location:

    C:\ProgramData\Microsoft\Team Foundation\Work Item Tracking\Custom Controls\10.0.

    After that, I’ve modified XML definition file for that specific work item and I think it’s wrong:

    < ?xml version="1.0" encoding="utf-8"?>


    GTK Work Item.
























    As you can see, there’s custom “CheckboxedOptionsControl” FIELD entry and appropriate “Control” entry. I don’t think it’s proper, but I’m not sure how to fix it.

    When I create new work item from Team Explorer, the form appears and there are 2 text fields. First one is “Title” field and it’s ok, but instead of custom form with check boxes I see new disabled text field with this error message:

    Could not find file ‘C:\ProgramData\Microsoft\Team Foundation\Work Item Tracking\Custom Controls\10.0\Ekobit.VSTSTools.CustomWIControls.CheckboxedOptionsControl.wicc’.

    How can I make it work?

    Thank you in advance.

    Goran

Leave a Reply

Your email address will not be published. Required fields are marked *

You may use these HTML tags and attributes: <a href="" title=""> <abbr title=""> <acronym title=""> <b> <blockquote cite=""> <cite> <code> <del datetime=""> <em> <i> <q cite=""> <strike> <strong>