Add a new Item to your Workflow, In the solution explorer select your workflow and add a new item to it. With your workflow selected hit (ctrl+shift+a) or
now with your "Add New Item" pane open select the "Workflow Association Form (Farm Solution only)" option.
Open up your workflow elements file, and take a look at the new AssociationURL attribute added to the workflow.
it should have a relative Url to the newly added form
<?xml version="1.0" encoding="utf-8" ?>
<!-- Customize the text in square brackets.
Remove
brackets when filling in, e.g.
Name="[NAME]"
==> Name="MyWorkflow" -->
<Elements xmlns="http://schemas.microsoft.com/sharepoint/">
<Workflow
Name="Computer Access Workflow"
Description="My SharePoint Workflow"
Id="6582541c-eb11-4904-9d35-1394b463f158"
CodeBesideClass="POC_WorkFlow.CA_Workflow.CA_Workflow"
CodeBesideAssembly="$assemblyname$"
AssociationUrl="_layouts/POC_WorkFlow/CA_Workflow/CA_AssociationForm.aspx">
<Categories/>
<MetaData>
<AssociationCategories>List</AssociationCategories>
<!--
Tags to specify InfoPath forms for the workflow; delete tags for forms that you
do not have -->
<!--<Association_FormURN>[URN
FOR ASSOCIATION FORM]</Association_FormURN>
<Instantiation_FormURN>[URN FOR
INSTANTIATION FORM]</Instantiation_FormURN>
<Task0_FormURN>[URN FOR TASK (type
0) FORM]</Task0_FormURN>
<Task1_FormURN>[URN FOR TASK (type
1) FORM]</Task1_FormURN>-->
<!--
Modification forms: create a unique guid for each modification form -->
<!--<Modification_[UNIQUE
GUID]_FormURN>[URN FOR MODIFICATION FORM]</Modification_[UNIQUE
GUID]_FormURN>
<Modification_[UNIQUE
GUID]_Name>[NAME OF MODIFICATION TO BE DISPLAYED AS A LINK ON WORKFLOW
STATUS PAGE</Modification_[UNIQUE GUID]_Name>
-->
<StatusPageUrl>_layouts/WrkStat.aspx</StatusPageUrl>
</MetaData>
</Workflow>
</Elements>
Pretty simple, but lets add some functionality to our form, open up the association forms aspx page. The form I'm working on requires specifying a primary and secondary AMANDA admin.
<%@ Assembly Name="$SharePoint.Project.AssemblyFullName$" %>
<%@ Assembly Name="Microsoft.Web.CommandUI,
Version=14.0.0.0, Culture=neutral, PublicKeyToken=71e9bce111e9429c" %>
<%@ Import Namespace="Microsoft.SharePoint" %>
<%@ Import Namespace="Microsoft.SharePoint.ApplicationPages" %>
<%@ Register Tagprefix="SharePoint" Namespace="Microsoft.SharePoint.WebControls" Assembly="Microsoft.SharePoint, Version=14.0.0.0,
Culture=neutral, PublicKeyToken=71e9bce111e9429c" %>
<%@ Register Tagprefix="Utilities" Namespace="Microsoft.SharePoint.Utilities" Assembly="Microsoft.SharePoint, Version=14.0.0.0,
Culture=neutral, PublicKeyToken=71e9bce111e9429c" %>
<%@ Register Tagprefix="asp" Namespace="System.Web.UI" Assembly="System.Web.Extensions,
Version=3.5.0.0, Culture=neutral, PublicKeyToken=31bf3856ad364e35" %>
<%@ Page Language="C#"
DynamicMasterPageFile="~masterurl/default.master"
AutoEventWireup="true"
Inherits="POC_WorkFlow.CA_Workflow.CA_AssociationForm"
CodeBehind="CA_AssociationForm.aspx.cs" %>
<asp:Content ID="Main" ContentPlaceHolderID="PlaceHolderMain" runat="server">
<div>
<div style="width:300px; display:inline-block; margin-bottom:20px"">
Primary Amanda Admin<br />
<SharePoint:PeopleEditor id="PrimaryAmandaAdmin_PPL" runat="server" IsValid="true"
AllowEmpty="false" Height="20px" Width="200px" AllowTypeIn="true" MultiSelect="false" />
</div>
<div style="width:300px; display:inline-block; margin-bottom:20px">
Secondary Amanda Admin<br />
<SharePoint:PeopleEditor id="SecondaryAmandaAdmin_PPL" runat="server" IsValid="true"
AllowEmpty="false" Height="20px" Width="200px" AllowTypeIn="true" MultiSelect="false" />
</div>
</div>
<div style ="margin-top:50px;">
<asp:Button ID="AssociateWorkflow" runat="server" OnClick="AssociateWorkflow_Click" Text="Associate Workflow" />
<asp:Button ID="Cancel" runat="server" Text="Cancel" OnClick="Cancel_Click" />
</div>
</asp:Content>
<asp:Content ID="PageTitle" ContentPlaceHolderID="PlaceHolderPageTitle" runat="server">
Computer Access Association Form
</asp:Content>
<asp:Content ID="PageTitleInTitleArea" runat="server" ContentPlaceHolderID="PlaceHolderPageTitleInTitleArea">
Computer Access Association Form
</asp:Content>
with the UI complete create a serializable class called AssociationData, now since we are going to use xml as our transportation format there is no need for the serializable attribute.
public class AssociationData
{
public string PrimaryAmandaAdmin{ get; set; }
public string SecondaryAmandaAdmin { get; set; }
public AssociationData(){ }
public AssociationData(string primaryAmandaAdmin)
{
this.PrimaryAmandaAdmin = primaryAmandaAdmin;
}
public AssociationData(string primaryAmandaAdmin, string secondaryAmandaAdmin)
: this(primaryAmandaAdmin)
{
this.SecondaryAmandaAdmin = secondaryAmandaAdmin;
}
}
//
This method is called when the user clicks the button to associate the
workflow.
private string GetAssociationData()
{
string xml = string.Empty;
string PrimaryAmandaAdmin = string.Empty;
string SecondaryAmandaAdmin = string.Empty;
if(PrimaryAmandaAdmin_PPL.Accounts.Count > 0)
PrimaryAmandaAdmin =
PrimaryAmandaAdmin_PPL.Accounts[0].ToString();
if(SecondaryAmandaAdmin_PPL.Accounts.Count > 0)
SecondaryAmandaAdmin =
SecondaryAmandaAdmin_PPL.Accounts[0].ToString();
AssociationData data = string.IsNullOrEmpty(SecondaryAmandaAdmin) ?
new AssociationData(PrimaryAmandaAdmin) : new AssociationData(PrimaryAmandaAdmin, SecondaryAmandaAdmin);
XmlSerializer xmlSerializer = new XmlSerializer(typeof(AssociationData));
using (StringWriter writer = new StringWriter())
{
xmlSerializer.Serialize(writer, data);
xml =
writer.ToString();
}
// TODO:
Return a string that contains the association data that will be passed to the
workflow. Typically, this is in XML format.
return xml;
}
<?xml version="1.0" encoding="utf-16"?>
<AssociationData xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:xsd="http://www.w3.org/2001/XMLSchema">
<PrimaryAmandaAdmin>DOMAIN\smithj</PrimaryAmandaAdmin>
<SecondaryAmandaAdmin>DOMAIN\doej</SecondaryAmandaAdmin>
</AssociationData>
In my previous post I talked about attaching a workflow from the event receiver, which is great for testing and even production if you want to minimize manual setup, but the problem is now that you have an association form the association values have to be set. To pull this off go back into your event receiver and pass your default values from code rather then your form. Let's do just that open up your event receiver and take a look.
public override void FeatureActivated(SPFeatureReceiverProperties properties)
{
Guid workflowID = new Guid("6582541c-eb11-4904-9d35-1394b463f158");
SPSite site =properties.Feature.Parent as SPSite;
if (site != null)
{
SPWeb web = site.RootWeb;
// OTB
List for Tasks
SPList taskList = web.Lists["Workflow
Tasks"];
// OTB
Hidden List
SPList historyList = web.Lists["Workflow
History"];
// List
our workflow gets attached to
SPList caList = web.Lists["List1"];
SPWorkflowTemplate wfTemplate = web.WorkflowTemplates[workflowID];
SPWorkflowAssociation wfAssociation =
SPWorkflowAssociation.CreateListAssociation(wfTemplate, "Computer Access",
taskList, historyList);
// TODO add Association Data XML
wfAssociation.AllowManual = true;
wfAssociation.AutoStartCreate = true;
wfAssociation.AutoStartChange = true;
caList.WorkflowAssociations.Add(wfAssociation);
wfAssociation.Enabled = true;
}
}
wfAssociation.AssociationData
= @"<?xml
version='1.0' encoding='utf-16'?><AssociationData
xmlns:xsi='http://www.w3.org/2001/XMLSchema-instance'
xmlns:xsd='http://www.w3.org/2001/XMLSchema'><PrimaryAmandaAdmin>DOMAIN\user1</PrimaryAmandaAdmin><SecondaryAmandaAdmin>DOMAIN\user2</SecondaryAmandaAdmin></AssociationData>";
notice that it's just the xml from before when we serialized our AssociationData class. Now a better approach would probably be to create the class using constructors and serialize just as you did before.