TFS 2010 – Custom Process Parameters Part 4 – Custom Types


In my previous posts, we discussed…

  1. Custom Process Parameters
  2. Process Parameter Metadata
  3. Custom Editors

In this post, I want to continue this discussion of process parameters and explain how to use your own custom types for process parameters. Previously, we used a String version number (i.e. “Beta2.0.30111.12”). But what if we would like to do something in our build based on part of the version number. For instance, we may want to deploy the first build for each day (i.e. revision number = 0). That would be very difficult with just a string. It would be easier if we stored the bits of the version number (Major, Minor, Date, and Revision). So let’s change our implementation!

Here is my VersionInfo class:

using System;

namespace CustomWorkflowTypes
{
    public class VersionInfo
    {
        public String Major { get; set; }
        public int Minor { get; set; }
        public int Date { get; set; }
        public int Revision { get; set; }

        public override string ToString()
        {
            return String.Format("{0}.{1}.{2}.{3}", Major, Minor, Date, Revision);
        }
    }
}

I also changed the dialog to use this new class:

using System;
using System.Windows.Forms;

namespace CustomWorkflowTypes
{
    public partial class SimpleVersionDialog : Form
    {
        public SimpleVersionDialog()
        {
            InitializeComponent();
        }

        public VersionInfo Version
        {
            get
            {
                return new VersionInfo()
                {
                    Major = textMajor.Text,
                    Minor = int.Parse(textMinor.Text),
                    Date = int.Parse(textDate.Text),
                    Revision = int.Parse(textRevision.Text)
                };
            }
            set
            {
                textMajor.Text = value.Major;
                textMinor.Text = value.Minor.ToString();
                textDate.Text = value.Date.ToString();
                textRevision.Text = value.Revision.ToString();
            }
        }
    }
}

A simple one line change to the SimpleVersionEditor class passes the new type to the dialog:

dialog.Version = value as VersionInfo;

And here is the XAML with a simple change to the type of the VersionNumber property:

<Activity x:Class="TfsBuild.Process" 
  xmlns="http://schemas.microsoft.com/netfx/2009/xaml/activities" 
  xmlns:mtbc="clr-namespace:Microsoft.TeamFoundation.Build.Client;assembly=Microsoft.TeamFoundation.Build.Client" 
  xmlns:mtbw="clr-namespace:Microsoft.TeamFoundation.Build.Workflow;assembly=Microsoft.TeamFoundation.Build.Workflow" 
  xmlns:mtbwa="clr-namespace:Microsoft.TeamFoundation.Build.Workflow.Activities;assembly=Microsoft.TeamFoundation.Build.Workflow" 
  xmlns:c="clr-namespace:CustomWorkflowTypes;assembly=CustomWorkflowTypes" 
  xmlns:s="clr-namespace:System;assembly=mscorlib" 
  xmlns:mva="clr-namespace:Microsoft.VisualBasic.Activities;assembly=System.Activities" 
  xmlns:this="clr-namespace:TfsBuild;" 
  xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml">
  <x:Members>
    <x:Property Name="VersionNumber" Type="InArgument(c:VersionInfo)" />
    <x:Property Name="Verbosity" Type="InArgument(mtbw:BuildVerbosity)" />
    <x:Property Name="Metadata" Type="mtbw:ProcessParameterMetadataCollection" />
    <x:Property Name="SupportedReasons" Type="mtbc:BuildReason" />
  </x:Members>
  <this:Process.Verbosity>[Microsoft.TeamFoundation.Build.Workflow.BuildVerbosity.Normal]</this:Process.Verbosity>
  <this:Process.Metadata>
    <mtbw:ProcessParameterMetadataCollection>
      <mtbw:ProcessParameterMetadata 
            Category="Basic" 
            Description="The Version number of the build." 
            DisplayName="Version Number"
            Editor="CustomWorkflowTypes.SimpleVersionEditor,CustomWorkflowTypes"
            ParameterName="VersionNumber" />
    </mtbw:ProcessParameterMetadataCollection>
  </this:Process.Metadata>
  <this:Process.SupportedReasons>All</this:Process.SupportedReasons>
  <mva:VisualBasic.Settings>Assembly references and imported namespaces serialized as XML namespaces</mva:VisualBasic.Settings>
  <Sequence>
    <mtbwa:WriteBuildMessage DisplayName="Version Number" Message="[VersionNumber.ToString()]" Importance="High" />
  </Sequence>
</Activity>

You may have also noticed the VisualBasic settings lines that I added. Those are needed so that the expression in the Message attribute can be evaluated by the Visual Basic Engine.

And that’s it. You just have to build it and deploy it like you did last time. Note that if a client can’t resolve the custom type, an error will appear to the user when they edit the definition.

That ends this series on Process Parameters. At least for now!

Comments (10)

  1. Alex Ivanoff says:

    I have a problem with persisting System.Version as custom parameter. Details are here: social.msdn.microsoft.com/…/94f12ff0-d223-4984-b1ef-9b34cf81ba3a. Could you help me figuring out what is going on?

  2. Brandon says:

    Do you have a posting on how to just create a basic listbox parameter?

    Just like the built in options "Logging Verbosity" or "Clean Workspace". You can select Normal, Detailed etc…

  3. NShah1215 says:

    Hi Jason,

    I had tried this example but I am getting the following error when I queue the workflow. I am able to create a build definition properly, but on queuing the build fails with following error:

    TF215097: An error occurred while initializing a build for build definition <Build Definition Name>: The type ‘InArgument(c:VersionInfo)’ of property ‘VersionNumber’ could not be resolved.

    Could you help me figure out what is going on?

  4. Hi,

    I read the whole posts about custom process parameters but cannot find an answer to the question I have.

    Is it possible to have a custom process parameter that is modified during a build (e.g. a number increased by x) and the result value is available in the next build run as value of this parameter?

  5. Hi Christin,

    I think the answer is no. It sounds like you want to modify the value of the process parameter on the definition so that the next build will have a new number. We don't really support this. However, if you use a SharedResourceScope to lock the definition, you could get the definition object in the build, and update the value of a process parameter. I have another post that gives an example of updating a process paramter.

    Good Luck,

    Jason

  6. Sundar Prasath says:

    Hi Jason,

    I am having problem with custom build parameters as array of [T] type. Can you help me to fix this?

    1.      I am using TFS2010 build upgrade template which internally uses TFSBuild.proj file

    2.      I have various custom activities and custom properties.

    3.      All custom activities and properties are there in one assembly and it was checked in and TFS version control path has been updated in build controller properties (Path to custom assembly)

    4.      In update template XAML file, I have added a custom argument.

    5.      This argument is “Array of [T]” type and in the browse window I am able to select the custom class from my custom assembly.

    6.      Meta data has been added to satisfy “edit build definition”.

    7.      I am able to see the property in my edit build definition and using collection editor I am able to add one or more items to the array.

    Now the issue is,

    When I trigger the build it fails stating below error,

    TF209001: An error occurred while processing ProjectStarted event for D:Builds21LSClientLoginUIBuildTypeTFSBuild.proj: Failed to create a 'Type' from the text 'c:CustomType'..

    TF270015: 'MSBuild.exe' returned an unexpected exit code. Expected '0'; actual '1'.

    Here MSIGen is the custom class which is part of custom assembly. When I search in the internet there are samples for “Array of [T]” with the type T is string. I do  not see any sample for custom array types.

    1.      I do not have any special code inside TFSBuild.proj but it only compiles the solution. It doesn’t do anything else.

    2.      If I remove the custom property build succeeded.

    3.      I have created custom property but no activities are using it. (We will have a custom activity use this but it fails before that)

    4.      TFS209001 seems to be null reference error but not sure why it is null even though it’s been created my TFS collection editor.

    5.      It looks like it works for single object but not for array of objects.

    Thanks,

    Sundar

  7. Hi Sundar,

    This seems like a complicated scenario. You should either contact the support services team or file a bug on our connect site. That's the best way to handle complex issues like this. To be honest, I never tried custom process parameters with the upgrade template. So, I have no idea what the problem might be.

    Thanks,

    Jason

  8. Sundar Prasath says:

    Hi Jason,

    Thanks for your quick reply. I got a hint from your reply and tried with default template and it seems to be working. Looks like this issue happens only with Upgrade template. Will try to contact support services.

    Thanks,

    Sundar

  9. Xabi says:

    Hello Jason,

    I'm using a simple List<T> as a Argument Type and it automatically adds a UI in order to fill those properties of T. It works fine but when editing at "Queue new build" time that list and removing list items or modifying a property of an item doesn't have any effect. It still stores those values of the list that it had at the beginning, the ones that I added editing the build definition. Obviously I know that because I print all list's values on screen and they keep unchanged. Do you have any idea why could be that?

    Thanks in advance,

    Xabi

  10. Hi Xabi,

    I have seen similar issues. We tried to fix some of them in Visual Studio 2012. The best fix is to create your own editor for your type and make sure that you always return a new instance of the list from the editor. The problem is that the property grid doesn't get any events when the list changes or an item in the list changes. It only notices a change if the entire list is recreated.

    Hope that helps,

    Jason