Archive for September, 2010

September 29, 2010

Using FieldRenderingControls in custom web forms

Within a custom Web Form we can add references to Microsoft.SharePoint.WebControls and add SharePoint FormField controls to render controls to edit values within a list. We can declare a FormField as follows:

<SharePoint:FormField ID="field_id" runat="server" FieldName="MyField" ControlMode="New" />

However, in addition to this definition, the FormField needs to know the list and content type, in case of being a New form, that it is attaching to. This can be attached through the ItemContext property of FormField, which uses an SPContext object (defaulting to SPContext.Current). Of course when we are in a Web Form, SPContext.Current will not be attached to a List or to a particular Item. Therefore, if we want this field to be able to work against an item we need to change the context that is attached to it.

We can achieve this by getting a context using SPContext.GetContext with the web and list we want in mind, and attaching this to our field, using something like the following code,

using (SPWeb web = SPContext.Current.Site.OpenWeb(webLocation))
	SPList targetList = web.Lists[listName];
	SPContext context = SPContext.GetContext(HttpContext.Current, 0, targetList.ID, web);
	field_id.ItemContext = context;

As can be seen, for the item id parameter, at the moment we have passed in 0, which should, in theory, work. However, when we add a SharePoint:SaveButton control to the form and try with this, we will see an exception is raised.

To get around this, under the context declaration we need to explicitly state the FormMode (this is set automatically when the item id is greater than 0 to Edit, but in the case of 0 is left as Invalid). We can do this with the code context.FormContext.FormMode = SPControlMode.New.

The context ideally needs to be attached to the element directly after it is added to its parent control. To achieve this in a clean way we can override the AddedControl method, which is called when any control is added to another control. We can then recurse over all controls in that control set and if it is a FieldMetadata (the base class for BaseFieldControl) derived class, or FormComponent (the base class for items such as SaveButton) derived class, set the item context.

protected override void AddedControl(Control control, int index)
	base.AddedControl(control, index);

protected void SetControlContext(control)
	if (control is FieldMetadata)
		FieldMetadata field = control as FieldMetadata;
		field.ItemContext = this.SPContext;
		field.ControlMode = this.SPContext.FormContext.FormMode;
	if (control is FormComponent)
		FormComponent formComponent = control as FormComponent;
		formComponent.ItemContext = this.SPContext;
		formComponent.ControlMode = this.SPContext.FormContext.FormMode;
	if (control.HasControls)
		foreach (Control childControl in control.Controls)

There are two things of note above, firstly, SPContext is a property of the class in which this sits. We do this, so that the context can be instantiated once, and then reused. Secondly, the field’s ControlMode is being set from the Context, this gives us the freedom of having a form that is both an Add form or an Edit form based on the query string parameters sent to the page. We can parse these parameters inside the SPContext property to set the ControlMode of the underlying controls. In addition this means our controls can lose the ControlMode property, and therefore their definition becomes less verbose.

We could therefore, finally, declare our property as follows:

private SPContext cachedContext = null;

protected SPContext SPContext
		if (cachedContext == null)
			using (SPWeb web = SPContext.Current.Site.OpenWeb(webUrl))
				SPList targetList = web.Lists[listName];
				int itemId = 0;
				int.TryParse(Response.QueryString("itemId"), out itemId);
				cachedContext = SPContext.GetContext(HttpContext.Current, itemId, targetList.ID, web);
				if (itemId &gt; 0)
					cachedContext.FormContext.FormMode = Edit;
					cachedContext.FormContext.FormMode = New;
		return cachedContext;

We can wrap all this code inside a simple Control which will set the properties of all sub controls and in this way achieve custom data entry forms using web forms techniques.

September 27, 2010

Failed to create receiver object SharePoint exception

When developing with SharePoint 2010, I had a working project with multiple features, one of which had an Feature Receiver added, working absolutely fine. However, on adding an Feature Receiver to a second feature in the same project, I encountered the following error:

Failed to create receiver object from assembly "AssemblySignature", class "EventReceiverClassName"
System.ArgumentNullException: Value cannot be null. Parameter name: type

After first checking that the Receiver name was correctly defined in Visual Studio the solution ended up being far more simple than I could have anticipated – by simplying manually retracting and removing the offending wsp from the farm solution store, and then redeploying, the problem went away.

September 21, 2010

Writing JavaScript based Web Apps that degrade well using Web Forms

A trait I’ve observed in programmers who have learnt their trade using Web Forms, rather than programmers who have been involved with another language such as PHP or Python, which forces a slightly higher regard for the output that the browser sees, is a slight disregard for browser settings, and a reluctance to write apps which degrade well; preferring the argument that JavaScript, Java, Flash or another technology must be enabled for the function of the particular web application.

Now, more than ever, with the increased push within Microsoft of jQuery as a Javascript platform, an understanding of writing JavaScript that degrades well is important.

Here I will outline a few techniques that are useful in ensuring that JavaScript content will degrade in a useful way for users with browsers which do not support JavaScript. While some of these techniques apply to all web applications, I write particularly with Web Forms in mind.

1) Do not hide elements that are essential to page context, and can only be shown using JavaScript

A common use of jQuery, Scriptaculous and other JavaScript libraries, is the ability to lay related information into tabs which are switchable without changing the page context, and without a postback to the server. As such the page is often laid out with the first tab visible, and the others hidden by CSS, waiting for the JavaScript event which triggers other tabs being shown and hidden. However, this alienates people without JavaScript support, in that they are stuck with just the first tab visible. There are two ways in which we can deal with this problem. Firstly, we could implement this functionality using PostBacks (asp:MultiView, with asp:Buttons styled correctly, to switch tabs), getting data from a library which is also presented as an HttpModule. The front end can then, in the document.ready method override the asp:Buttons functionality to call off to the HttpModule directly using an AJAX call and format the tabs accordingly. Secondly, and more simply, we could allow the tabs to degrade as a simple list at the top of the page, with anchor named links allowing the user to jump to areas of the page. This method is preferable since the anchor named links (denoted by a #name on the end of the URI), enable the developer to ensure that history is maintained when the jumps are captured with JavaScript, allowing the user to switch tabs and use the back button on their browser to switch back a tab.

2) Be cautious in use of LinkButton and AutoPostback

Both LinkButton and AutoPostback require JavaScript in order to function correctly. Of course clients sometimes want this functionality, but this can be mitigated in both cases. Firstly, with LinkButtons, use a simple anchor tag, which links to a page which parses QueryString arguments. If a PostBack is absolutely required use an asp:Button and use CSS to style it to appear as a link. Secondly, with AutoPostback, this is a piece of functionality oft requested. We can enable AutoPostback, but in addition add an asp:Button which points to the same method as the, for example, asp:DropDown change handler. This button can then be hidden using JavaScript on the page load. Therefore, if the user has JavaScript enabled, the functionality is as expected, if not, the AutoPostback will not work, but the Button will appear.

3) Where Flash is used, use swfobject and insist on a functional alternative

swfobject is a great library which allows injection of Flash based on the browsers capability to display it. As part of the arguments to inject the Flash object into the page, swfobject accepts a div in which the Flash sits. As part of the design of the site, always insist that a reasonable, even with low functionality, alternative is designed. This can then be positioned in the div that the Flash replaces. If the user does not have the correct version of Flash, the alternative is presented and does not disrupt the user’s experience of the site. As an example, I did this with a Flash file that presented a “Coverflow” style interface to the user; the non JavaScript alternative was simply the current focussed image with left and right arrows, which triggered PostBacks to jump to the next image. Though not as highly functional as the Flash object, this presented users without a JavaScript/Flash capable browser/machine, with an alternative that served the purpose, and put across the message adequately.

September 17, 2010

Code driven auto-model generation using DynamicObject

The DynamicObject base class is a new base class that was released with version 4 of the .NET framework. The DynamicObject base class gives us a number of very cool pieces of functionality, particularly in ORM applications, which will be very familiar to users of interpreted languages such as PHP, Python, Ruby, etc.

DynamicObject sits in the System.Dynamic name space, and gives us inverse functionality of the dynamic keyword. Where the dynamic keyword, when applied to a variable allows method resolving at runtime, such as

dynamic myObj = new MyObj();

//MyObj may or may not have a method called run, this will be resolved at runtime;

DynamicObject allows an object to resolve, at runtime, calls to it’s own methods. Instances of DynamicObject will preferably be instanciated as dynamic objects, as above, but in addition to the call being resolved at runtime, the object itself will respond to the call at run time.

To this end DynamicObject gives us a number of basic methods to override, which will allow us to access and set Properties (TryGetMember, TrySetMember) and Methods (TryInvokeMember). In addition, it allows us to specify what happens when the object is interacted with as an array (TryGetIndex, TrySetIndex, TryDeleteIndex).

As an example, the TryGetMember may be overridden as follows

FieldCollection myCustomFields = new FieldCollection();
PropertyCollection properties = new PropertyCollection();

public override bool TryGetMember(GetMemberBinder binder, out object result)
	//Check if we hold a field of this type
	if (myCustomFields.ContainsField(binder.Name))
		//return the property object associated with that name
		result = properties[binder.Name];
	return base.TryGetMember(binder, out result);

As can be seen this code is implemented very simply, and, in this case, we can use a simple dictionary structure to store names against values.

Where this can really come into its own is in auto generating models based in database definitions. Using the metadata views in SQL Server (all_objects, all_columns etc.), we can pull the fields for a table, and, in the constructor of the dynamic object populate our fields collection. This means when we do CRUD operations, such as a load, we can build queries dynamically, based on the composition of the model. The best thing about this is that no objects need to be changed when database structure is changed, new properties are simply accessed as a property of the dynamic object.

As such we can build up the Fields by pulling all User generated fields for a particular table from all_columns view in a SQL database, filtering by the object name, joining to all_objects view, and the object type (‘U’ for user).

Using this pattern we can build a base class which needs very little extension to handle the vast majority of objects, and we only extend when we need to implement validation, and individual business logic.

September 16, 2010

Provisioning taxonomy type site columns in a feature

Taxonomy type site columns give us the same issues as Lookup type site columns have historically, with a slight twist.

Where, with Lookup fields, we can specify all the attributes in the XML correctly but they don’t get picked up correctly; with Taxonomy fields we don’t have attributes for all the necessary pieces of information – particularly the TermSetId and the SspId (Id of the Term Store).

To get round this issue, we can use a similar method to that which we used for Lookup fields in MOSS 2007, whereby we attached a feature receiver to correctly set the List GUID based on the List name we had specified in the CAML field definition.

To this end we firstly need some CAML in our Elements file, to provision the Site Column

<Field ID="{5d0d24ee-c332-465d-8efc-83d40ab615ff}"
         Group="My Fields"
         DisplayName="My Taxonomy Field"
          <Value>Managed Metadata Service</Value>
          <Value>Keywords Group</Value>

As can be seen, we have defined some additional custom properties to set the service application name, the termstore group, and the termset which we want the field to access. We must do this since there aren’t any attributes in the schema that we can use for these purposes.

In addition, Taxonomy Fields require a note type field alongside them. Above, the name can be seen stored as a custom property, so in order for this to work, we need to define this site column in CAML as follows:

<Field ID="{700a3d33-a85c-47dd-80d1-2eba10439b46}"
         Group="My Fields"
         DisplayName="My Taxonomy Field Text"
         Type="Note" />

We now need to write a feature receiver for the Site scoped feature which holds this solution element. Inside the FeatureActivated method, we want to traverse the Xml of our Elements file, and detect where the type of field is TaxonomyFieldType.

When we have that we can then update the column so that its definition is correct as follows (XML references as XPath, replace with whichever method you use – XmlDocument, XDocument, XPathDocument etc.)

//Get the current field

TaxonomyField taxonomyField = web.Fields[Field/@ID] as TaxonomyField;

TaxonomySession taxonomySession = new TaxonomySession(||Current site from properties.Feature.Parent||);

string sspName = {Property[Name='SspName']/Value}
string groupName = {Property[Name='TermGroup']/Value}
string termSetName = {Property[Name='TermSet']/Value}
string textFieldId = {Property[Name='TextFieldId']/Value}

//Set the term store id
taxonomyField.SspId = session.TermStores[sspName].Id;

//Set the term set id
taxonomyField.TermSetId = session.TermStores[sspName].Groups[groupName].TermSets[termSetName].Id;

//Set the text field's id
taxonomyField.TextField = new Guid(textFieldId);

//Save the changes

Note: TaxonomyField and TaxonomySession classes used above are from the Microsoft.SharePoint.Taxonomy assembly/namespace, located in 14/ISAPI; you will also need to import System.Text.RegularExpressions

When this is run the Site Column will be correctly provisioned against the Managed Metadata Service. We can substitute in different values for this to work against alternative Service Applications, Groups and Term Sets.