Custom add/edit/display form for list in SharePoint


Sometimes we don’t want to use SharePoint’s custom add/edit/display form. We want our own custom form when user will try to view, edit or add an item. I’ll show you today how easily we can do so.
For this post I’ll consider I’ll have a list with three fields: Product Code, ProductName and ProductDescription. I’ll show how we can create a list with these two fields with custom add/edit/display form. The list’s fields are described in the table below:
Field nameField TypeComments
Product CodeTextTitle field will be used instead of creating a new one
Produce NameText
Product DescriptionText
Table 1: Custom list template/Content Type’s fields

The first step of this approach is to create a content type with required fields associated with the content type. The noticeable point here is that In the content type declaration, we can define custom forms for add/edit/display.

Step 1: Create a content type for your list

To create content type right click on your project and click ‘add new item’ and then select content type as shown below:
image
Figure 1: Add content type

Next you will be prompted for the base content type as shown below: If you want to create a custom list, you can select Item as shown below:
image
Figure 2: ‘Item’ is the base content type for custom/generic list

Then you will have provided the content xml file. You need to modify the content type xml file as shown below. Please modify the Inherits=”False” from the content types.
<?xml version="1.0" encoding="utf-8"?>
<Elements xmlns="http://schemas.microsoft.com/sharepoint/">
  <!--Defined fields-->
  <Field ID="{30C3D21A-A7C9-410E-A896-82875475F697}" Name="ProductName"
         DisplayName="Product Name" Type="Text" Required="FALSE" >
  </Field>
  <Field ID="{9621763e-3494-4a86-a3eb-fd2593f1a1f1}" Name="ProductDescription"
         DisplayName="Product Description" Type="Text" >
  </Field>
  <!-- Parent ContentType: Item (0x01) -->
  <ContentType ID="0x0100c5e54b7f62ad451a92f9235d43ec9082"
               Name="ProductContentType"
               Group="Custom Content Types"
               Description="My Content Type"
               Inherits="false"
               Version="0">
    <FieldRefs>
      <FieldRef ID="{fa564e0f-0c70-4ab9-b863-0177e6ddd247}" Name="Title" 
                DisplayName="Product Code" Sealed="TRUE"/>
      <FieldRef ID="{82642ec8-ef9b-478f-acf9-31f7d45fbc31}" Name="LinkTitle" 
                DisplayName="Product Code" Sealed="TRUE"/>
      <FieldRef ID="{BC91A437-52E7-49E1-8C4E-4698904B2B6D}" Name="LinkTitleNoMenu" 
                DisplayName="Product Code" Sealed="TRUE" />
      <FieldRef ID="{30C3D21A-A7C9-410E-A896-82875475F697}" Name="ProductName" 
                DisplayName="Product Name" Required="False" />
      <FieldRef ID="{9621763e-3494-4a86-a3eb-fd2593f1a1f1}" Name="ProductDescription" 
                DisplayName="Product Description" Required="False" />
    </FieldRefs>
    <XmlDocuments>
      <XmlDocument NamespaceURI="http://schemas.microsoft.com/sharepoint/v3/contenttype/forms/url">
        <FormUrls xmlns="http://schemas.microsoft.com/sharepoint/v3/contenttype/forms/url">
          <Display>_layouts/blogtest/Product.aspx?mode=display</Display>
          <Edit>_layouts/blogtest/Product.aspx?mode=edit</Edit>
          <New >_layouts/blogtest/Product.aspx?mode=new</New>
        </FormUrls>
      </XmlDocument>
    </XmlDocuments>
  </ContentType>
</Elements>
Figure 3: Content Type xml file with fields and add/edit/display form declared

Now let’s explain what’s in the xml shown in figure 3.
  • Firstly I’ve modified Inherits to false in ConentType tag.
  • I’ve defined two fields inside the <Elements> tag that I’ve used later in content types
  • Then used those fields in <FieldRefs> of <ContentType> tags. These fields will be available in Content type. I’ve also used three existing fields (for Title) from base Content Type (Item).
  • Finally I’ve defined New, Edit and Display form for these content types in <XmlDocuments> section.


Step 2: Create a list Template based on Content type

Now you have defined content types with three fields. Next step is to define a list template based on the content type. To do so click add new item from visual studio context menu and select “List Definition From Content Type” as shown below:
image
Figure 4: Create list definition from content type in ‘Create new Item’ dialog.

Next you will be prompted for available  content types in the project as shown below. Remember to uncheck the button ‘Add a list instance for this list definition’ for this demo now.
image
Figure 5: Create list definition from Content Type

Now you will find two files Elements.xml and Schema.xml files are added. Our full focus will be now on Schema.xml.

Modify the content in <Fields> tag:
Ensure Title fields with display name ‘product code’ exists as shown below:
<Field ID="{fa564e0f-0c70-4ab9-b863-0177e6ddd247}" Name="Title" DisplayName="Product Code" Sealed="TRUE" Type="Text" />
image
Figure 6: Add title field in the list template (if not exists)

Then find two fields LinkTitle and LinkTitleNoMenu. Then change their display name to ‘Product Code’ as shown below. These two fields are link to edit menu.
image
Figure 7: Rename the displayName for linkTitle and LinkTitleNoMenu field

Modify the content in <Views> tag
Open the views tag and add the fields you want to display in default view under <View> with Default value is true as shown below.
image
Figure 8: Define the fields to be shown in default view

 

 

Step 3: Create Custom add/edit/display form

Next step is to develop a custom application page to use for add/edit/display. As sown in figure 3, you can three different pages for add, edit and view. However for brevity I want to use a single page for all these three operations. You need to create an application page in appropriate location (in my case this is _layouts/blogtest folder). Rather than using three different files for add/edit/display, you can use a single page for all these three tasks as shown below:
<XmlDocuments>
  <XmlDocument NamespaceURI="http://schemas.microsoft.com/sharepoint/v3/contenttype/forms/url">
    <FormUrls xmlns="http://schemas.microsoft.com/sharepoint/v3/contenttype/forms/url">
      <Display>_layouts/blogtest/Product.aspx?mode=display</Display>
      <Edit>_layouts/blogtest/Product.aspx?mode=edit</Edit>
      <New >_layouts/blogtest/Product.aspx?mode=new</New>
    </FormUrls>
  </XmlDocument>
</XmlDocuments>
By passing different parameter to a single page we can identity the page’s mode (add, edit or view). Also SharePoint by default add the list and item id at the end of the page. So your page’s url will look like for new item:
_layouts/blogtest/Product.aspx?mode=new&List=LISTGUID&ID=ITEMID
So from the page (Product.aspx) you can identity the list id and item id from querystring.
I’m not showing details of the product.aspx page here.
You can download the full source code from this skydrive link.

How to use the project attached with this post?

  1. Download the code from here.
  2. Deploy the solution to a SharePoint site.
  3. Create a new list with “ProductListDefinition” template. This template will be installed in the site as you deploy the SharePoint solution.
  4. Now try to add/edit/view items in the list. You will find the custom form product.aspx is used for add/edit/view.

SharePoint 2013: App Concept


SharePoint 2013 has added a bunch of new concepts for apps. You can think of a SharePoint app just like iPhone app or Android app for time being. If you have not already setup the on-premises development environment for SharePoint 2013 apps development, please follow the link Set up an on-premises development environment for apps for SharePoint to setup your development environment.

Install/Host an App

You can install the app in a SharePoint web (called host web for the app). When you install an app in the SharePoint web (called host site), the app needs a place to exist  where app’s css, javascripts, pages etc will be placed. The app resources (like pages, css, javascript etc) are not stored/kept inside host web. Where the app’s resources are stored/kept basically depends on app’s hosting type. The app can be hosted in any of the followings:
  • inside SharePoint (called SharePoint hosted)
  • You can host app in Cloud. You have two options for cloud-hosting apps
    • Windows Azure (called AutoHosted)
    • Third-party solution providers can provide setup for apps (called Provider-hosted)
You can get more details on this hosting options at MSD link.

The app can add some links/actions (like ribbon, ECB menu etc) in the host web. But the app itself doesn’t live inside host site.  When you use SharePoint hosted option for an app and as soon as you install the app in a host web another web (usually subsite to host site) is created (called app web) for the app components to be installed. The relation to host and app web for SharePoint hosted app, is shown below:
image
Figure 1: SharePoint hosted Apps are usually installed under subsite of Host web
For other options (like provider hosted and windows azure hosted app), app web is optional and the actual app web resources are stored in windows azure or third party servers.
Though app webs are installed under subsite of host web usually, for SharePoint hosted app, it doesn’t mean you can access the app web directly with url. Rather the apps are only accessible from different url. The App web is accessible from different url (not like host-web url). This is to keep the app webs isolated from host web. Usually these two types of webs urls (host and app web) belongs to two different domains. You can find more details on this host web url and app web url from this MSDN link.

 

App Types (From users’ points of view)

Now question comes what types of apps we can develop and how they will look like from user’s point of view. You can do the following types of things with apps:
UI Custom Actions
An app can add links like ribbon, custom actions or ECB menu to the host web. When user will click on the link, user will be redirected to the app web. Let’s consider a scenario which will explain this UI custom actions in details:
  1. You installed an app (let’s call it SharePoint PDF Converter) in a SharePoint site http://mysite.mydomain.com/businessdocs (called host web). The app will add a ribbon button in the site to convert any word document to pdf. When you will install  the app ‘SharePoint PDF Converter’, a subsite will be created under host web (http://mysite.mydomain.com/businessdocs) and the app contents (like javascript, css, aspx pages etc) will be deployed in the subsite. As mentioned already app webs are not accessible by url (like http://mysite.mydomain.com/businessdocs/appwebname) directly, rather they have different url for isolation. The idea of adding ribbon button in host web shown below:image Figure 2: An app can add ribbon to the host web. 
  2. Now what will happen on user will click the ribbon button? Usually user will be redirected to the app web and app developers have the option to pass the current selected item details (like id, url etc) to the app web. The concept of redirecting from host web to app web and having the app web taking control of the full browser page is called ‘Immersive Full Page’ user experience.
You can get more details on custom action app types on MSDN link Create custom actions to deploy with apps for SharePoint.
 
App Part
Apps can also add web-part like stuffs in the host web called Part (or app part). You can think of an ‘app part’ as like web part but it’s provided by app web. You can think of it as just like web part but instead of Farm WSP solution this app part comes from app deployment to your host web. You can find more details on this app part on MSDN link Create app parts to deploy with apps for SharePoint. The following image shows an app part that is available to be added in the host site:
image
Figure 3: Add an app-part (just like adding webpart)
Once you add the app-part the page will look like below which kind of resembles the webpart concept:
image
Figure 4: App part added to the page
 
Immersive Full Page
All apps have a default page which might take the full page. Apps show custom action or app part in the host web. The app might take the full page by redirecting user from host web url to app web. For example, the host web url is http://www.hostweb.com and an app is installed in the host web which adds a custom action. When user clicks the custom action, the user will be navigated to a new url (say, http://www.appweb.com). So the app takes the full page (so it’s called Immersive Full page). However user will not notice the url changes as the new app site will have the same UI look and feel. The idea of having the app web to have similar look and feel like host web is achieve thorough a new concept in SharePoint 2013 – called Chrome Control. Basically Chrome control is kind of adding few components (js,div element etc) in App web so that when user redirect from host web to app web, the chrome control retrieves css, js from host web and apply it in the app web on the fly. Also the chrome control create a SharePoint 2013 ribbon bar in the app site, so that user can navigate back to the hot web.You can get clear idea of chrome control watching this video.

How to set PeopleEditor value using javascript in SharePoint

While developing SharePoint application, you may want to use PeopleEditor control or your own custom control derived from EntityEditorWithPicker. If you come across the problem with setting its value using Javascript, below is the code sample that may help.

<script type="text/javascript">

function SetPickerValue(pickerid, key, dispval)
{
    var xml = '<Entities Append="False" Error="" Separator=";" MaxHeight="3">';
    xml = xml + PreparePickerEntityXml(key, dispval);
    xml = xml + '</Entities>';

    EntityEditorCallback(xml, pickerid, true);
}

function PreparePickerEntityXml(key, dispval)
{
    return '<Entity Key="' + key + '" DisplayText="' + dispval + '" IsResolved="True" Description="' + key + '"><MultipleMatches /></Entity>';
}

</script>


The easiest way to set PeopleEditor’s value is to use the EntityEditorCallbak function. This function is provided with built-in SharePoint javascript functions (both WSS 3.0 and MOSS 2007). It is the same function that is used by the control to set its own value. The first two parameters of the function are important – the first one is XML containing entities definition and the second one is a client identifier of the control which value should be set. (The client identifier is the same which is available in the ClientID property on the server side).
The above code sample presents the SetPickerValue function. The function covers XML generation from a given key and display text, and calling of EntityEditorCallback function.

Example of SetPickerValue call:

SetPickerValue('ctl00_PlaceHolderMain_pplEdit', 'domain\\m.kapusta','Marcin Kapusta');

Generated XML (for the given call) is the following:

<Entities Append="False" Error="" Separator=";" MaxHeight="3">
  <Entity Key="domain\m.kapusta" DisplayText="Marcin Kapusta" IsResolved="True" Description="domain\m.kapusta">
    <MultipleMatches />
  </Entity>
</Entities>


Be careful when providing domain account name. Javascript treats \ (backslash) character as a special character. If you want to have it in the string, you have to use \\ instead. That’s why I had to provide ‘domain\\m.kapusta’ as a parameter to get domain\m.kapusta value in the XML.

If you want to set PeopleEditor value to have multiple entities, you can modify SetPickerValue function to generate multiple <Entity />nodes in the XML.

You can set IsResolved attribute to False to have unresolved entity in the control. When the entity is unresolved you are able to provide some suggestions (accessible in the popup menu) adding child nodes to the <MultipleMatches /> node. I don’t want to go into details about the multiple matches as it is the topic for another post.

add/delete an event receiver to a single list with PowerShell in SharePoint 2010


In SharePoint 2010 the Event Receivers has been improved so much. But there is still a big issue on the default functionality when we want to attach/register an event receiver to a single list. By default when we create an event receiver project Visual Studio creates an Elements.xml associated to the event receiver that contains the association xml generated from the information that the Visual Studio wizard asked us. Basically, this xml associates the event receiver to a content type that we specified.
So, how should we attach an event receiver to a single list? We have two options the create a little application and do it through the object model, or using the PowerShell scripting equivalent to the object model code.
Let’s go through the second option: PowerShell
The first thing that we should do is to deploy the assembly of the Event Receiver into the GAC.
After that we can create and execute our PowerShell script.
Here It’s the sample script to register an ItemUpdated List Item Event Receiver to a single list/library:
=============================================================

$spWeb = Get-SPWeb -Identity http://moss2010
$spList = $spWeb.Lists["My List Name"]
$spEventReceiver = $spList.EventReceivers.Add()
$spEventReceiver.Assembly = "Project.Name.Class, Version=1.0.0.0, Culture=neutral, PublicKeyToken=24242342424"
$spEventReceiver.Class = "Namespace.MyClass.ClassName"
$spEventReceiver.Name = "My Event Name"
$spEventReceiver.Type = 10002
$spEventReceiver.SequenceNumber = 1000
$spEventReceiver.Synchronization = 1
$spEventReceiver.Update()
=========================================================
And some explanation of each property:
- We can get all the assembly and class information with .Net Reflector from our .dll.
- What name to put in the Name property is your choice.
- The Type property is an enumerated property whose possible values are listed in the table 1.
- The SecuenceNumber property determines the execution order of this event receiver when there are more than one event receiver attached to the same list.
- The Synchronization property is another enumerated field that could have three possible values, listed in the table 2.
Finally, we should call the Update method to persists the changes.
Well, after add our event we can check which events are attached to a list with the next PowerShell script:
======================================================

$spWeb = Get-SPWeb -Identity http://moss2010
$spList = $spWeb.Lists["My List Name"]
$spList.EventReceivers | Select Name,Assembly,Type
====================================================
This script just show us Name, Assembly and Type properties of each event attached to the list.
After that, maybe we want to delete any event receiver from a list. As you can guess… Yes! another PowerShell script can save us!:
====================================================

$spWeb = Get-SPWeb -Identity http://web.spdev.local
$spList = $spWeb.Lists["My List Name"]
$eventsCount = $spList.EventReceivers.Count
$assembly = "Project.Name.Class, Version=1.0.0.0, Culture=neutral, PublicKeyToken=24242342424"
$class = "Namespace.MyClass.ClassName"
$type = 10002
$name = "My Event Name"
for ($i = 0; $i -lt $eventsCount; $i+=1)
{
if ($spList.EventReceivers[$i].Assembly -eq $assembly -and
$spList.EventReceivers[$i].Class -eq $class -and
$spList.EventReceivers[$i].Type -eq $type -and
$spList.EventReceivers[$i].Name -eq $Name)
{
$spList.EventReceivers[$i].Delete()
}
}
$spList.Update()
=========================================================
Cool, isn’t it? In the above script we go through all the events associated to a list and delete those which Assembly, Class, Type and Name values are equal to the variables values that we set first.
As I promised here are the tables with the enumerator values.
Table 1
Member nameEnum codeDescription
InvalidReceiver -1Indicates that an invalid event receiver type has been specified.
ItemAdding 1Event that occurs before an item has been added.
ItemUpdating 2Event that occurs before an item is updated.
ItemDeleting 3An event that fires before an item is deleted.
ItemCheckingIn 4An event that occurs before an item has been checked in.
ItemCheckingOut 5An event that occurs before an item is checked out.
ItemUncheckingOut 6An event that occurs before an item is unchecked out.
ItemAttachmentAdding 7Event that occurs before an attachment has been added to an item.
ItemAttachmentDeleting 8An event that occurs before an attachment has been removed from the item.
ItemFileMoving 9An event that occurs before a file is moved.
FieldAdding 101Event that occurs before a field is added to a list.
FieldUpdating 102Event that occurs before a field is updated.
FieldDeleting 103An event that occur before a field is removed from a list.
ListAdding 104Event that occurs before a list is created.
ListDeleting 105An event that occurs before a list is deleted.
SiteDeleting 201Event that occurs before a site collection is deleted.
WebDeleting 202Event that occurs before a site is deleted.
WebMoving 203Event that occurs before site a site URL has been changed.
WebAdding 204An event that occurs before a new site is created.
WorkflowStarting 501Event that occurs before a workflow starts running.
ItemAdded 10001Event that occurs after an item has been added.
ItemUpdated 10002Event that occurs after an item has been updated.
ItemDeleted 10003An event that occurs after an item has been deleted.
ItemCheckedIn 10004Event that occurs after an item has been checked in.
ItemCheckedOut 10005An event that occurs after an item has been checked out.
ItemUncheckedOut 10006An event that occurs after an item has been unchecked out.
ItemAttachmentAdded 10007An event that occurs after an attachment has been added to the item.
ItemAttachmentDeleted 10008Event that occurs after an attachment has been removed from the item.
ItemFileMoved 10009An event that occurs after a file has been moved.
ItemFileConverted 10010An event that occurs after a file is transformed from one type to another.
FieldAdded 10101An event that occurs after a field has been added.
FieldUpdated 10102An event that occurs after a field has been updated.
FieldDeleted 10103An event that occurs after a field has been removed.
ListAdded 10104Event that occurs after a list has been created.
ListDeleted 10105Event that occurs after a list has been deleted.
SiteDeleted 10201Event that occurs after a site collection has been deleted.
WebDeleted 10202Event that occurs after a site has been deleted.
WebMoved 10203Event that occurs after a site URL has been changed.
WebProvisioned 10204An event that occurs after a new site has been created, but before that new site is provisioned.
WorkflowStarted 10501Event that occurs after a workflow has started running.
WorkflowPostponed 10502Event that occurs after a workflow has been postponed.
WorkflowCompleted 10503An event that occurs after a workflow has completed running.
EmailReceived 20000Event that occurs after a list receives an e-mail message.
ContextEvent 32766Identifies workflow event receivers, and is therefore not a true event type.
Table 2
Member nameEnum code
Default 0
Synchronous 1
Asynchronous2
Tip: If we want avoid that the event receiver is registered to any content type, we should delete the Elements.xml from the VS project.

How to Implement Site event receivers in SharePoint 2010


In SharePoint 2010 we now have a new type of receiver which is related to site. We now can track when a web is adding, provisioned, moving, moved, deleting and deleted.

All receivers that end with ing are synchronous and those which ends with ed are asynchronous.

So let us go ahead and explore some of the receiver types and try to understand how it works.

Open up visual studio 2010 and select SharePoint 2010 and select event receivers in the template type.



Connect with the site and select farm solution.



Select Web receivers from the list. Let us select these four options and explore them. Rest you can check on your own and let us know if you face any issue.



You then will be presented with four receivers.



Go ahead and add following code in each of them. Now build and deploy the solution. And now try creating site, and then delete the site.

public override void WebDeleting(SPWebEventProperties properties)
       {
           //This code actually triggers for sub sites that is being deleted. Hence we have taken reference of parent web to write the entry.

           SPList lstTracking = properties.Web.ParentWeb.Lists["Track Different Events"];

           properties.Web.ParentWeb.AllowUnsafeUpdates = true;

           SPListItem item = lstTracking.Items.Add();

           item["Title"] = "Deleting the Web";
          
           item.Update();

           properties.Web.ParentWeb.AllowUnsafeUpdates = false;
         
       }

       ///


       /// A site is being provisioned.
       ///

       public override void WebAdding(SPWebEventProperties properties)          
       {
          
           //This handler will trigger at the time of web being added as a sub site. Track Different Events list is in the parent site.

           SPList lstTracking = properties.Web.Lists["Track Different Events"];

           properties.Web.AllowUnsafeUpdates = true;

           SPListItem item = lstTracking.Items.Add();

           item["Title"] = "Adding the Web";

           item.Update();

           properties.Web.AllowUnsafeUpdates = false;

          
       }

       ///


       /// A site was deleted.
       ///

       public override void WebDeleted(SPWebEventProperties properties)
       {
           //This code actually triggers for sub sites that is deleted. Hence we have taken reference of parent web to write the entry by
           //instantiating SPSite. properties.Web returns null as web has been deleted by this time.

           SPSite site = new SPSite("");

           SPWeb web = site.AllWebs["ECMA"];

           SPList lstTracking = web.Lists["Track Different Events"];

           web.AllowUnsafeUpdates = true;

           SPListItem item = lstTracking.Items.Add();

           item["Title"] = "Web has been deleted";

           item.Update();

           web.AllowUnsafeUpdates = false;
       }

       ///


       /// A site was provisioned.
       ///

       public override void WebProvisioned(SPWebEventProperties properties)
       {
           //This code actually triggers for sub sites that is created. Hence we have taken reference of parent web to write the entry.          

           SPList lstTracking = properties.Web.ParentWeb.Lists["Track Different Events"];

           properties.Web.ParentWeb.AllowUnsafeUpdates = true;

           SPListItem item = lstTracking.Items.Add();

           item["Title"] = "Web has been provisioned";

           item.Update();

           properties.Web.ParentWeb.AllowUnsafeUpdates = false;
       }

We've created a site. So now we can see two entries in the list.




Now delete the sub site and we get this.