List Definition from Content Type using Event Receiver

As a SharePoint developer, you’ve probably heard of VSeWSS (Visual Studio extensions for Windows SharePoint Services). A co-worker wrote this post which is an example of how to use VSeWSS to implement a SharePoint solution. I’d like to take that example and show you how adapt it to use the new Visual Studio 2010 SharePoint Developer Tools. Thanks to Pranab for allowing us to use his walkthrough as a point of comparison. Take a moment to read over his introduction for further background on this walkthrough. In short, we will implement a transcript document library using a Content Type with custom fields. When the user uploads a file, it is parsed by an Event Receiver and the custom properties of the item are populated with data from the file. Before proceeding ensure you have the necessary prerequisites: https://msdn.microsoft.com/en-us/library/ee231582(VS.100).aspx.

Start Visual Studio as administrator and create a SharePoint Content Type project. To do this, click File -> New -> Project… , select Visual C# -> SharePoint -> 2010, select the Content Type project template, name it “ContosoTranscriptDoc” and name the solution “ContosoTranscriptDocLib”, then click OK. In the first page of the project wizard set the Site URL field to match the site you’ll be using for deployment and debugging, then select Deploy as farm solution trust level and click Next (sandboxed trust is possible but requires changes to the sample code below). As shown below, choose the “Document” base Content Type and click Finish.

clip_image002

Next we’ll add fields to the Content Type. Expand the ContosoTranscriptDoc node in the Solution Explorer and open the Elements.xml file. Add the following lines within the <FieldRefs> tag.

       <FieldRef ID="{672B4E30-1FDD-48F3-AABC-546404D3D483}" Name="Patient" Required="TRUE" ShowInDisplayForm="TRUE" ShowInNewForm="FALSE" ShowInEditForm="TRUE" />
      <FieldRef ID="{3EC8A782-F667-4FB9-970D-D14372811E85}" Name="Doctor" Required="TRUE" ShowInDisplayForm="TRUE" ShowInNewForm="FALSE" ShowInEditForm="TRUE" />
      <FieldRef ID="{04D116F7-1381-4CB1-A60C-87A222FB86CB}" Name="PNumber" Required="TRUE" ShowInDisplayForm="TRUE" ShowInNewForm="FALSE" ShowInEditForm="TRUE" />
      <FieldRef ID="{25644618-1C4F-49EB-96DC-C1BD5BCD5253}" Name="ServiceDate" Required="TRUE" ShowInDisplayForm="TRUE" ShowInNewForm="FALSE" ShowInEditForm="TRUE" />

I’ve generated the GUIDs using the Tools -> Create GUID feature in Visual Studio.

Add the following lines before the </Elements> tag while making sure the GUIDs match what you used above:

 <Field ID="{672B4E30-1FDD-48F3-AABC-546404D3D483}" Type="Text" Name="Patient" DisplayName="Patient Name" Sealed="TRUE" StaticName="Patient"> 
</Field> 
<Field ID="{3EC8A782-F667-4FB9-970D-D14372811E85}" Type="Text" Name="Doctor" DisplayName="Attending Doctor" Sealed="TRUE" StaticName="Doctor"> 
</Field> 
<Field ID="{04D116F7-1381-4CB1-A60C-87A222FB86CB}" Type="Text" Name="PNumber" DisplayName="Patient Number" Sealed="TRUE" StaticName="PNumber"> 
</Field> 
<Field ID="{25644618-1C4F-49EB-96DC-C1BD5BCD5253}" Type="DateTime" Name="ServiceDate" DisplayName="Date of Service" Sealed="TRUE" StaticName="ServiceDate"> 
</Field> 

Our Content Type is ready for use; next up is to create the List Definition based on this Content Type. Right click the project node and select Add -> New Item, SharePoint -> 2010 and select the “List Definition from Content Type” project item template, name it “ContosoTranscriptDocLib”. The wizard opens and you can see that our Content Type is automatically selected (see screenshot below). Keep the default options and click Finish. A new List Definition using the custom Content Type is created, as well as a List Instance. Rename the ListInstance1 node in the Solution Explorer to “ContosoTranscripts”.

clip_image004

The final step of our implementation is to hook-up an event receiver which pulls data from the document and populates the custom fields of our “ContosoTranscriptDoc” list item. Right click the project node in the Solution Explorer and select Add -> New Item, SharePoint -> 2010, select the “Event Receiver” project item template and name it “ContosoTranscriptDocLibItemEventReceiver”. Note that the event source is already selected for the List Definition. Select both the “An item is being added” and “An item was added” events (see screenshot) and click Finish. This creates an Event Receiver class and hooks up the events you specified to the list.

clip_image005

The event receiver file opens automatically. Add the following code within the ContosoTranscriptDocLibItemEventReceiver class to drive the data extraction. Include the using statement “using System.IO;” at the top of the file and overwrite the generated ItemAdding and ItemAdded methods.

         /// <summary>
        /// String between MR# and DATE
        /// +4 is because "MR#:" itself has the length of 4 characters
        /// </summary>
        /// <param name="str"></param>
        /// <returns></returns>
        private string GetNo(string str)
        {
            int first = str.IndexOf("MR#:") + 4;
            int last = str.LastIndexOf("DATE:");
            string ouput = str.Substring(first, last - first);
            return ouput.Trim();
        }
        /// <summary>
        /// String between DOCTOR and SUBJECTIVE
        /// </summary>
        /// <param name="str"></param>
        /// <returns></returns>
        private string GetDoctor(string str)
        {
            int first = str.IndexOf("DOCTOR:") + 7;
            int last = str.LastIndexOf("SUBJECTIVE:");
            string ouput = str.Substring(first, last - first);
            return ouput.Trim();
        }
        /// <summary>
        /// String between DATE and DOCTOR 
        /// </summary>
        /// <param name="str"></param>
        /// <returns></returns>
        private string GetDate(string str)        
        {
            int first = str.IndexOf("DATE:") + 5;
            int last = str.LastIndexOf("DOCTOR:");
            string ouput = str.Substring(first, last - first);
            return ouput.Trim();
        }
        /// <summary>
        /// String between NAME and MR 
        /// </summary>
        /// <param name="str"></param>
        /// <returns></returns>
        private string GetPatient(string str)
        {
            int first = str.IndexOf("NAME:") + 5;
            int last = str.LastIndexOf("MR#:");
            string ouput = str.Substring(first, last - first);
            return ouput.Trim();
        }
       /// <summary>
       /// An item is being added.
       /// </summary>
       public override void ItemAdding(SPItemEventProperties properties)
       {
           base.ItemAdding(properties);
           string extension = properties.AfterUrl.Substring(properties.AfterUrl.LastIndexOf(".") + 1);
           if (extension != "txt")
           {
               properties.Cancel = true;
               properties.ErrorMessage = "Please upload only text files";
           }
       }
       /// <summary>
       /// An item was added.
       /// </summary>
       public override void ItemAdded(SPItemEventProperties properties)
       {
           base.ItemAdded(properties);
           int itemID = properties.ListItem.ID;
           string siteUrl = properties.WebUrl + "/";
           string listTitle = properties.ListTitle;
           // extract the content of the file in a string 
           TextReader tr = new StreamReader(properties.ListItem.File.OpenBinaryStream());
           string content = tr.ReadToEnd();
           tr.Close(); 
           string patient = GetPatient(content);
           string doctor = GetDoctor(content);
           string serviceDate = GetDate(content);
           string patientNo = GetNo(content);
           properties.ListItem["Patient Name"] = patient;
           properties.ListItem["Attending Doctor"] = doctor;
           properties.ListItem["Patient Number"] = patientNo;
           properties.ListItem["Date of Service"] = DateTime.Parse(serviceDate);
           properties.ListItem.Update();
       }

That’s it! We’re ready to deploy and test the customization. Remember that our tools automatically add the SharePoint Items to the feature, which is by default included in the package. Here is a screenshot of the feature designer which shows the included SharePoint items and files for deployment. Feel free to update the feature title and description with more descriptive text as I’ve done below.

clip_image007

Let’s test our solution. Start debugging of the project (F5) and wait for the SharePoint site to appear. The new ContosoTranscriptDocLib – ContosoTranscript List should be visible in the quick-launch bar. Open the document library and then click the “Add new item” link. A zip file is attached to this post which contains sample input files (there are also some on Pranab’s original post). Browse to the sample file of your choice and click OK. The event receiver will execute and automatically populate the custom fields. Here is a screenshot with the results of the parsing. You can add breakpoints to the event receiver to step through the code.

clip_image009

As you can see, the new Visual Studio 2010 SharePoint Developer tools enable rapid implementation and deployment of SharePoint solutions. We’re excited to hear any feedback you have on our feature set.

- Erik Cutts

Contoso_Transcipts.zip