13
votes

I have forked a project with the code below:

var pc = ParentSection.GetService<IPendingChangesExt>();

var model = pc.GetType().GetField("m_workItemsSection", BindingFlags.NonPublic | BindingFlags.Instance);
var t = model.FieldType;
var mm = model.GetValue(pc);

var m = t.GetMethod("AddWorkItemById", BindingFlags.NonPublic | BindingFlags.Instance);

m.Invoke(mm, new object[] { selectedWorkItemId });

That adds an work item by its ID to the current pending changes.

Now, I want to link the work items choosing between "Associate" or "Resolve" (associate and resolve), depending on which button the user clicked on plugin's interface as below: enter image description here

If the user clicks on 'Associate and resolve', the work item should be associated and marked as resolved upon check-in.

If the user clicks on 'Associate only' the work item must be only linked to changeset, but not resolved.

Any help will be welcome

3

3 Answers

33
votes

This is not a property of WorkItem. This is the Check-in action on the work item. Details you can refer this link Changing the Default CheckIn Option to Associate in TFS 2012

You may need to use CheckinWorkItemAction Enumeration. More detail info from MSDN.

Similar question about TFS - VS Extension: Add work item to pending changes via API and also check this link: C# Programmatically Checking in code changes with TFS API while associating the changeset to a Work Item


Update

If you want to change the default state 'resolve' to 'associate', you need to

  • change the set the registry key HKEY_CURRENT_USER\Software\Microsoft\VisualStudio\1x.0\TeamFoundation\SourceControl\Behavior\ResolveAsDefaultCheckinAction to False.

  • Or For VS2015, there is an option Tools > Options > Source Control > Visual Studio Team Foundation > "Resolve associated work items on check-in".

enter image description here

Note: Both of above only affects your client machine.

Otherwise, for all users, you need to edit the Work Item Template definition for the types of work items you are using (Bug, Task, etc.). Details steps you can refer this question How to disable auto done status for task in checkin

3
votes

There is only one link type between Work Item and Changeset and the link information is stored in work item. The difference between "Associate" and "Resolve" in Visual Studio is that "Associate" only link the work item to changeset while "Resolve" will change the status of the work item to "Resolved" after linking the work item to changeset.

If you are developing your own plugin, you need to write code to achieve these features manually.

Following code shows how to link a work item to a changeset:

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using Microsoft.TeamFoundation.Client;
using Microsoft.TeamFoundation.VersionControl.Client;
using Microsoft.TeamFoundation.WorkItemTracking.Client;

namespace APPI
{
    class Program
    {
        static void Main(string[] args)
        {
            string url = "http://xxx.xxx.xxx.xxx:8080/tfs/DefaultCollection";
            TfsTeamProjectCollection ttpc = new TfsTeamProjectCollection(new Uri(url));
            WorkItemStore wis = ttpc.GetService<WorkItemStore>();
            VersionControlServer vcs = ttpc.GetService<VersionControlServer>();
            int wid = 82;
            int cid = 332;
            WorkItem wi = wis.GetWorkItem(wid);
            Changeset cs = vcs.GetChangeset(cid);
            ExternalLink el = new ExternalLink(wis.RegisteredLinkTypes["Fixed in Changeset"], cs.ArtifactUri.AbsoluteUri);
            wi.Links.Add(el);
            wi.Save();     
        }
    }
}
1
votes

Look, I've accomplished it, but I KNOW it's wrong:

After including the work item using the code on the question:

IPendingChangesExt pendingChangesExt = ParentSection.GetService<IPendingChangesExt>();

var workItemSection = pendingChangesExt.GetType().GetField("m_workItemsSection", BindingFlags.NonPublic | BindingFlags.Instance);

var modelType = workItemSection.FieldType;
var model = workItemSection.GetValue(pendingChangesExt);

var m = modelType.GetMethod("AddWorkItemById", BindingFlags.NonPublic | BindingFlags.Instance);                

m.Invoke(model, new object[] { selectedWorkItemId });

I've added some new code (those are different functions, ok?) ... this second call will wait for the work item appears on the Pending Changes 'Related Work Items' section, and will change its association from Resolve to Associate 'manually'.

IPendingChangesExt pendingChangesExt = ParentSection.GetService<IPendingChangesExt>();

var model = pendingChangesExt
                     .GetType()
                     .GetField("m_workItemsSection", BindingFlags.NonPublic | BindingFlags.Instance);

var modelType = model.FieldType;
var workItemSection = model.GetValue(pendingChangesExt);

var selectedWil = workItemSection
                              .GetType()
                              .GetProperty("SelectedWorkItems")
                              .GetValue(workItemSection) as ObservableCollection<WorkItemValueProvider>;

var availablWil = workItemSection
                              .GetType()
                              .GetProperty("WorkItemsListProvider")
                              .GetValue(workItemSection) as WorkItemsListProvider;

// Waiting for section to be ready to start association
while (!availablWil.WorkItems.Where(x => x.Id == selectedWorkItemId).Any())
{                    
    await System.Threading.Tasks.Task.Delay(25);
}

selectedWil.Clear();
selectedWil.Add(availablWil.WorkItems.Where(x => x.Id == selectedWorkItemId).First());

EnvDTE80.DTE2 dte2 = Package.GetGlobalService(typeof(DTE)) as DTE2;
dte2.ExecuteCommand("TeamFoundationContextMenus.WorkItemActionLink.TfsContextPendingChangesPageWorkItemActionLinkAssociate");

selectedWil.Clear();

Despite the effectiveness of this code, I'm still working for a better solution on running the second method. The 'default value' suggested on comments will not work, because the developer should be able to associate/resolve on choosing a button, only.