Search code examples
visual-studiovisual-studio-2012tfsvisual-studio-2015tfs-sdk

How to Associate an work item as "Associate" or "Resolve" in Visual Studio TFS API (VS2015)?


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


Solution

  • 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.