Showing posts with label SharePoint 2010. Show all posts
Showing posts with label SharePoint 2010. Show all posts

Wednesday, February 5, 2014

SharePoint issue “The context has expired and can no longer be used. Exception from HRESULT 0x80090317”

Problem
A client had problems with some workflow instances. Most instances did complete without a problem others failed.  Especially workflow that continued after a few days reported the following error in the workflow history log:
“{Microsoft.SharePoint.SPException: The context has expired and can no longer be used. (Exception from HRESULT: 0x80090317) ---> System.Runtime.InteropServices.COMException (0x80090317): The context has expired and can no longer be used. (Exception from HRESULT: 0x80090317)
   at Microsoft.SharePoint.Library.SPRequestInternalClass.SetHttpParameters(String bstrHttpMethod, String “

I did some googling and found some hits that found a solution in synchronizing the date/time of the WFE’s and admin servers. In our case that wasn’t the the case.

Cause
After digging into the problem I noticed particular workflow activity opened a site collection using an SPUserToken.
using (SPSite site = new SPSite(WorkflowProperties.SiteId, InitializationData.UserToken))
{
//do some work
}
The SPUserToken was part of the workflow context and was populated when the workflow instance was first created. Default the SPUserToken is valid for 1440 minutes (1 day). In case the workflow instance is de-hydrated after more than 1 day the SPUserToken is expired and isn’t valid anymore. You will get the message “The context has expired and can no longer be used. (Exception from HRESULT: 0x80090317” and are not allowed to open the sitecollection.

Conclusion
So when you get this specific error make sure to check if you use an old SPUserToken! And be aware the the SPUserToken can expire.

Detailed exception information
Full error message: "The context has expired and can no longer be used. (Exception from HRESULT: 0x80090317)"
Sample Stacktrace:
{Microsoft.SharePoint.SPException: The context has expired and can no longer be used. (Exception from HRESULT: 0x80090317) ---> System.Runtime.InteropServices.COMException (0x80090317): The context has expired and can no longer be used. (Exception from HRESULT: 0x80090317)
   at Microsoft.SharePoint.Library.SPRequestInternalClass.SetHttpParameters(String bstrHttpMethod, String bstrRequestDigest, UInt32 flags, Guid gTranLockerId, Byte[]& ppsaImpersonateUserToken, Boolean bIgnoreTimeout, String bstrUserLogin, String bstrUserKey, UInt32 ulRoleCount, String bstrRoles, Boolean bWindowsMode, ApplicationPrincipalInfo& pAppUserInfo, Boolean bInvalidateCachedConfigurationProperties, Int32 lAppDomainId, ISPManagedObjectFactory pFactory, Boolean bCallstack)
   at Microsoft.SharePoint.Library.SPRequest.SetHttpParameters(String bstrHttpMethod, String bstrRequestDigest, UInt32 flags, Guid gTranLockerId, Byte[]& ppsaImpersonateUserToken, Boolean bIgnoreTimeout, String bstrUserLogin, String bstrUserKey, UInt32 ulRoleCount, String bstrRoles, Boolean bWindowsMode, ApplicationPrincipalInfo& pAppUserInfo, Boolean bInvalidateCachedConfigurationProperties, Int32 lAppDomainId, ISPManagedObjectFactory pFactory, Boolean bCallstack)
   --- End of inner exception stack trace ---
   at Microsoft.SharePoint.SPGlobal.HandleComException(COMException comEx)
   at Microsoft.SharePoint.Library.SPRequest.SetHttpParameters(String bstrHttpMethod, String bstrRequestDigest, UInt32 flags, Guid gTranLockerId, Byte[]& ppsaImpersonateUserToken, Boolean bIgnoreTimeout, String bstrUserLogin, String bstrUserKey, UInt32 ulRoleCount, String bstrRoles, Boolean bWindowsMode, ApplicationPrincipalInfo& pAppUserInfo, Boolean bInvalidateCachedConfigurationProperties, Int32 lAppDomainId, ISPManagedObjectFactory pFactory, Boolean bCallstack)
   at Microsoft.SharePoint.SPGlobal.CreateSPRequestAndSetIdentity(SPSite site, String name, Boolean bNotGlobalAdminCode, String strUrl, Boolean bNotAddToContext, Byte[] UserToken, String userName, Boolean bIgnoreTokenTimeout, Boolean bAsAnonymous)
   at Microsoft.SharePoint.SPWeb.InitializeSPRequest()
   at Microsoft.SharePoint.SPWeb.InitWebPublic()
   at Microsoft.SharePoint.SPWeb.get_CurrentUser()

Thursday, January 30, 2014

How to handle ‘multicultural’ issues with the formula of a SharePoint SPFieldCalculated

Setting a formula in code for a SharePoint SPFieldCalculated is a real nasty task. I spent a lot of time figuring out why some formula’s did work and others did not in various cases.

When designing a formula you may think you need to take a few things into account like language and separator. During testing I came to the conclusion that the following has impact:
- thread culture
- separator character used for separating parameters in the formula
- SPContext.Current == null or not
- SharePoint default language (what language was the original SharePoint setup)
- and that the language of the formula method (like CONCATENATE) is not important at all.

Simple Test Case
First let me tell you about my test case I used. Two text fields, one is a company tag, the other is the first name of a contact person. A third will be the calculated field. It will need to show the following: ‘<Company Tag> - <First Name>’, without the ‘.
SPFieldText internalName: companyTag, DisplayName (English) = “Company Tag”, DisplayName (Dutch) = “Tag bedrijf”
SPFieldText internalName: contactFirstName, DisplayName (English) = “First Name”, DisplayName (Dutch) = “Voornaam”,
The creation of the calculated field should be in code (in this case a feature receiver). The deployment should work in various cases like:
Case 1: Activating feature by hand in UI.
Case 2: Feature is activated by onet.xml of a web template.

The problem:
The problem arises when you need it to work for case 1 and case 2 in different cultures. In my case in English and Dutch. By testing the creation using the following scenario’s you will see where it fails.

Scenario 1: Case 1 in English thread culture
Formula: “=CONCATENATE([Company Tag],\” – \”,[First Name])”
Separator: , (English)
Display names:  all English
Result: It works…

Scenario 2: Case 1 in Dutch thread culture
Formula: “=CONCATENATE([Tag bedrijf];\” – \”;[Voornaam])”
Separator: ; (Dutch)
Display names:  all Dutch
Result: It works…

Scenario 3: Case 2 in English thread culture
Same as Scenario 1:
Result: It works…

Scenario 4: Case 2 in Dutch thread culture
Same as Scenario 2:
Result: It does NOT work. 

Why doesn’t scenario 4 work? Threads are all Dutch, passing all the same parameters for the formula as scenario 2. So why…?
Also tried various combinations on the formula like:
- English display names, English and Dutch separator,
- Using Internal names, English and Dutch separator,
- Using Dutch formula method names like “=TEKST.SAMENVOEGEN([Tag bedrijf];\” – \”;[Voornaam])”

Only thing that seems to work was setting the separator to the default English (the default SharePoint installation culture) variant:
Formula: “=CONCATENATE([Tag bedrijf];\” – \”;[Voornaam])”
Separator: , (English)
Display names:  all Dutch

The main difference I detected between case 1 and case 2 was the SPContext.Current was null in case 2.
In case you design a formula and need to determine the separator, do not only check the Thread culture, but also the existence of SPContext.Current.

Guidelines regarding formula
Adding all variations of testing and failures together I came up with the following guideline when designing the formula for the SPFieldCalculated.
- Formula string should start with a '=' (not shown in above cases)
- always encapsulate the display name with brackets [], better safe than sorry (not shown in above cases)
- in case SPContext.Current is NOT null
* use field display names for the Thread.CurrentThread.CurrentUICulture
* use separator for the Thread.CurrentThread.CurrentUICulture  (Dutch= ‘;’ English=”,”)
- in case SPContext.Current==null (e.g.: when feature code via activated by an onet.xml of a webtemplate)
* use field display names for the Thread.CurrentThread.CurrentUICulture
* use separator that matched the SharePoint Initial Installation Language (English on my development machine. Contact me if you know how to determine this in code!)
- Methods names in the formula are culture independent, you can use English/Dutch/Whatever without a problem. (e.g. CONCATENATE and TEKST.SAMENVOEGEN work in English and Dutch thread culture)

I hope I saved you some valuable time in creating formula’s for SPFieldCalculated fields in code.
New insights on the formula are welcome.

Wednesday, September 25, 2013

SharePoint Synchronous Event Receiver with message to UI and saving form data

The goal is to save data from the new/edit form to list item and in certain situations show a message to UI by using event receivers.

As we all know showing a message to UI is not going to work when using async event receivers. So we’re stuck to use sync event receivers. At first you will see examples that shows a message by using code like this:

try
{
    //some code
}
catch (Exception ex)
{
    properties.Status = SPEventReceiverStatus.CancelWithError;
    properties.ErrorMessage = ex.Message;
    properties.Cancel = true;
}

The options for properties.Status are:
Continue, there is no error.
CancelNoError, silently cancel the request as if the request has succeeded.
CancelWithError, cancel the request with the error message specified in ErrorMessage.
CancelWithRedirectUrl cancel the request and redirect to the URL specified in RedirectUrl.

The only usable is CancelWithRedirectUrl. However this will not save your form data to the listitem. So all changes are lost.
Where is the option to ‘ContinueWithRedirectUrl’? It doesn’t exist.
Luckily we have the SPUtility class with the Redirect method. When using the SPRedirectFlag.DoNotEndResponse flag it will not abort the event receiver thread (Flag Default will abort event receiver thread and therefor will not save the afterproperties on the list item.). In this case the event receiver thread will be completed and afterproperties are save to the list item. And the redirect will be processed. You could redirect a user to a custom page, provide some parameters and show a message as you like.

Code sample of the redirect:

SPUtility.Redirect("notificationpage.aspx?status=somestatusvalue", SPRedirectFlags.DoNotEndResponse, HttpContext.Current);

Wednesday, June 12, 2013

Copy SPField property values without changing Content Type

In a particular project for a client I needed to copy all field properties from document A to document B. However the content type should not be changed. For simplicity I ignore edge cases. For example in case the source and destination content type have different required SPFields.

At first I copied all available fields from source to destination:

foreach (SPField field in sourceItem.Fields)
{
    if (field.ReadOnlyField)
    {
        continue;
    }
    if (destinationListitem.Fields.ContainsField(field.InternalName))
    {
        destinationListitem[field.InternalName] = sourceItem[field.InternalName];
    }
}
destinationListitem.SystemUpdate()

The content type changed. No wonder as the SPField ‘ContentType’ was copied.
Changed code to to ignore SPField ‘ContentType’

IEnumerable<string> ignoreFields = new List<string>()
{
    "ContentType"
};
 
foreach (SPField field in sourceItem.Fields)
{
    if (field.ReadOnlyField)
    {
        continue;
    }
    if (ignoreFields.Contains(field.InternalName, StringComparer.InvariantCultureIgnoreCase))
    {
        continue;
    }
 
    if (destinationListitem.Fields.ContainsField(field.InternalName))
    {
        destinationListitem[field.InternalName] = sourceItem[field.InternalName];
    }
}
destinationListitem.SystemUpdate()

But still content type changed.
It turns out that you need to skip the SPField ‘ContentType’ and ‘MetaInfo’ to make sure the content type of the destination item is not changed.

Final code snippet:

IEnumerable<string> ignoreFields = new List<string>()
{
    "ContentType",
    "MetaInfo"
};
 
foreach (SPField field in sourceItem.Fields)
{
    if (field.ReadOnlyField)
    {
        continue;
    }
    if (ignoreFields.Contains(field.InternalName, StringComparer.InvariantCultureIgnoreCase))
    {
        continue;
    }
 
    if (destinationListitem.Fields.ContainsField(field.InternalName))
    {
        destinationListitem[field.InternalName] = sourceItem[field.InternalName];
    }
}
destinationListitem.SystemUpdate()

Sunday, October 14, 2012

Mapping DateTime properties in SharePoint User Profile using BCS

Just a quick one this time.

When importing a field to a SharePoint UserProfile DateTime type in the User Profile. You normaly have a BCS model with a property of System.DateTime and map that to the UserProfile. However this won't work, you will get errors like this:
"Microsoft.Office.Server.UserProfiles.PropertyInvalidFormatException: Invalid Date Format: Input must match smalldatetime data type, or be a string that parses to match the Guid data type ---> System.FormatException: String was not recognized as a valid DateTime.   
at System.DateTimeParse.ParseExact(String s, String format, DateTimeFormatInfo dtfi, DateTimeStyles style)   
at Microsoft.Office.Server.UserProfiles.UserProfileGlobal.ValidatedDate(Object value, PropertyDataType propDataType, SiteContext si)     -
-- End of inner exception stack trace ---   
at Microsoft.Office.Server.UserProfiles.UserProfileGlobal.ValidatedDate(Object value, PropertyDataType propDataType, SiteContext si)   
at Microsoft.Office.Server.UserProfiles.UserProfileGlobal.ValidatedSingleValue(Object value, ProfileSubtypeProperty prop, PropertyDataType

propDataType, UserFormat userFormat, UserProfileApplicationProxy userProfileApplicationProxy, Guid partitionID, SiteContext si)   
at Microsoft.Office.Server.UserProfiles.UserProfile.BulkPropertiesUpdate(Int64 importExportId, Hashtable properties, String strAccountName)."

To fix you need to do the following:

- The property in your BCS model should still be 'System.DateTime'. Otherwise you cannot map the BCS to the UserProfile Date typed property.
- However make sure your BCS code outputs a string in the following format: yyyyMMddHHmmss.0Z (like: .ToString("yyyyMMddHHmmss.0Z", CultureInfo.InvariantCulture);
This will make sure the output can be converted to a DateTime.

If you want more details use reflector on:
- class Microsoft.Office.Server.UserProfiles.UserProfileGlobal
- method private static DateTime ValidatedDate(object value, PropertyDataType propDataType, SiteContext si)

Tuesday, April 24, 2012

How to add a Windows account to Security Group in a claims based SharePoint 2010 environment

In a timer job I added users to a certain SPGroup and added this group to some libraries with contribute rights. When I logged is with that user account it didn’t have the contribute rights. The problem was that the Windows account was added instead of the claims based user.

At first I just used the following code:

   1: String loginName = "SP2010\user1";
   2: SPGroup group = web.SiteGroups["somegroup"];
   3:  
   4: SPUser user = web.EnsureUser(loginName);
   5: if (user != null)
   6: {
   7:   group.AddUser(user);
   8: }

However this added a user as formatted in this XML:

<User ID="442" Sid="S-1-5-21-4190988674-4107964418-2216591577-1137" Name="Some name" LoginName="SP2010\user1" Email="test@test.com" Notes="" IsSiteAdmin="False" IsDomainGroup="False" Flags="0" />

I changed it to look for the correct SPUser using this code snippet:
   1: String loginName = "SP2010\user1";
   2:  
   3: SPClaimProviderManager mgr = SPClaimProviderManager.Local;
   4: if (mgr != null)
   5: {
   6:   SPClaim claim = new SPClaim(SPClaimTypes.UserLogonName, loginName, "http://www.w3.org/2001/XMLSchema#string", SPOriginalIssuers.Format(SPOriginalIssuerType.Windows));
   7:   claimLoginName = mgr.EncodeClaim(claim);
   8: }
   9:  
  10:  
  11: SPGroup group = web.SiteGroups["somegroup"];
  12: SPUser user = web.EnsureUser(claimLoginName);
  13: if (user != null)
  14: {
  15:   group.AddUser(user);
  16: }

This will generate a claim that represents the windows account “SP2010\user1”. This will find the correct SPUser, the XML is:

<User ID="255" Sid="" Name="Some name" LoginName="i:0#.w|SP2010\user1" Email="test@test.com" Notes="" IsSiteAdmin="False" IsDomainGroup="False" Flags="0" />

Notice the formatting of the LoginName.

Now the correct user account (in claims format) is added to the library with contribute rights the user logging in with SP2010\user1 can contribute.

Tuesday, November 15, 2011

How to add a survey list to the Quicklaunch

When you add a survey list to a site using a webtemplate or sitetemplate you add the following line to the onet.xml:

<Lists>
<List FeatureId="00bfea71-eb8a-40b1-80c7-506be7590102" Type="102" Title="$Resources:core,surveyList;" Url="$Resources:core,lists_Folder;/$Resources:core,surveyList;" QuickLaunchUrl="$Resources:core,lists_Folder;/$Resources:core,surveyList;/overview.aspx" />
</Lists>

When you create a site with this onet.xml the survey list is created however it’s not added to the quicklaunch. This is probably because the <Navbars> doesn’t contain a ‘Survey’ category.

To fix this add this line to the <Navbars> element:

<NavBar Name="$Resources:core,category_Surveys;" Prefix="&lt;table border='0' cellpadding='4' cellspacing='0'&gt;" Body="&lt;tr&gt;&lt;td&gt;&lt;table border='0' cellpadding='0' cellspacing='0'&gt;&lt;tr&gt;&lt;td&gt;&lt;img src='/_layouts/images/blank.gif' id='100' alt='' border='0'&gt;&amp;nbsp;&lt;/td&gt;&lt;td valign='top'&gt;&lt;a id='onetleftnavbar#LABEL_ID#' href='#URL#'&gt;#LABEL#&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;&lt;/td&gt;&lt;/tr&gt;" Suffix="&lt;/table&gt;" ID="1007" />

Or you could use one of the alternatives:
- Add the survey list in code (web.Lists.Add with SPListTemplateType.Survey) and set OnQuickLaunch to true.
- manually add the survey list to the quicklaunch using the List Settings –> Title, description and navigation. The Navbar item will be added automatically.

Wednesday, October 19, 2011

Deploy files to Pages subfolder (SharePoint 2010)


Scenario:
Deploy pages/files to a subfolder in the pages library.

How it’s done:
In Visual Studio 2010 create a module:

image

with the following elements.xml

<?xml version="1.0" encoding="utf-8"?>
<Elements xmlns="http://schemas.microsoft.com/sharepoint/">
<Module Name="AdminPages" Url="$Resources:osrvcore,List_Pages_UrlName;/AdminPages" Path="AdminPages">
<File Path="ChangePassword.aspx" Url="ChangePassword.aspx" Type="GhostableInLibrary" />
<File Path="ForgotPassword.aspx" Url="ForgotPassword.aspx" Type="GhostableInLibrary" />
</Module>
</Elements>

Some explanation about the elements file:
Module.Name: name of the module
Module.Url: location in SharePoint, where do you want to deploy the files to.
Module.Path: location of the files in your feature (In this case in VS2010 the ‘AdminPages’ module creates a directory in the feature with name ‘AdminPages’)
File.Path: location of the file in your feature (it also uses the Module.Path, so don’t add the ‘AdminPages’ because it’s defined in the Module.Path)
File.Url: filename in SharePoint (you could name the file whatever you like)
 
Add the module to a feature.
Deploy the solution.

After installing and activating the feature (that contains the AdminPages module) you will see that a folder ‘AdminPages’ is created in the /Pages (depending on language) folder.

image

image

Friday, June 24, 2011

Reminders for Continuous build for SharePoint projects in Team Foundation Server (TFS)

Based on this MSDN article How to Build SharePoint Projects with TFS Team Build I setup the build machine. When doing so and tested it on my current SharePoint 2010 project I found some issues. As a reminder-to-self here some notes:

Deployment of DLLs to GAC

Some dll’s should be deployed to GAC. Use gacutil (of the .NET 4 framework) to do this, using a .NET 2 or 3.5 version doesn’t work.

- Download and install the Microsoft Windows SDK for Windows 7 and .NET Framework 4
- Gacutil location for 64bit systems is C:\Program Files\Microsoft SDKs\Windows\v7.1\Bin\NETFX 4.0 Tools\

SharePoint DLLs

Copy all SharePoint dll’s from the C:\Program Files\Common Files\Microsoft Shared\Web Server Extensions\14\ISAPI\
When your SharePoint project references Microsoft.SharePoint.Publishing, also copy System.Web.DataVisualization.dll (.NET 3.5). It location is C:\Program Files (x86)\Microsoft Chart Controls\Assemblies\ This is because the publishing assembly references the DataVisualization assembly.

Updating SharePoint 2010

When installing cumulative or Service Packs on SharePoint 2010, you need to update the dll’s on your build server to keep them aligned.

Thursday, June 23, 2011

SharePoint 2010 Workflow: Could not load file or assembly '$assemblyname$'

During workflow development I got the following error when starting a Site workflow:

Load Workflow Assembly: System.IO.FileNotFoundException:
Could not load file or assembly '$assemblyname$' or one of its dependencies. The system cannot find the file specified. File name: '$assemblyname$'


Very strange, it did worked for months. So I checked the workflow feature in the 14-hive. The element manifest was wrong. It contained the $assemblyname$ tag instead of the assembly name.

<Workflow
Name="Ctalk - MailCtalkEditieWF"
Description="Workflow for sending Ctalk Editions"
Id="15f3ae38-12bc-4dbf-ad2b-9281ff09d43e"
CodeBesideClass="Ctac.Ctalk.MailCtalkEditie.MailCtalkEditie"
CodeBesideAssembly="$assemblyname$">

After a ‘normal’ deployment in VS2010 the element manifest file was correct:

<Workflow
Name="Ctalk - MailCtalkEditieWF"
Description="Workflow for sending Ctalk Editions"
Id="15f3ae38-12bc-4dbf-ad2b-9281ff09d43e"
CodeBesideClass="Ctac.Ctalk.MailCtalkEditie.MailCtalkEditie"
CodeBesideAssembly="Ctac.Ctalk, Version=1.0.0.0, Culture=neutral, PublicKeyToken=6a509658686505f0">


So what was the problem… It’s the VS2010 ‘Copy to SharePoint Root’ command part of the CKSDev extension. It just copied the file as is, it doesn’t replace the tag. So keep this in mind when using the ‘Copy to SharePoint Root’ command in combination with workflow development.

Wednesday, June 22, 2011

Provisioning URL values with description using element manifest

When provisioning files/items that contain an URL typed field you probably want to provide an url and description. When using an element manifest to provision some content you probably use something like this:

<?xml version="1.0" encoding="utf-8"?>
<Elements xmlns="http://schemas.microsoft.com/sharepoint/">
<Module Name="Banners" Url="Banners">
<File Path="Banners\banner1.png" Url="banner1.png" Type="GhostableInLibrary">
<Property Name="URL" Type="string" Value="http://www.yellowred.nl"/>
<Property Name="NewWindow" Value="true"/>
</File>
</Module>
</Elements>
There’s no room for providing a description. The property element has the following ‘Type’ attribute options: int, string, datetime. So no url type.

As you know the URL value is just a string with in specific format: <url>, <description>
You can also find this format when using SharePoint Manager 2010. Open the item containing the url field. Check the schema.xml and search for the url.

To provide a description to the url, provision your data like this:

<?xml version="1.0" encoding="utf-8"?>
<Elements xmlns="http://schemas.microsoft.com/sharepoint/">
<Module Name="Banners" Url="Banners">
<File Path="Banners\banner1.png" Url="banner1.png" Type="GhostableInLibrary">
<Property Name="URL" Type="string" Value="http://www.yellowred.nl, Yellow and Red"/>
<Property Name="NewWindow" Value="true"/>
</File>
</Module>
</Elements>

Friday, June 3, 2011

Issues with RemoveFieldRef and ContentTypes

When inheriting from content types you can use RemoveFieldRef to remove unused fields. The RemoveFieldRef however is very picky when you want it to work.

Example content type “SampleInheritance” (inherits from content type: Event (0x0102))

<Elements xmlns="http://schemas.microsoft.com/sharepoint/">
<ContentType
ID="0x0102002748FB863AA69C449E1D30D488B1DFDD"
Name="SampleInheritance"
Description="Create a new meeting, deadline or other event."
Group="Sample"
Inherits="FALSE"
Hidden="false"
ReadOnly="false"
Sealed="false">
<FieldRefs>
<FieldRef ID="fce16b4c-fe53-4793-aaab-b4892e736d15" Name="EMail" DisplayName="E-Mail" />
<FieldRef ID="114bf263-2846-4854-839b-715c0f573cea" Name="Header1" DisplayName="Header" />
<FieldRef ID="f9589629-6f74-403e-83f6-8da3316dcca2" Name="BedankPagina" DisplayName="BedankPagina" />
<RemoveFieldRef ID="{7d95d1f4-f5fd-4a70-90cd-b35abc9b5bc8}"/>
</FieldRefs>
<XmlDocuments xmlns="http://schemas.microsoft.com/sharepoint/">
<XmlDocument NamespaceURI="http://schemas.microsoft.com/sharepoint/v3/contenttype/forms">
<FormTemplates xmlns="http://schemas.microsoft.com/sharepoint/v3/contenttype/forms">
<Display>ListForm</Display>
<Edit>ListForm</Edit>
<New>ListForm</New>
</FormTemplates>
</XmlDocument>
</XmlDocuments>
</ContentType>
</Elements>

Follow these rules:

  1. in <RemoveFieldRef> the GUID in the ID should start with ‘{‘ and ends with ‘}’
  2. in <RemoveFieldRef> the GUID in the ID must be lowercase
  3. set "Inherits=false” or remove the attribute.

Wednesday, February 23, 2011

Programmatically connect two ListViewWebParts to Filter based on value of Provider Web Part

When you want to connect two ListViewWebParts on a page using the user interface is easy. However doing it programmatically was a bit more complicated. It’s not complex, but you need to know what classes you need to use.

Connecting Web Parts using the User interface:

image
Select the provider Web Part and connect to the consumer web part with the ‘Send Row of Data To’ option.

image
Step 1:
Configure the connection on the Consumer Web Part.
Select ‘Get Filter Values From’

image

Step 2:
Provider Field Name: Title(linked to item)
Consumer Field Name: ArticleEditionTitle

The provider and consumer field names are dependent on your needs. In this case the value of the Title of the provider Web Part is passed to the consumer web part. The consumer web part will only show rows where ArticleEditionTitle is equal to the passed value.

 Connecting Web Parts programmatically:
Use the SPRowToParametersTransformer class, provide the correct provider and consumer fieldnames. These fields are the same as the ones used in step 2 of the user interface procedure.

SPRowToParametersTransformer transformer = new SPRowToParametersTransformer();
transformer.ProviderFieldNames = new string[] { "LinkTitleNoMenu" };
transformer.ConsumerFieldNames = new string[] { "ArticleEditionTitle" };

Get the WebParts that need to get connected
string providerWebPartTitle = "Editions";
string consumerWebPartTitle = "Articles";

WebPart providerPart = (from WebPart w in mgr.WebParts where w.Title == providerWebPartTitle select w).FirstOrDefault();
WebPart consumerPart = (from WebPart w in mgr.WebParts where w.Title == consumerWebPartTitle select w).FirstOrDefault();

Determine the ConnectionPoints of the webparts. Note the provider/consumer ConnectionIds
string providerConnectionId = "DFWP Row Provider ID";
string consumerConnectionId = "DFWP Filter Consumer ID";

//get connectionpoints
ProviderConnectionPoint providerConnectionPoint = (from ProviderConnectionPoint conn in manager.GetProviderConnectionPoints(providerPart)
where conn.ID == providerConnectionId
select conn).FirstOrDefault();

ConsumerConnectionPoint consumerConnectionPoint = (from ConsumerConnectionPoint conn in manager.GetConsumerConnectionPoints(consumerPart)
where conn.ID == consumerConnectionId
select conn).FirstOrDefault();

Add a WebPartConnection using WebPartManager.SPConnectWebParts, and pass along the transformer.

// connect the webparts
manager.SPConnectWebParts(providerPart, providerConnectionPoint, consumerPart, consumerConnectionPoint, transformer);

That’s it.