Monday, 23 August 2010

Use SharePoint as Source for External Content Type

In SharePoint 2010 there are a lot of possible sources for External Content Types. Next to the default ones (WCF, Web Service, SQL), SP allows the creation of custom BDC Models using Visual Studio 2010. By using a custom Model you can use any source that you can access with Code.

In the following example, I will create a custom BDC Model for using a SharePoint List as a Source.

Create Visual Studio Project

  1. Create a new Visual Studio Project of Type "Business Data Connectivity Model".

image

Note: Remember that SharePoint 2010 uses .NET Framework 3.5 and not 4

  1. Enter the url and "Deploy as a farm solution"
  2. Visual Studio will generate
  • a Package, which is deployed to SharePoint
  • a Feature, which is added to the Package
  • a default BDC Model, a xml description of the Entity (There is also a visual editor)
  • a default Entity, a code description of the Entity

Create xml Entity

Visual Studio knows 3 visual ways to adapt the xml Entity:

  1. The BDC Designer enables you to define the entities in your model and to visually arrange their relationships with one another.
    image 
  2. Use the BDC Method Details window to define the parameters, instances, and filter descriptors of a method.

    image

  3. The BDC Explorer displays the elements that make up the model.
    image 

Step 1

Remove all default created methods in the Method Details windows.

image

Step 2

Rename the Entity to "Customer"

Rename the default Identifier to anything that suits your needs, I decided to use the ItemID of type System.Int32. This field will hold the unique ItemID of any SharePoint Item.

image image

Step 3

In the "BDC Method Details" view generate a "Specific Finder" Method for reading an item in the list. The parameters for the method will be automatically generated for you.

image

Step 4

Adapt the Customer TypeDescriptor in the BDC Explorer. Add TypeDescriptor fields for each field that you want to have in each customer entity e.g. FirstName (String), LastName (String) and of course the identifier ItemID (Integer).

Since ItemID is the field related to your identifier, you have to set additional properties for that TypeDescriptor:

  • 'Identifier' Property -> select ItemID (the link to the identifier value in the entity)
  • 'Read-only' Property -> set to True (since ItemID will be linked to the SharePoint ItemID in the source, we do not want the user to be able to change this value)

image

After adapting one Customer TypeDescriptor, these changes will be used for any newly generated method.

Step 5

Create methods for the other operations: 'Finder' (Read List), 'Creator' (Create Item), 'Updater' (Update Item) and 'Deleter' (Delete Item)

In the Create Method adapt the NewCustomer TypeDescriptor by removing the ItemID TypeDescriptor, since we don't want the user to be able to enter a value for this field.

Create Code Entity

In the Solution Explorer you can find a code file called "Entity1.cs". In this file the code is stored that links the fields defined in the xml entity to code fields.

Step 1

Create a property for each of the fields that you want to have for each customer. e.g.

public int ItemID { get; set; }
public string LastName { get; set; }
public string FirstName { get; set; }

Create Code for Service

In the Solution Explorer you can find a code file called "CustomerService.cs". In this file the code is stored that reads the items from any source and stores them in BDC Entities.

Step 1

At the beginning of the class you will find the example methods, which were generated with the project. These can be deleted. Only keep the methods that have "throw
new System.NotImplementedException();
" in their body.

Step 2

In my example I used the SharePoint Object Model for retrieving data from SharePoint. I generated a method that would create a Customer Entity on basis of a SharePoint List Item that contains the information about a Customer:

private static Customer generateCustomer(SPListItem customerItem) {
Customer myCustomer = new Customer();
myCustomer.ItemID = customerItem.ID;
myCustomer.FirstName = customerItem["First Name"].ToString();
myCustomer.LastName = customerItem["Last Name"].ToString();
return myCustomer;
}

Step 3

Write the code for the different methods:

ReadItem(int itemID)

Customer myCustomer = null;
using (SPSite oSite = new SPSite(http://sp2010.tripleaconnected.com/personal/KS)){
    using (SPWeb oWeb = oSite.OpenWeb()) {
        SPList oList = oWeb.Lists["Contacts"];
        if (oList != null) {
SPListItem customerItem = oList.GetItemById(itemID);
if (customerItem != null) {
myCustomer = generateCustomer(customerItem);
}
        }
    }
}
return myCustomer;

ReadList()

List<Customer> customers = new List<Customer>();
using (SPSite oSite = new SPSite(http://sp2010.tripleaconnected.com/personal/KS)){
using (SPWeb oWeb = oSite.OpenWeb()) {

SPList oList = oWeb.Lists["Contacts"];

foreach(SPListItem customerItem in oList.Items){
customers.Add(generateCustomer(customerItem));
}
}
}
return customers;

Create(Customer newCustomer)

Customer returnCustomer = null;
using (SPSite oSite = new SPSite(http://sp2010.tripleaconnected.com/personal/KS)){
using (SPWeb oWeb = oSite.OpenWeb()) {

SPList oList = oWeb.Lists["Contacts"];

SPListItem customerItem = oList.AddItem();
customerItem["First Name"] = newCustomer.FirstName;
customerItem["Last Name"] = newCustomer.LastName;
customerItem.Update();
SPListItem returnCustomerItem = oList.GetItemByID(customerItem.ID);

if (returnCustomerItem != null) {
returnCustomer = generateCustomer(returnCustomerItem);
}
}
}
return returnCustomer;

Update(Customer customer)

using (SPSite oSite = new SPSite(http://sp2010.tripleaconnected.com/personal/KS)){
using (SPWeb oWeb = oSite.OpenWeb()) {

SPList oList = oWeb.Lists["Contacts"];

SPListItem customerItem = oList.GetItemById(customer.ItemID);

if (customerItem != null) {
customerItem["First Name"] = customer.FirstName;
customerItem["Last Name"] = customer.LastName;
customerItem.Update();
}
}
}

Delete(int itemID)

using (SPSite oSite = new SPSite
("http://sp2010.tripleaconnected.com/personal/KS")) {

using (SPWeb oWeb = oSite.OpenWeb()) {

SPList oList = oWeb.Lists["Contacts"];

SPListItem customerItem = oList.GetItemById(itemID);

if (customerItem != null) {
customerItem.Delete();
}
}
}

Note:

When writing this code file see the Best Practices for Error Handling: http://msdn.microsoft.com/en-us/library/ff464398.aspx

Deploy

You can now deploy the project and create a new External List based on this BDC Model.

Note:

When trying to deploy a Business Data Connectivity Model with Visual Studio 2010 you might get the following error:

Error occurred in deployment step 'Add Solution': Property 'SiteUrl' contains an invalid URL

Solution:

Add a property to the Feature.xml File:

<Properties>
<Property Key="SiteUrl" Value="http://sp2010.tripleaconnected.com">
</Properties>

Tuesday, 10 August 2010

Accessing rows in repeating groups in Browser Enabled Forms

There are not so many differences in versions of XPath between regular forms and web enabled ones. But inability to reference rows in web enabled forms by the index is one of the most annoying.

  1. The current row index expression:
    count(preceding-sibling::*[local-name() = "MyRepeatingGroup"])
    As you can see that formula successfully substitutes the position() function not available in web forms.
  2. Accessing value of the previous row:
    preceding-sibling::my:MyRepeatingGroup[count(preceding-sibling::my:MyRepeatingGroup) = count(current()/preceding-sibling::my:MyRepeatingGroup) - 1]/my:FieldToAccess

Conditional Default Values in InfoPath

Sometimes you want the default value of a field to be dependent upon a condition. However, there isn't any direct functionality to support IF statements in the default values of fields. Substituting a rule for a default value only gets you so far, as the rule is only applied when that field that changes, not the fields that the rule depends on. Updating the field whenever any dependent field is changed would require you to copy the rule to each field. This is not very maintainable, so below I will describe two approaches to avoiding this.

The first approach is simple, but it has some limitations and caveats. The second approach is more complicated, but should work in all cases.

Method 1: Using the union and array indexer operators

The first approach is to use the union operator '|' along with an array indexer '[]' to select the proper value:

(TrueResult | ElseResult) [(BoolCondition) + 1]

There are two caveats to using this approach:

  1. The node set will always be returned in document order. It does not matter what the order is in the parenthesis, as (field1 | field2) == (field2 | field1). Since you cannot change the node set ordering, you may have to modify your BoolCondition to be NOT-ed. For more information on document order, you can visit the w3.org page on XPaths.
  2. Inside of the parenthesis, you must only have nodes; you cannot have strings or any other type. So (field1 | "hello world") will not work.

Method 2: Using concat, substring and string-length

To overcome these caveats, you can use the second approach here.  That is to use concat, substring and string-length.  For example, the same generic if statement from the previous approach converts to

concat(

substring(TrueResult, 1, (BoolCondition) * string-length(TrueResult)),

substring(ElseResult, 1, (not(BoolCondition)) * string-length(ElseResult)))

Friday, 23 July 2010

Uploading a Page Layout for SharePoint 2010 in Visual Studio 2010

Step 1

Open Visual Studio and create a new C# project, using the "Empty SharePoint Project" template, listed under the SharePoint 2010 project templates. For this example, we'll name our project "MyCustomPageLayout".

image

For this example, we'll be deploying our solution as a "Sandboxed solution".

image 

Click "Finish" to wire up your solution to your particular SharePoint site.

Step 2

As with SharePoint 2007, we'll need to provision our Page Layouts to our SharePoint Site Collection Master Page Gallery by using a "Module" element. To do this, right click on the Project and say you want to add a new item. Select "Module" from the list of SharePoint 2010 items.

 image 
We'll name the Module "masterpage". When you create the new module, you'll see something like this:

image

By default, the module contains a sample text file you, so you can see how the Module deploys a file to SharePoint. However, we don't need it, so we'll delete Sample.txt. Next, we need to wire up our module to point to the URL of the Master Page Gallery, so we'll add a "Url" attribute to the Module node. Our Elements.xml file should now contain this XML:

<?xml version="1.0" encoding="utf-8"?>
<Elements xmlns="http://schemas.microsoft.com/sharepoint/">
  <Module Name="masterpage" Url="_catalogs/masterpage">
  </Module>
</Elements>

Step 3

Next, we're going to drop our new Page Layout file into the Module. To do this, we'll right-click on the "masterpage" module in the Visual Studio explorer and select "Add existing item". Browse to your Page Layout and select it.

Step 4

Next, we'll need to update the Module's Elements.xml file to tell SharePoint what to do with the new Page Layout.

The first thing we'll need to do is to update the "Url" property of the File element, by removing the "masterpage/" prefix fromt the Url. (Since we already specified in the Module element that we're deploying the Page Layout to the "masterpages" library, if we left the "masterpages/" part of the Url on the file, SharePoint would try to provision our Page Layout into a library called "masterpages" inside the existing "masterpages" library! We don't want that!) Your new File element should look like this:

<File Path="masterpage\MyCustomPageLayout.aspx" Url="MyCustomPageLayout.aspx" />

Now we need to specify some properties for the Page Layout. These properties are identical to the kind of properties you would specify for a Page Layout in SharePoint 2007:

  • The Title of the file in SharePoint
    In our case, the file itself has a URL of MyCustomPageLayout.aspx, but we can also give the page a Title that will show up when the Page Layout is being selected by the content author, such as "My Custom Page Layout".
  • The Content Type: All Page Layouts must inherit from the "Page Layout" content type (which we add using a Resource file value.)
  • A Publishing Preview Image: The Publishing Preview Image is the image that shows up when a user picks that Page Layout from the Page Layout picker while they're creating a new page. In this example, we'll just point to the Article Page's preview image.
  • The Publishing Associated Content Type: This is the Content Type that the Page Layout is representing, such as an "Article Page", "Welcome Page", etc. The value is a string representing a Lookup value that's a combination of the name of the Content Type and its unique identifier.

Our new File node will look like this:

<File Path="masterpage\MyCustomPageLayout.aspx" Url="MyCustomPageLayout.aspx" Type="GhostableInLibrary">
      <Property Name="Title" Value="My Custom Page Layout" />
      <Property Name="ContentType" Value="$Resources:cmscore,contenttype_pagelayout_name;" />
      <Property Name="PublishingPreviewImage" Value="~SiteCollection/_catalogs/masterpage/$Resources:core,Culture;/Preview Images/CustomPageLayout.png, ~SiteCollection/_catalogs/masterpage/$Resources:core,Culture;/Preview Images/CustomPageLayout.png" />
      <Property Name="PublishingAssociatedContentType" Value=";#$Resources:cmscore,contenttype_articlepage_name;;
#0x010100C568DB52D9D0A14D9B2FDCC96666E9F2007948130EC3DB064584E219954237AF3900242457EFB8B24247815D688C526CD44D;#"
/>
</File>

Please note, there's no line break inside the PublishingAssociatedContentType value string; I needed to add a line break so you could see the whole value here

That's it! We've built our Module.

Step 5

Now, we're going to add a module for the Page Layout preview image. Right click on the project and add an additional Module element. Delete the "Sample.txt" file from it, and add your new preview image. (In our case, we'll call it CustomPageLayout.png".) Modify your module file so it looks like this:

<Module Name="Previews Images" Url="_catalogs/masterpage/$Resources:core,Culture;/Preview Images">
  <File Path="Preview Images\CustomPageLayout.png" Url="CustomPageLayout.png" Type="GhostableInLibrary">
      <Property Name="Title" Value="Custom Page Layout Preview Image" />
  </File>
</Module>

You might have noticed that a new Feature got created for you when you created a new Module. Let's rename the Feature itself MyCustomPageLayout in the project tree, like this:

 image

Step 6

We'll open up our MyCustomPageLayout feature, and give it a helpful name and description. Notice that you can graphically see that your Module is a part of this Feature, and you can even see which Module files will be provisioned when the Feature is activated.

image

Step 7>

The last thing we need to do is to make sure our Solution Package is getting configured correctly, to include our new Feature.

By default, the "Empty SharePoint Project" will create a DLL for you. If we just went ahead and deployed our Solution Package with the settings as-is, we would be deploying a DLL to the GAC. However, since our solution doesn't have any code in it, it would be a blank DLL. That's a bit messy, so let's avoid that. Click on the Project in your Solution Explorer, and look in the Properties Window. Change the "Include Assembly in Package" property to "False".

image

Step 8

To deploy our new Solution Package to our Sandboxed Site, we'll go to the "Build" menu in Visual Studio, and select "Deploy MyCustomPageLayout".

 image

This will assemble the Solution Package, deploy the Solution Package to the Sandbox, then activate the Sandboxed feature.

If we browse to our Master Page Library, we'll now see our new Page Layout. Note that, unlike SharePoint 2007, our Page Layout will appear checked out by default, if we haven't modified any of the approval or check-out settings of the out of the Master Page Gallery. We'll need to check it in and approve it for it to be available to us.

How to create a custom Page Layout using SharePoint Designer

Scenario:

You're working with the Enterprise Wiki Site Template and you don't really like where the "Last modified…" information is located (above the content). You want to move that information to the bottom of the page.

Option 1: Modify the "EnterpriseWiki.aspx" Page Layout directly.

Option 2: Create a new Page Layout based on the original one and then modify that one.
image

We'll go ahead and go with Option 2 since we don't want to modify the out of the box template just in case we need it later on.

How To:

Step 1

Navigate to the top level site of the Site Collection > Site Actions > Site Settings > Master pages (Under the Galleries section). Then switch over to the Documents tab in the Ribbon and then click New > Page Layout.

image 

Step 2

Select the Enterprise Wiki Page Content Type to associate with, give it a URL and Title. Note that there's also a link on this page to create a new Content Type. You might be interested in doing this if you wanted to say, add more editing fields or metadata properties to the layout. For example if you wanted to add another Managed Metadata column to capture folksonomy aside from the already included "Wiki Categories" Managed Metadata column.

image

Step 3

SharePoint Designer time! Hover over your newly created Page Layout and "Edit in Microsoft SharePoint Designer."

image

Step 4

Now you can choose to build your page manually by dragging your SharePoint Controls onto the page and laying them out as you'd like…
image


… Or you can copy and paste the OOB Enterprise Wiki Page Layout. I think I'll do that.

Step 5

Alright, so you've copied the contents of the EnterpriseWiki.aspx Page Layout and now it's time for some customizing. I found the control I want to move, so I'll simply do a copy or cut/paste to the new spot.
image


Step 6

Check-in, publish, and approve the new Page Layout.

Step 7

Almost there! Navigate to your publishing site, in this case the Enterprise Wiki Site, then go to Site Actions > Site Settings > Page layouts and site templates (Under Look and Feel). Here you'll be able to make the new Page Layout available for use within the site.

image

Step 8

Go back to your site and edit the page that you'd like to change the layout for. On the Page tab of the Ribbon, click on Page Layout and select your custom Page Layout.

image 
Et voila! You just created a custom Page Layout using SharePoint Designer 2010, re-arranged a SharePoint control and managed to plan for the future by not modifying the out of the box template. That was a really simple example but I hope it helped to give you some ideas on how else you can customize Page Layouts within SharePoint 2010!

image

Wednesday, 9 June 2010

Property 'SiteUrl' contains an invalid URL

When trying to deploy a Business Data Connectivity Model with Visual Studio 2010 you get the following error:

Error occurred in deployment step 'Add Solution': Property 'SiteUrl' contains an invalid URL

Solution:

Add a property to the Feature.xml File:

<Properties>
<Property Key="SiteUrl" Value="http://sp2010.tripleaconnected.com">
</Properties>

Tuesday, 7 July 2009

How to create Class from InfoPath Schema

  1. On the File menu, click Save As Source Files and save the form files in a folder on your local computer. There will be a file called myschema.xsd among the files you just saved.
  2. Close InfoPath.
  3. Open a Visual Studio 2005 Command Prompt which you can access via Start > Programs > Microsoft Visual Studio 2005 > Visual Studio Tools.
  4. At the command-line prompt, use the cd command to navigate to the folder where you saved the form files. To generate a C# class for the form, at the command-line prompt type:
    1. xsd myschema.xsd /c /l:cs
  5. This will create a C# file named myschema.cs for the InfoPath form.

Note: Once you have created the class and examine it, you will notice that the name of the class is the same as the name of the root node of the InfoPath form. So if you want the generated class to have a meaningful name other than myFields, which is the default name assigned to the root node of an InfoPath form, you must change the name of the root node in InfoPath to the name you want the generated class to have, before you create the .NET class.

How to make a Folder accessible in SharePoint

If you want to show files from a folder within SharePoint, you can use the PageViewer Webpart.

This can be usefull if you want to make your logfiles accessible without having to log on to the server.

Therefore just

  1. add a PageViewer WebPart to your Webpart Page.
  2. Set the WebPart Properties to show Files from a Folder.
  3. Enter the link to the folder e.g. \\servername\LogFiles

Use Audience Targeting to adapt the visibility of the Webpart.

How to Access SharePoint User Information in InfoPath

In InfoPath 2007 you can get the logon name of the current user by using the built-in function username(). (http://blogs.msdn.com/infopath/archive/2006/05/26/608092.aspx)

If you want to get more information that just the user name you should use the UserprofileService Web Service of MOSS. You have two options with this service:

  1. You can pass the account information of the user who’s details you need
  2. You can pass a null value, which will cause the web service to grab the current users identify

How to do it:

Step 1: Create the form

  1. Open InfoPath and design a new, blank form
  2. Save the form

Step 2: Set the security level of the form

  1. From the Main Toolbar, select Tools | Form Options
  2. Select Security and Trust from the list box
  3. Clear the checkbox next to Automatically determine security level
  4. Select Full Trust
  5. Hit OK.

Step 3: Add code to the form

  1. From the Main Toolbar, select Tools | Programming | Loading Event
  2. Right-click on UserProfileDemo node of the Project Explorer and choose Add Web Reference
  3. Enter the following for the URL: http://<yourServerName>/_vti_bin/UserProfileService.asmx
  4. Enter the following for Web reference name: ProfileService
  5. Click Add Reference
  6. Replace the FormEvents_Loading event handler with the following code snippet:

ProfileService.UserProfileService profileService = new Gravity.IP.Activities.SignUp.ProfileService.UserProfileService();
profileService.UseDefaultCredentials = true;
ProfileService.PropertyData[] userProps = null;
// Passing null to this method causes the profile of the current user to be returned.
userProps = profileService.GetUserProfileByName(null);
int prefferedNamePropID = -1;
if((userProps != null) && (userProps.Length != 0)){
    for (int i = 0; i < userProps.Length; i++) {
        if(prefferedNamePropID != -1){
            switch (userProps[i].Name.ToLower())  {
                case "preferredname":
                    prefferedNamePropID = i;
                    break;
            }
        }else{
            break;
        }
    }
}
if (prefferedNamePropID != -1) {
    ProfileService.ValueData[] values = userProps[prefferedNamePropID].Values;
    if (values.Length > 0) {
        String currentUserDisplayName = values[0].Value.ToString();
    }
}

Monday, 6 July 2009

Hot to get Item ID in InfoPath Instantiation Form

When using an InfoPath Form as an Instantiation Form for a Workflow, the Form is loaded inside a XmlFormView WebPart.

Due to this you cannot get Query Parameters via the InputParameters (http://msdn.microsoft.com/en-us/library/microsoft.office.infopath.loadingeventargs.inputparameters.aspx).

Instead you should add a reference to System.Web and use HttpCurrent.Context.Request.QueryString to fetch the values.

String id = HttpContext.Current.Request.QueryString("ID")

How to use InfoPath for a Workflow Instantiation / Association Form

Possible Forms:

  • Association form - this pops up when a WF is associated with a list.
  • Initiation form - this pops up when a WF is initiated on a list item.
  • Modification form - you'd use this WF to perform a modification on the WF.
  • Task form - this can be used to substitute for the task created for the WF.

Important: The schema of the init and association form must be the same. If you want to have different Form, you should make both forms in one and hide the fields depending on the type of call.

This How To uses the example of a list selection when the workflow is initiated.

1. Create the Form

Open InfoPath and create a new blank form template. Select 'Enable browser-compatible features only'.

2. Get the Workflow Context

(http://msdn.microsoft.com/en-us/library/ms558892.aspx)

Create a XML File with the following content

<Context isStartWorkflow="true"isRunAtServer="false"provideAllFields="true"siteUrl=""/>



Following are the elements of the schema:




  • Context   Root element that represents the workflow context information being passed to the form. The Context element has the following attributes:


  • isStartWorkflow   Boolean; true if the form is being used as the workflow initiation form. You can use a specified form as both workflow association and initiation form, and it can present different views at each stage.


  • isRunAtServer    Boolean; true if the form is being opened on the server.


  • provideAllFields    Boolean; true if the form is being used as the last point of user interaction before the workflow starts. This means the form is being used either as a workflow initiation form or as a workflow association form for a workflow you have specified to start automatically.


  • siteUrl   String. The URL of the SharePoint site from which the form is being opened.


Note:  The form does not use the actual values specified in the first three attributes; they are included solely to indicate that the attributes are defined as Boolean data types.



Save the file to your computer Add the context schema to your Form





  • In Office InfoPath 2007, on the Design Tasks pane, select Data Source, and then click Manage Data Connection.


  • In the Data Connections dialog box, click Add.


  • In the Data Connection Wizard, select Create a new connection to, and then select Receive data. Click Next.


  • Select XML Document, and then click Next.


  • Browse to the location where you saved your Context.xml file, select it, click Open, and then click Next.


  • Select Include the data as a resource file in the form template, and then click Next.


  • Type Context as the data connection name, and ensure that Automatically retrieve data when form is opened is selected. Click Finish, and then click Close.




After you include the Context.xml file as a resource file in the form template, you no longer need the file. You do not need to include the file in your workflow solution.




3. Design the Form



Main Data Source



Add a RepeatingTable Control (Lists – List – ListName, ListID)



Note: This control is just added for the creation of the Data Source Fields, it can be removed from the directly





Add a DropDown Control (SelectedList)




Set the Control Properties to look up the values in the Repeating Table Fields




using Microsoft.Office.InfoPath; 


Lists DropDown Properties 



Add the Code




Click On Tools –> Programming –> Loading Event to open Microsoft Visual Studio Tools for Applications






Note: to install Microsoft Visual Studio Tools for Applications you have to go to Control Panel –> Add or Remove Programs –> Select your Office installation –> Change –> Add or Remove Features; You have to enable it for every Office Application with which you want to use it.




a) Include a Reference to Windows SharePoint Services to you project




b) use the following using statements



using Microsoft.Office.InfoPath; 
using System;
using System.Xml;
using System.Xml.XPath;
using Microsoft.SharePoint;
using System.Collections.Generic;



c) Create Properties




Note: Member variables are not supported in browser-enabled forms. Instead, write and read these values from the FormState dictionary




private String SiteUrl { 
get {
if (FormState["SiteUrl"] != null) {
return FormState["SiteUrl"].ToString();
} else {
return String.Empty;
}
}
set {
FormState["SiteUrl"] = value;
}
}
private String SelectedListID {
get {
if (FormState["SelectedListID"] != null) {
return FormState["SelectedListID"].ToString();
} else {
return String.Empty;
}
}
set {
FormState["SelectedListID"] = value;
}
}




 d) add a method for removing the initial empty entry in the DropDown





private void emptyListDropDown() { 
XPathNavigator myRoot = MainDataSource.CreateNavigator();
XPathNavigator xnContentTypes = myRoot.SelectSingleNode("/my:ListSelector/my:Lists", NamespaceManager);
List<XPathNavigator> nodesToDelete = new List<XPathNavigator>();
foreach (XPathNavigator option in xnContentTypes.Select("..//my:List", NamespaceManager)) {
nodesToDelete.Add(option);
}
//delete all nodes on the list
foreach (XPathNavigator option in nodesToDelete) {
option.DeleteSelf();
}
}




e) add a method for getting the Context Information





private void getContextData() { 




XPathNavigator secDSNav = DataSources["Context"].CreateNavigator();




if (secDSNav.MoveToChild(XPathNodeType.All)) {

string myNamespace = NamespaceManager.LookupNamespace("");




SiteUrl = secDSNav.GetAttribute("siteUrl", myNamespace);

}


}




f) add methods for loading the Lists from SharePoint and add them to the DropDown Control





//fills the list dropdown with all non hidden lists from the site 

private void loadSharePointLists(String siteUrl) {


using (SPWeb oWeb = new SPSite(siteUrl).OpenWeb()) {


foreach(SPList oList in oWeb.Lists){


if(!oList.Hidden){


createAndAddDropDownListItem(oList.Title, oList.ID.ToString());


}


}


}


}




private void createAndAddDropDownListItem(String name, String guid) {

string myNamespace = NamespaceManager.LookupNamespace("my");


XPathNavigator myRoot = MainDataSource.CreateNavigator();


XPathNavigator node = myRoot.SelectSingleNode("/my:ListSelector/my:Lists", NamespaceManager);


DeleteNil(node);


using (XmlWriter writer = node.AppendChild()) {


writer.WriteStartElement("List", myNamespace);


writer.WriteElementString("ListName", myNamespace, name);


writer.WriteElementString("ListID", myNamespace, guid);


writer.WriteEndElement();


writer.Close();


}


}




public void DeleteNil(XPathNavigator node) {

if (node.MoveToAttribute("nil", "http://www.w3.org/2001/XMLSchema-instance")) {


node.DeleteSelf();


}


}




g) adapt the Loading Method to load the DropDown Control





public void FormEvents_Loading(object sender, LoadingEventArgs e) { 

emptyListDropDown();


getContextData();


String siteUrl = SiteUrl;


if (siteUrl != String.Empty) {


loadSharePointLists(siteUrl);


}


}




Add a Submit Button




IP forms are hosted in an aspx page or client application, so when the user wants to submit the form, he needs to have the submit button



a) create a connection to submit the xml data/string to the host environment (the host will handle the rest)



b) close the form so that it doesn’t just submit and stick around.




Add domain level trust




In InfoPath click on Tools->Form Options->Security and Trust




4. Create Code from your Form Schema




On the File menu, click Save As Source Files and save the form files in a folder on your local computer. There will be a file called myschema.xsd among the files you just saved.





Close InfoPath.



Open a Visual Studio 2005 Command Prompt which you can access via Start > Programs > Microsoft Visual Studio 2005 > Visual Studio Tools.



At the command-line prompt, use the cd command to navigate to the folder where you saved the form files. To generate a C# class for the form, at the command-line prompt type:




xsd myschema.xsd /c /l:cs 



This will create a C# file named myschema.cs for the InfoPath form.



Note: Once you have created the class and examine it, you will notice that the name of the class is the same as the name of the root node of the InfoPath form. So if you want the generated class to have a meaningful name other than myFields, which is the default name assigned to the root node of an InfoPath form, you must change the name of the root node in InfoPath to the name you want the generated class to have, before you create the .NET class.




5. Adapt your Workflow to use the Form






Add myschema.cs to the project



Publish the Form




In InfoPath click File –> Publish –> Select ‘To a network location” –> Browse to the root folder of the Workflow Project –> empty the url –> publish



Copy the dll from the Form Source Folder into the root folder of the Workflow Project




Adapt Workflow.xml





In MetaData add <Association_FormURN>[URN FOR ASSOCIATION FORM]</Association_FormURN>



Set the AssociationURL attribute of the Workflow element to the following value:




AssociationUrl="_layouts/CstWrkflIP.aspx"



Note: To get the Form ID open your Form in InfoPath click on File –> Properties



Note:




AssociationUrl="_layouts/CstWrkflIP.aspx" 

InstantiationUrl="_layouts/IniWrkflIp.aspx"


ModificationUrl="_layouts/WFMod.aspx"




Adapt Feature.xml




In the ElementManifest Tag add the xsn file and dll




<ElementFile Location="Foobar.dll"/> 

<ElementFile Location="Foobar.xsn"/>




6. Use Form Data in the Workflow





To use the information from the InfoPath Form within the workflow use the following code




//deserialise InfoPath initiation form data 
String initData = workflowProperties.InitiationData;
XmlSerializer xser = new XmlSerializer(typeof(ListSelector));
XmlTextReader reader = new XmlTextReader(new System.IO.StringReader(initData));
ListSelector listSelector = (ListSelector)xser.Deserialize(reader);




7. Attach the Workflow to you List or Library





Open the List –> Settings –> Workflow settings –> Add a Workflow –> Select the Workflow –> Enter a name –> Select the List and Start Options






After clicking OK you will see your form



image