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

Improve performance TSWA

by Ewald Hofman 16. July 2009 23:49

When teams are adopting Team System Web Access, they get stuck in a known issue in Team System Web Access. It allocates a lot of memory that is not freed up. So over time you can see error messages that cannot be explained.

To improve the performance of your TSWA installation you can do the following things:

  • Make sure you have installed SP1; we have fixed a very significant memory leak there.
  • Create a designated app pool for TSWA application. Work item tracking consumes a lot of memory, so it is better not to share it with other applications.
  • Make sure you’re sharing metadata cache by specifying WorkItemTrackingCacheRoot parameter in the config file. Not doing that will result in creating a separate full copy of the metadata cache for each connected user.
  • Grant read/write permissions to the TSWA account to the HKLM\Software\Microsoft\VisualStudio\9.0 key and everywhere under it. Not having read/write access to these keys results in full metadata cache download every time new user connects to a server. That data gets thrown away because it is already in the cache in most cases.
  • Configure garbage collection for the http cache. I don’t know, however, whether http cache is used in 2008 version of TSWA, but at least it won’t hurt anyway. Here’s what we’re using at Microsoft:

    <caching>
       <cache disableMemoryCollection = "false" disableExpiration = "false" privateBytesLimit = "1000000000" percentagePhysicalMemoryUsedLimit = "60" privateBytesPollTime = "00:02:00"/>
    </caching>

  • Disable debugging in the web.config file. Having debugging turned on caused virtual memory leaks.
  • Finally, you can set the VM bytes limit to recycle the app pool to 1.9 GB; that will enable automatic recycling once the app pool consumes all available memory.

 

kick it on DotNetKicks.com

Tags:

TSWA | VSTS 2008

Powered by BlogEngine.NET 1.6.1.0
Theme by Mads Kristensen


ClusterMap

Statistics

Statistics created at 09 Sep 2009

121 posts
493 comments
328 raters
1949906 visit (1042 per day)
19 users online

Recent comments

Comment RSS