There’s plenty of documentation available on MSDN and from other sources that discusses how to create custom controls to be used by TFS Work Item Type (WIT) definitions. I was working with a customer recently and I needed to customize a work item to show a button that would launch a custom user interface. The custom UI would create an associated Code Review work item and a shelveset from the user’s current pending changes. I thought there might be an existing implementation of a custom WIT button control but alas, after scouring the web and other resources I couldn’t find any. So I started down the path of writing my own.
As mentioned above there is already a wealth of documentation detailing how to create custom controls, so I won’t get into most of those details. Instead I’ll focus on the code for the button control and how you can re-use the control to add similar functionality to your work item types.
The implementation turns out to be pretty straight forward. I create a new control library and add a new control that derives from UserControl and implements the IWorkItemControl interface. The IWorkItemControl interface implementation is shown below.
1: protected IServiceProvider _serviceProvider;
2: void IWorkItemControl.SetSite(IServiceProvider serviceProvider)
3: {
4: _serviceProvider = serviceProvider;
5: }
6:
7: protected WorkItem _workItem;
8: object IWorkItemControl.WorkItemDatasource
9: {
10: get { return _workItem; }
11: set { _workItem = (WorkItem)value; }
12: }
13:
14: public event EventHandler AfterUpdateDatasource;
15:
16: public event EventHandler BeforeUpdateDatasource;
17:
18: public void Clear()
19: {
20: }
21:
22: public void FlushToDatasource()
23: {
24: }
25:
26: public void InvalidateDatasource()
27: {
28: }
29:
30: public System.Collections.Specialized.StringDictionary Properties
31: {
32: get { return _properties; }
33: set { _properties = value; }
34: }
35:
36: public bool ReadOnly
37: {
38: get { return _readonly; }
39: set { _readonly = value; }
40: }
41:
42: public string WorkItemFieldName
43: {
44: get { return _fieldName; }
45: set { _fieldName = value; }
46: }
With the interface implemented, I then simply add a new button control to the custom control with some default settings as shown.
1: private Button _button;
2:
3: public WitButton()
4: {
5: InitializeComponent();
6: InitControl();
7: }
8:
9: private void InitControl()
10: {
11: if (_button != null)
12: return;
13:
14: _button = new Button();
15: _button.Text = "Default Text";
16: _button.Width = 125;
17: _button.Top = 10;
18: _button.Click += new EventHandler(_button_Click);
19:
20: base.Controls.Clear();
21: base.Controls.Add(_button);
22: }
Finally, I expose the Text and Width properties of the internal button control so that they can be set by any derived controls. I also expose an event that is fired when the button is clicked.
1: public string ButtonText
2: {
3: get { return _button.Text; }
4: set { _button.Text = value; }
5: }
6:
7: public int ButtonWidth
8: {
9: get { return _button.Width; }
10: set { _button.Width = value; }
11: }
12:
13: public delegate void ButtonClickHandler(object sender, EventArgs args);
14: public event ButtonClickHandler ButtonClick;
15:
16: void _button_Click(object sender, EventArgs e)
17: {
18: if (ButtonClick != null)
19: ButtonClick(sender, e);
20: }
That’s it for the base control, which by itself won’t do much, so there is no *.wicc file necessary at this point.
Now I need to create a derived control that will set the base control’s button text and width accordingly and handle the ButtonClick event to actually perform some work when the user clicks the button. A simple example of this is shown in the next snippet.
1: public partial class MyButton : WitButton
2: {
3: public MyButton()
4: {
5: InitializeComponent();
6: this.ButtonText = "Do Something";
7: this.ButtonWidth = 150;
8: this.ButtonClick += new ButtonClickHandler(MyButton_ButtonClick);
9: }
10:
11: void MyButton_ButtonClick(object sender, EventArgs args)
12: {
13: MessageBox.Show("Button was clicked");
14: }
15: }
With the code in place I create a *.wicc file named MyCustomWitButton and deploy it along with the binaries for my custom control and the base control to Environment.SpecialFolder.CommonApplicationData\Microsoft\Team Foundation\Work Item Tracking\Custom Controls\10.0\ (there are multiple folders which are probed to locate custom WIT controls as explained here). Now I can add the derived button to my Bug work item type definition (no field name or label is required for the button control).
After saving the work item type definition I can now create a new Bug within the team project and I should get the following result.
Finally I can get to the original problem I was trying to solve, which was using a button from the work item form to launch a custom UI to create an associated Code Review work item and shelveset. I created a WPF application and launch it on the ButtonClick event. I am also leveraging the protected _workitem and _serviceProvider members from the base control to access the associated Bug work item and the TFS work item DocumentService respectively. The results are shown below.
I’ve created a project on CodePlex where you can download the source for the base control and a sample derived control.