Create custom work item control for TFS Web Access 2010 (TWA)

by Ewald Hofman 10. August 2010 05:06

For the customization of the work item controls for the client are many examples. There is even a codeplex project that hosts some examples. However if you want to provide a custom work item control for Web Access, it turns out that there is only little information. In this post I will describe what I did to create the custom Web Access control.

Thanks to Guneet Umra of Avanade and Serkan Inci of Microsoft to help me out on this.

For this example I will show you how you can create a textbox that has a blue background, but you can make it as fancy as you like.

  1. Open Visual Studio and create a new Class library called WebControl.
  2. Add the references to
    1. C:\Windows\assembly\GAC_MSIL\Microsoft.TeamFoundation.WebAccess.Controls\10.0.0.0__b03f5f7f11d50a3a\Microsoft.TeamFoundation.WebAccess.Controls.dll
    2. C:\Windows\assembly\GAC_MSIL\Microsoft.TeamFoundation.WebAccess.WorkItemTracking\10.0.0.0__b03f5f7f11d50a3a\Microsoft.TeamFoundation.WebAccess.WorkItemTracking.dll
    3. C:\Program Files (x86)\Microsoft Visual Studio 10.0\Common7\IDE\ReferenceAssemblies\v2.0\Microsoft.TeamFoundation.WorkItemTracking.Client.dll
    4. C:\Program Files (x86)\Microsoft Visual Studio 10.0\Common7\IDE\PrivateAssemblies\Microsoft.TeamFoundation.WorkItemTracking.Controls.dll

      Or you can add the following lines to your project file:

          <Reference Include="Microsoft.TeamFoundation.WorkItemTracking.Client, Version=10.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a">
            <SpecificVersion>False</SpecificVersion>
            <Private>False</Private>
          </Reference>
          <Reference Include="Microsoft.TeamFoundation.WorkItemTracking.Controls, Version=10.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a">
            <SpecificVersion>False</SpecificVersion>
            <Private>False</Private>
          </Reference>
          <Reference Include="Microsoft.TeamFoundation.WebAccess.WorkItemTracking, Version=10.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a">
            <SpecificVersion>False</SpecificVersion>
          </Reference>
          <Reference Include="Microsoft.TeamFoundation.WebAccess.Controls, Version=10.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a">
            <SpecificVersion>False</SpecificVersion>
          </Reference>

  3. Create a wicc file, named BlueTextbox.wicc (for more information the wicc file, see http://msdn.microsoft.com/en-us/library/bb286959.aspx)
  4. Set the Build Action (in the properties window) of this file to Content (so it is included in the setup project that will be created later)
  5. Add the following contents to the wicc file

    <?xml version="1.0" encoding="utf-8" ?>
    <CustomControl xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:xsd="http://www.w3.org/2001/XMLSchema">
      <Assembly>WebControl.dll</Assembly>
      <FullClassName>WebControl.BlueTextbox</FullClassName>
    </CustomControl>
  6. Rename the Class1.cs file to BlueTextbox.cs
  7. Paste the following contents to the file

    using System;
    using System.Drawing;
    using Microsoft.TeamFoundation.WebAccess.WorkItemTracking.Controls;
    using System.Web.UI;
    using System.Web.UI.WebControls;

    namespace WebControl
    {
        public class BlueTextbox : BaseWorkItemWebControl
        {
            public BlueTextbox()
                : base(HtmlTextWriterTag.Div)
            {

            }

            /// <summary>
            ///  The textbox with the blue background
            /// </summary>
            public TextBox TextBox { get; set; }

            /// <summary>
            /// The value in the work item
            /// </summary>
            public string FieldValue
            {
                get
                {
                    // When it has a value, return that value
                    if (HasValidField && Field.Value != null)
                        return Field.Value.ToString();

                    // Else return the empty string
                    return string.Empty;
                }
                set
                {
                    // When the field is value and is editable
                    if (HasValidField && Field.FieldDefinition.IsEditable)
                        // And it is changed
                        if ((Field.Value != null || value != null)
                            && (Field.Value == null || Field.Value.ToString() != value))
                        {
                            // Set the value in the work item
                            base.OnBeforeUpdateDatasource(EventArgs.Empty);
                            Field.Value = value;
                            base.OnAfterUpdateDatasource(EventArgs.Empty);
                        }
                }
            }

            /// <summary>
            /// The value of the textbox
            /// </summary>
            public string Value
            {
                get
                {
                    EnsureInnerControls();

                    return TextBox.Text;
                }
                set
                {
                    EnsureInnerControls();

                    TextBox.Text = value;
                }
            }

            /// <summary>
            /// Build the control
            /// </summary>
            public override void InitializeControl()
            {
                base.InitializeControl();

                // Create the TextBox
                EnsureInnerControls();

                // Save the value when it is changed
                TextBox.TextChanged += delegate { FlushToDatasource(); };
            }

            /// <summary>
            /// Add the textbox
            /// </summary>
            public void EnsureInnerControls()
            {
                if (TextBox != null)
                    return;

                TextBox = new TextBox { BackColor = Color.Blue };

                base.Controls.Clear();
                base.Controls.Add(TextBox);
            }

            /// <summary>
            /// THe underlying data is changed. Change the contents of the TextBox accordingly
            /// </summary>
            public override void InvalidateDatasource()
            {
                base.InvalidateDatasource();

                // If the field is valid
                if (HasValidField)
                {
                    // And it has a value
                    if (Field.Value != null)
                    {
                        // change the contents of the TextBox
                        Value = Field.Value.ToString();
                    }
                    else
                    {
                        // else clear the value
                        Clear();
                    }
                }
                else
                {
                    // If the field is not valid, then also clear the contents of the TextBox
                    Clear();
                }

                // Ensure the control is shows correctly
                ResetStyles();
            }

            /// <summary>
            /// Ensure that the control is correctly shows (width + enabled)
            /// </summary>
            private void ResetStyles()
            {
                Width = Unit.Percentage(100);
                Enabled = !ControlReadOnly && !EditorReadOnly;
            }

            public override void FlushToDatasource()
            {
                base.FlushToDatasource();

                FieldValue = Value;
            }

        }
    }
  8. To distribute this control, the easiest way is to create a setup project. Since the location is different for x86 and x64 the easiest way is to create two setup projects.
  9. First create the x64 setup project: Add a new project of type Setup Project with the name WebControlSetup.x64 (you can find the project type in the Add New Project dialog under Other Project Types –> Setup and Deployment –> Visual Studio Installer)

    image
  10. Remove the User’s Desktop and the User’s Programs Menu folders
  11. Click on the project in the Solution Explorer and open the properties window. Change the TargetPlatform to x64
  12. Change the DefaultLocation of the Application Folder to [ProgramFiles64Folder]\Microsoft Team Foundation Server 2010\Application Tier\Web Access\Web\App_Data\CustomControls
  13. Right click on the right pane to add the Primary output of the WebControl project

    image 
  14. When you add the primary output also all the Microsoft.TeamFoundation dll’s are added. Those dll’s should not be installed however

    image
  15. You can remove those by opening up the Detected Dependencies, select those dll’s, right click to open the context menu and choose Exclude. You will see the dll’s will be removed from the File System window

    image
  16. Now also add the Content of the WebControl project
  17. Repeat these steps to create a x86 setup project, but now use the target folder “[ProgramFilesFolder]\Microsoft Team Foundation Server 2010\Application Tier\Web Access\Web\App_Data\CustomControls”
  18. You can now build the WebControl solution, and install the appropriate msi on the TFS server.
  19. Now change the work item type to make use of the new control. You can find information how to modify work item types at http://msdn.microsoft.com/en-us/library/ms243849.aspx
  20. When you change the FieldControl of the Title field in the FORMS section to BlueTextbox,

        <FORM>
          <Layout>
            <Group>
              <Column PercentWidth="80">
                <Control FieldName="System.Title" Type="BlueTextbox" Label="&amp;Title:" LabelPosition="Left" />
              </Column>
    you will see the following when you create a new task

    image
  21. There is a nice little feature in TFS to have two separate layouts for the Windows and the Web UI: You can add the attribute Target=”Windows” or Target=”Web” to the Layouts node to show two different layouts.

        <FORM>
          <Layout Target="WinForms">
            <Group>
              <Column PercentWidth="80">
                <Control Type="FieldControl" FieldName="System.Title" Label="&amp;Title:" LabelPosition="Left" />
              </Column>


          <Layout Target="Web">
            <Group>
              <Column PercentWidth="80">
                <Control Type="BlueTextbox" FieldName="System.Title" Label="&amp;Title:" LabelPosition="Left" />
              </Column>

Tags:

Work items | VSTS 2010 | TSWA

Comments

10/21/2010 8:36:07 AM #

Steve

Hi, I enjoyed the examplel very much and think it will be very helpful... however I am having issues location the webaccess dlls for version 10.  Where can I download these at?  I looked on the server where the tfswa is installed and do not see them anywhere.  Thanks for help

Steve United States |

10/21/2010 11:05:40 AM #

Ewald Hofman

When you install TFS 2010 on your machine, you get those dll's

Ewald Hofman Netherlands |

10/21/2010 11:22:50 AM #

Steve

We already installed TFS 2010 on the server, web access is already set up and we have been using work items through the web access. But we are unable to get to these dll's.

We had 2008 which had the bin directory but we are not seeing that inside 2010's web access folder after doing the upgrade. We can see them in the GAC on the development server but a physical search on the server gives us nothing.

Our development environment is not on the same machine as the TFS server. Does that matter? Could we access form the GAC if we had a development environment on the server??

Thanks for the help

Steve United States |

10/21/2010 11:24:39 AM #

Ewald Hofman

You can find these dll's on the TFS server. You can just copy those dll's to your local machine and (preferribly) add them in a subfolder of your solution.

Ewald Hofman Netherlands |

10/21/2010 11:59:04 AM #

Steve

Hmmm... yes, we have searched on the server.. I would think they would be in Application Tier\Web Access\Web\bin like they were in 2008 however they are not there.. I have also done global search on the server as well with no results (except of course in the GAC where I cant get them).  I wonder if I put VS on the server if I could access them though VS then?  I guess we have a busted install or something then.  Any other ideas?  

Thanks for the help

Steve United States |

10/21/2010 12:01:12 PM #

Ewald Hofman

Are those files not at

C:\Windows\assembly\GAC_MSIL\Microsoft.TeamFoundation.WebAccess.Controls\10.0.0.0__b03f5f7f11d50a3a\Microsoft.TeamFoundation.WebAccess.Controls.dll
C:\Windows\assembly\GAC_MSIL\Microsoft.TeamFoundation.WebAccess.WorkItemTracking\10.0.0.0__b03f5f7f11d50a3a\Microsoft.TeamFoundation.WebAccess.WorkItemTracking.dll
C:\Program Files (x86)\Microsoft Visual Studio 10.0\Common7\IDE\ReferenceAssemblies\v2.0\Microsoft.TeamFoundation.WorkItemTracking.Client.dll
C:\Program Files (x86)\Microsoft Visual Studio 10.0\Common7\IDE\PrivateAssemblies\Microsoft.TeamFoundation.WorkItemTracking.Controls.dll

Ewald Hofman Netherlands |

10/21/2010 12:05:25 PM #

Steve

I see entries in C:\Windows\assembly for them (with Processor Architecture set to MSIL), however I cant click on them to open them.... they have a weird icon on them like a service icon in a box).. From what I understand, which may not be correct, is that you cant access them without possible hacking the registry or something like that.. I do have the last 2 in the locations you mention above.

Steve United States |

10/21/2010 12:14:41 PM #

Steve

AH... got them... From command prompt, I see the structure you have above but not from windows explorer... Strange... Anyways, many thanks for your help and time

Steve United States |

10/21/2010 12:34:18 PM #

Ewald Hofman

the assembly is the GAC. You can go into the folder structure of the GAC throught the command prompt. Thats why

Ewald Hofman Netherlands |

10/21/2010 12:44:12 PM #

Steve

Got it working now, after changing virtual directory to .NET 4.0.  Thanks for the help!

Steve United States |

10/21/2010 1:05:41 PM #

Ewald Hofman

You can also target your web app to .Net 3.5. The TWA is targeted for .net 3.5, so I don't know whether everything in TWA will keep working!

Ewald Hofman Netherlands |

12/14/2010 7:38:28 AM #

Steve

Hello Again Ewald... I was wondering if you could give me some advice on another issue I'm having... I have my custom controls finished for thick client and am now porting to the web... With your previous help, I can now do this, however I can not figure out how to get some of the functionality... Basically what I would like to do is I have a gridview (custom control) that should display selections based on the selected area (MS control)... I would like to update gridview when the area is changed, without having to do postback.. This was easy for thick client as I just listened to the FieldChanged event... Any idea how I can get this functionality on the web??  I assume it needs to be done with javascript, but how do I get access to a control already on the form and listen for an event? I have looked everywhere and nobody can seem to help. Many thanks.

Steve United States |

12/15/2010 1:47:40 PM #

Ewald Hofman

In the web, the FieldChanged event is only available on the server. So what you are trying to do is not possible in the web.

Sorry for that.

Ewald Hofman Netherlands |

1/4/2011 6:18:09 AM #

John Miles

Steve, I'm doing something similar- in that I want one of my fields to be updated within the custom control when another is set. I've done this by registering some javascript with the web page (during the 'EnsureInnerControls' function in the above example) which will handle the event ('onkeyup' in my case) on the client side.

My Web Access work item control is still a work-in-progress (I'm struggling to get the values saved back to the server...), but the client-side javascript bit seems to work OK!

Maybe this will help you:

// Use the page's script manager to add some javascript to the page to handle the events
ClientScriptManager scriptManager = this.Page.ClientScript;
string changeScript = "";
changeScript += "function MyValueChange(){ ";
changeScript += "        document.getElementById('c_we_ctl41_TEXTBOX_2_ID').value= 'New Value';";
changeScript += "}";

scriptManager.RegisterClientScriptBlock(this.GetType(), "MyValueChangeScript", changeScript, true);

// Add 2 text boxes to the control, hook the event so that when the first changes, the second is also changed
base.Controls.Clear();

TextBox txtMyTextBox1 = new System.Web.UI.WebControls.TextBox();
txtMyTextBox1.ID = "TEXTBOX_1_ID";
txtMyTextBox1.Attributes.Add("onkeyup", "MyValueChange()"); // Add a javascript event handler to the onkeyup event
base.Controls.Add(txtMyTextBox1);

TextBox txtMyTextBox2 = new System.Web.UI.WebControls.TextBox();
txtMyTextBox1.ID = "TEXTBOX_2_ID"; // Note the ID has "c_we_ctl41_" in front when it is rendered in the web page- not sure why or if this is always the same... could be problematic..
base.Controls.Add(txtMyTextBox2);

John Miles United Kingdom |

1/4/2011 9:03:37 AM #

John Miles

Hi Ewald,

Thanks for this great article- it's pretty much the only thing of use I can find on the internet about web-based Work Item controls!

I've created a nice custom control which actually edits a number of fields within the same control (it's going to be used for timesheet entry within the work items), and it is working great apart from the fact that I'd like certain sub-controls within the control to be cleared once the user clicks save on the work item.

I was expecting the InvalidateDatasource function to be called again once the page has been saved to reset the control values, but this doesn't appear to be the case. Do you know if there are any other functions to override or any other way of achieving this?

Regards,

John

John Miles United Kingdom |

4/16/2011 3:24:12 AM #

Nick Hoggard

Hi Ewald,

Just wanted to say thanks for a really helpful article - this was a great headstart for porting my existing TFS custom controls to TSWA.

Thanks!

Nick

Nick Hoggard New Zealand |

5/9/2011 5:52:52 AM #

pandian.s

Hi Ewald,

I done steps up to 20...after I search the my control type in Type combo from the layout tab.my contol type is not comming ...can u tell me y? I am using vs2010

pandian.s United States |

5/10/2011 3:06:29 AM #

Raghavendra

Hi Edwald,

1.  I have done all the steps from 1 to 18.
2.  When i have installed the setup project the Webcontrol.dll file and BlueTextbox.cs file has copied to the locaion c:\Programfiles\Microsoft Team Foundation Server 2010\\Application Tier\Web Access\Web\App_Data\CustomControls location.
3.  As a next step I had copied BlueTexbox.wicc file to the same location.
4.  I started to customize the One of the work Item (Task) in TFS using VS 2010 power tool (Process template editor : Create a new field, add new control to Layout) where I was not able to see the BlueTextbox Type.
As alternative I had copied both WebControl.dll and BlueTextbox.wicc file to following location.
C:\ProgramData\Microsoft\Team Foundation\Work Item Tracking\Custom Controls\10.0.
Once the above step is done, I was able to see the BlueTextbox type in the layout section properties tab.
==============================================================================
After all these efforts I am getting error when try to create a new work item task in the project portal and web access as below detail.
Error: Unable to create ‘BlueTextbox’
Microsoft.TeamFoundation.WebAccess.WorkItemTracking.Controls.WorkItemFormException: Unable to load custom control manifest for ‘BlueTextbox’ at
Microfost.TeamFoundation.Webaccess.WorkItemTracking.Controls.Control.ControlFactory.GetCustomControlType(String controlName) at
Microsoft.TeamFoundation.webAccess.WorkItemTracking.Control.ControlFactory.GetControlType(ControlDefinition controldefinition) at
Microsoft.TeamFoundation.WebAccess.WorkItemTracking.Control.ControlFactory.CreateCustomControl(ControlID)

Please let me know how to correct this.
Thank you in advance.

Raghavendra India |

5/10/2011 4:23:16 AM #

Ewald Hofman

I don't use the power tool. I add the field/control by editing the xml.
I have no clue how you can support this in the power tools.

You could try to post your question on the MSDN forums

Ewald Hofman Netherlands |

5/11/2011 12:15:36 AM #

Raghavendra

Hi Ewald,

Thank you for quick response.

Even i have tried editing the task.xml file and import to TFS, which result the same error message. Can you please share me the sample xml file containing both <layout Target="web"> and <layout Target="windows"> tags.

Raghavendra India |

5/20/2011 2:54:44 AM #

Sen

Hi Ewald,

Thanks a lot for the post. It solved many questions on my mind.
I have a clarification. Is it possible to validate field values of two work items and perform some operation. For example, it should pop up a message if a user tries to close Parent task state to closed without changing child task states to closed. Is this implementation feasible.

Thanks in advance,

regards,
Sen

Sen India |

Comments are closed

Powered by BlogEngine.NET 1.6.1.0
Theme by Mads Kristensen


ClusterMap

Statistics

Statistics created at 09 Sep 2009

121 posts
492 comments
326 raters
1911627 visit (1042 per day)
24 users online

Recent comments

Comment RSS