Monday, March 8, 2010

SharePoint 2010 - ListInstance CustomSchema attribute

The CustomSchema attribute of the ListInstance feature element is a new attribute introduced in SharePoint 2010. What it does is pretty simple – it allows you to change the metadata of the list instance that you create (I was complaining about the lack of this capability in SharePoint 2007). The idea is pretty simple – in the CustomSchema attribute you specify the feature root relative path of a list schema file which is actually a normal list definition schema.xml file and that is basically it – you have all metadata elements from the schema in your new list instance. So, it doesn’t seem like a big deal because you can achieve the same thing with a custom list definition and a list instance based on it – and yes this is obviously the preferred thing to do when you have many instances that should be based on the same list schema. But on the other hand if you have many list instances all with different schemas (basically with some minor customizations – several new fields and some list views) you can go on with this kind of short-cut implementation which is far more economical. Another good thing to know here is that since the list schema file is the same as in a list definition you can always promote it to a custom list template definition (with just adding a list definition element and moving the schema to it) in case you need to create more than one list instance based on that schema. The opposite thing is also possible – you can “demote” a list definition item with just keeping its schema for the CustomSchema attribute of a ListInstance and removing the list definition element itself. Note here that the list schema file that you use in the CustomSchema should contain all major elements of the List/MetaData section – ContentTypes, Fields, Views, Forms – it’s not like the ListInstance element uses its base list definition (specified in the FeatureId attribute) and applies the CustomSchema as modifications on top of it – actually the list schema specified in the CustomSchema attribute should be a fully-fledged list schema and if you miss some of its elements you will receive various errors on feature activation or later when you are using the list. And since you have a fully-blown list schema it is that straightforward to have the schema reused to or from a custom list definition.

Another advantage of using a ListInstance with CustomSchema is that it can be tested very easily: you don’t need an iisreset (or recycle the app pool) after you make a change in the list schema – you just need to delete the list instance and create it again. Actually you can first create and test your list schema this way and only after put it in a custom list definition.

Another thing that may be bothering is the size of the list schema files – you remember the huge schema.xml files containing tons of CAML in SharePoint 2007 – well, this is not the case any more – except several mandatory items you can now put mostly the customizations that you need and the schema file is still pretty small – several kilobytes or so – I will show you a sort of minimalistic schema.xml file below.

So here is a sample ListInstance element with a CustomSchema attribute:

  <ListInstance Title="ListInstance2"

                OnQuickLaunch="TRUE"

                TemplateType="100"

                FeatureId="00bfea71-de22-43b2-a848-c05709900100"

                Url="Lists/ListInstance2"

                CustomSchema="ListInstance2/Schema.xml"

                Description="">

And the custom (minimalistic) list schema file that I used for it:

<List xmlns:ows="Microsoft SharePoint" Title="Basic List" EnableContentTypes="TRUE" FolderCreation="FALSE" Direction="$Resources:Direction;" Url="Lists/Basic List" BaseType="0" xmlns="http://schemas.microsoft.com/sharepoint/">

  <MetaData>

    <ContentTypes>

      <ContentTypeRef ID="0x0100678499b7e7024385820d8586270c1a75" />

    </ContentTypes>

    <Fields></Fields>

    <Views>

      <View BaseViewID="1" Type="HTML" WebPartZoneID="Main" DisplayName="$Resources:core,objectiv_schema_mwsidcamlidC24;" DefaultView="TRUE" MobileView="TRUE" MobileDefaultView="TRUE" SetupPath="pages\viewpage.aspx" ImageUrl="/_layouts/images/generic.png" Url="AllItems.aspx">

        <XslLink Default="TRUE">main.xsl</XslLink>

        <RowLimit Paged="TRUE">30</RowLimit>

        <Toolbar Type="Standard" />

        <ViewFields>

          <FieldRef Name="Attachments" />

          <FieldRef Name="LinkTitle" />

          <FieldRef Name="Comments" />

        </ViewFields>

        <Query>

          <OrderBy>

            <FieldRef Name="ID" />

          </OrderBy>

        </Query>

      </View>

    </Views>

    <Forms>

      <Form Type="DisplayForm" Url="DispForm.aspx" SetupPath="pages\form.aspx" WebPartZoneID="Main" />

      <Form Type="EditForm" Url="EditForm.aspx" SetupPath="pages\form.aspx" WebPartZoneID="Main" />

      <Form Type="NewForm" Url="NewForm.aspx" SetupPath="pages\form.aspx" WebPartZoneID="Main" />

    </Forms>

  </MetaData>

</List>

A short explanation about the list schema above – so you see all major elements – ContentTypes, Fields, Views and Forms in the MetaData section – the Fields element even empty should be present (yes, otherwise you will receive an error on feature activation). In the ContentTypes element there is a single ContentTypeRef element which attaches a site content type to the list, the Fields element should normally contain all fields that should exist in the list, including the ones that are in the attached content types. A pretty nasty thing here of having to define a set of fields first as site columns and then in every list definition that uses the containing content type, isn’t it. I will demonstrate a work-around for the field redefinition issue in one of the next postings. Basically instead of ContentTypeRef you can use a ContentType element and define an inline list content type which uses site and/or list columns (both types alike provided in the Fields element).

In the Views element I’ve defined a single list view – pretty simple at that. You can see several FieldRef elements in the ViewFields of the view, the last one is actually for a field from the referenced site content type  - don’t worry that the field is missing in the Fields element – this won’t break the activation of the feature and can be fixed afterwards. The last metadata element is the Forms one – it is simply copied from a standard list definition schema and just defines the new, edit and display forms of the list.

The last bit in the sample schema worth noting is the EnableContentTypes attribute in the schema root element. It should be set to true even if you have just one content type in the list, otherwise in the new item menu in the all items view page of the list you will see the default “new item” label instead of the actual name of your content type.

31 comments:

  1. Thanks for posting this solution, it got me one step closer ot my goal.
    There is just one snag with this great solution....
    The views you define here are only visible from the list itself. When you post a webpart onto the page using the onet (beware, you must use the list url and not the base type, or it will crash)
    then the views being displayed on the webpart will be taken from the base schema and not your custom schema :-(

    The problem I have, is, if you create a custom task list with a custom Ct attached to it, the connect to Outlook button disappears. How annoying. But with your approach, the button stays, and my CT is added, but the webpart provisioning does not show my custom fields.

    ReplyDelete
  2. Hi Alex,
    this is an interesting finding - so after all the schema of the original list template specified in the ListInstance element still affects some things about the list behavior (I haven't check that yet myself).
    Actually I've had the problem with the View element in Module element provisioning before, especially when I use standard list template for the list instance but I still want to have a customized (XSLT) list view web part on a page. And normally I solve this using code - the idea is to get the hidden SPView of the (XSLT) list view web part - this is possible through its ViewGuid property - you just create a Guid of it and then use SPList.Views[Guid] to get the SPView instance. With the latter you can modify the ViewFields, Query, etc properties. You can also check this posting of mine - http://stefan-stanev-sharepoint-blog.blogspot.com/2010/02/listviewwebpart-spview-two-sides-of.html - it demonstrates a sample method for updating the hidden SPView of a (XSLT) list view web part - the difference is that the SPView is retrieved by iterating all SPViews and matching the SPView.Url property which will contain the URL of the page that contains the web part.

    ReplyDelete
  3. Hi Stefan,

    Thanks for the interesting post. I have one question though. I want to create a feature for a document library with default folders in it. So when I activate the feature, the folder structure must be present in the document library. I know this should be possible using a listinstance, but I don't know which files are needed for this. Can you help me out?

    Thanks, Kevin

    ReplyDelete
  4. Hi Kevin,
    you can provision folders in lists this way:
    for non-library lists you can use the Data element below the ListInstance element like:

    <ListInstance TemplateType="13011" Title="ListTitle" Description="" Url="Lists/ListUrl" FeatureId="GUID">
    <Data>
    <Rows>
    <Row>
    <Field Name="BaseName">folder1</Field>
    <Field Name="FSObjType">1</Field>
    </Row>
    </Rows>
    </Data>
    </ListInstance>

    and for document libraries you can use an empty Module element which you can place just below the ListInstance element in your elements file:

    <Module Path="folder1" Url="MyDocuments/folder1" ></Module>

    ("MyDocuments" is the site relative url of your library)

    ReplyDelete
    Replies
    1. Hi,

      Thanks for sharing such a useful finding.

      I am actually looking for something around the provisioning data as you explained and I have a question.

      Do you have any idea how big the Data could be, I mean to say how many rows can we provisioned during feature activation? Is it possible to provision like 2000 rows while list instance get created.

      Any pointers will be highly appropriated.

      Thanks.

      Delete
    2. Hi Usman,

      I haven't tried myself with so many rows but I don't think it will be a problem to provision several thousand list items using this approach. It won't be the fastest way to batch provision list items though - the internal implementation uses the SPListItemCollection.Add method. All in all I don't expect that the feature activation will take more than one minute so if you don't have problems with that go for it.

      Greets
      Stefan

      Delete
  5. Hi Stefan,

    Thanks for your reply. Ok, so now I know what to put in the Elements file, but which other files do I have to create? Thnx

    ReplyDelete
  6. Hi Kevin,

    you don't need other files except the Elements file and the feature.xml for the folders to get provisioned.

    ReplyDelete
  7. Thanks! It worked. Saved me a lot of time.

    One other question though... Do you know if it is possible to set permissions on those folders from within the elements.xml or feature.xml?

    ReplyDelete
  8. Hi Kevin,

    I am afraid that you can't specify permissions for folders from the elements file - you will need some custom code within a feature receiver for that.

    ReplyDelete
  9. HI Kevin thanks for Sharing Knowledge
    I have scenario like i want to create list instance based on asset library but i don't want all OOTB CTs like image,audio and video but i want custom content type with site columns so i am thinking custom schema is right choice for this.So i dive in to VS 2010 created sharepoint empty project with Fields,Content type and list instances as project item but i dont know how to go for schema.xml file please advise me

    ReplyDelete
  10. Hi Ronak,
    So you need to do the following (let's suppose that you have a feature element named "MyList"):
    - first you need to add the schema.xml file just below the feature element
    - then you need to set in the properties of the schema.xml the deployment type from "NoDeployment" (which is the default one) to "ElementFile"
    - and finally you need to set the "CustomSchema" attribute of your ListInstance element to "MyList/Schema.xml" (note that "MyList" is the name of the feature element - this path will also appear in the deployment location property of the schema.xml).

    ReplyDelete
  11. Thanks Stefan for your reply.Will try as per your suggestion i can also copy and paste out of box schema.xml file for asset library and then modify as per my req right ?

    ReplyDelete
  12. Did you try using CustomSchema attribute in List element from onet.xml? MSDN says it should be possible (http://msdn.microsoft.com/en-us/library/aa543874.aspx) but same code which works in ListInstance doesn't work in this case.

    ReplyDelete
  13. Hi Stefan i was following this post in order to create a simple List instance for Category list based on Custom List Def but during deployment i got this error Error occurred in deployment step Activate Features': The file Template\Features\LookupSiteColumn_CategoryList\Category/schema.xml does not exist. i am deploying as Sandbox solutions and i also have Schema.xml(deployment Element File) file under Category List Instance Item in VS 2010.

    Please advise.
    Thanks
    Ronak

    ReplyDelete
  14. alright its working now i just change path from ListInstance2/Schema.xml to ListInstance2\Schema.xml

    Thanks now its working like charm just not getting work for lookup column though

    ReplyDelete
  15. Hi Cristian,
    I tried this with the List element in the onet.xml and couldn't get it working either, I am not sure whether the CustomSchema attribute is defined only in the XSD of the onet.xml and ignored by the SharePoint runtime. I experimented quite a lot with placing the schema.xml file in the root folder of the site definition, in the XML and LISTS subfolders but unfortunately all attempts failed.

    ReplyDelete
  16. Hi Ronak,
    Glad you got it working, I see that I used this slash "/" in the posting sample and I remember I got that from a working test project, but it maybe a bug after all.

    ReplyDelete
  17. using this method, my content type's columns are added to the list, but the content type still shows as "Item". When I click on the "Item" content type, it has duplicates of all of the fields of the content type I'm referencing. Any ideas why this is happening?

    ReplyDelete
  18. Ugh....found the solution to the duplicates issue. Close and reopen Visual Studio!

    ReplyDelete
  19. Nice post...Is there any way of specifying a custom AddForm or EditForm.aspx page for the ContentTypeRef? Since ContentTypes cannot have custom add or edit forms, they must be specified on the list.

    ReplyDelete
  20. Hi Clem,
    Actually it is possible to specify custom add and edit forms for content types - check this MSDN article: http://msdn.microsoft.com/en-us/library/ms473210.aspx (it is about the FormUrls document schema that can be used in the content type definition XML)
    Additionally you can check the FormTemplates document schema here: http://msdn.microsoft.com/en-us/library/ms468901.aspx which also can be used for customizing the add and edit form for a specific content type (in this case the add and edit form pages are the original ones defined by the list definition but the form templates used inside them can be set to be different from the default ones for the list).
    Stefan

    ReplyDelete
  21. Hello. I have found little problem in UseLegacyForm attribute in Form in schema.xml.
    When I create ListInstance(not list definition) and create custom schema.xml and create also my new forms and add to<form useLegacyForm="TRUE" it doesn't ignore webpartzone and still generate fields in this zone... When I create listdefinition and then in schema.xml define form UseLegacyForm="TRUE" it doesn't generate in webpartzone my fields and I must generate it by my own....Hmm...Why I can not use in ListInsance in schema.xml UseLegacyForm? Thank You

    ReplyDelete
  22. Hi Anonymous,
    Honestly this behaviour was totally new to me. I can only assume that this is by design (unfortunately as many things in SharePoint). It turns out that the schema.xml works differently in list definitions and in the ListInstance element with the CustomSchema attribute at least in some cases (the first comment in this posting deals with another such case).

    ReplyDelete
  23. "...A pretty nasty thing here of having to define a set of fields first as site columns and then in every list definition that uses the containing content type, isn’t it. I will demonstrate a work-around for the field redefinition issue in one of the next postings..."

    Do you happen to have that demo code still lying around?

    ReplyDelete
  24. Hi John,

    it's in the next posting: http://stefan-stanev-sharepoint-blog.blogspot.com/2010/03/contenttypebinding-vs-contenttyperef.html

    ReplyDelete
  25. Hi Stefan!
    Is it possible to define more than 2 views in addition to defined ones with BaseViewId='0' and BaseViewId='1' using schema referenced by 'CustomSchema' attribute in list instance?
    Regards, Sergey.

    ReplyDelete
  26. Hi Sergey,

    yes, it is possible to create more than 2 views in the list schema - I don't think that there is an upper limit for this.

    Greetings
    Stefan

    ReplyDelete
  27. Thank you for this excellent article!
    René

    ReplyDelete
  28. SharePoint 2010 CustomSchema list is good....

    ReplyDelete
  29. Over 4 years old and still helping people. Excellent article - thanks!

    Bob

    ReplyDelete