Saturday, May 25, 2013

Provision list view web parts declaratively and modify their views in Sharepoint

You may provision list views web parts as any other web part by adding it to Module > File > AllUsersWebPart in the feature’s element manifest. In this case you have to add xml declaration of the web part inside AllUsersWebPart (you may get it if e.g. will add list view web part on the page manually and then will export it to the file on disk). However there is more simple way to provision list view web parts using View element:

   1: <Module Name="MyPages" Url="$Resources:cmscore,List_Pages_UrlName;" Path="pages">
   2:   <File Url="default.aspx" Type="GhostableInLibrary" Level="Published">
   3:     <View
   4:         List="Lists/MyList"
   5:         BaseViewID="3"
   6:         Name="MyList"
   7:         WebPartZoneID="RightWebPartZone"
   8:         WebPartOrder="0" />
   9:   ...
  10:   </File>
  11: </Module>

In this example we provision list view web part on the default.aspx page into RightWebPartZone. This web part will use list view with BaseViewID=3 for list with url Lists/MyList on the current web site. BaseViewID can be retrieved from query string when you choose create new view of specific type from the list settings of ribbon.

However this web part will use default settings for the selected list view, e.g. it will have default columns and sorting settings. In some cases we may need to modify the list view used by web part, e.g. add new columns, remove columns which were added by default, change sorting settings, etc. In order to do that we need to understand what happens behind the scene when we provision list view web part like this.

If you will check the views for the list for which we provision web part e.g. in Sharepoint Manager and then compare them with list, which was created by the same template, but without provisioned list view web part, you will find that during provisioning web part creates additional view with specified BaseViewID and with Hidden=true. I.e. this view won’t be visible from UI. So if we want to modify this view, we need to get instance of appropriate SPListView object and then manipulate its properties. We can do it e.g. in feature receiver:

   1: public class MyFeatureReceiver : SPFeatureReceiver
   2: {
   3:     public override void FeatureActivated(SPFeatureReceiverProperties properties)
   4:     {
   5:         ...
   6:         var list = web.Lists.Cast<SPList>().FirstOrDefault(l => l.Title == "MyList");
   7:         if (list != null)
   8:         {
   9:             var view = list.Views.Cast<SPView>().LastOrDefault(v => v.BaseViewID == "3" && v.Hidden);
  10:             if (view != null)
  11:             {
  12:                 view.ViewFields.DeleteAll();
  13:                 view.ViewFields.Add("Title");
  14:                 view.ViewFields.Add("Modified");
  15:                 view.Query = "<OrderBy><FieldRef Name=\"Modified\" Ascending=\"FALSE\" /></OrderBy>";
  16:                 view.Update();
  17:             }
  18:         }
  19:         ...
  20:     }
  21: }

In this example at first we get instance of the list (line 6), then find hidden view with specified BaseViewID and (line 9). After that we delete all columns which were added by default and add columns which we need: Title and Modified. Also we specify sort order: by Modified field descending. After that your web part will show the view with specified settings, instead of default ones.

16 comments:

  1. Great, but happens if we provision list view web part to default.aspx and default2.aspx? What we should specify in the LastOrDefault() expression? How to recognize which hidden view belongs to specific aspx page?

    ReplyDelete
  2. hello Dmitry,
    this is a problem indeed. As workaround you may check in what order web parts are provisioned and change event receiver logic according to this order. Fast, but not reliable.
    As another solution you may enumerate all list view web parts on the page using LimitedWebPartManager and try to retrieve target view id programmatically in event receiver (as far as I remember manipulating with list views using classical provisioning of list view web part is also possible. I.e. it should be possible to get list view id having instance of the web part in the code). When you will have this list view id, you will know for sure what view you should modify for this web part.

    ReplyDelete
  3. Hi, thanks for the article, but i have a problem provisioning views this way, maybe you know some workaround as well. The view which is provisioned declaratively, looks different. I am setting baseviewid and it works great, but the toolbar type is usually specified as "standard", however, add new item missing (and drag and drop new file in case of document library view, i am talking about sharepoint 2013 version, just noticed that in the article version is not specified). So, when editing the view on the page, and trying to change the toolbar type, does not help. There are 4 options there: full bar, summary, show bar, and no bar. First 2 suppose to show new item link, and they dont, only Show toolbar option shows shows menu on the top, but it looks like 2010. In one of my previous 2013 projects i got the view provisioned correctly, but the approach was a bit weird. i exported the view from the page, and used as a base, programmatically replaced some guids and added to the page. Sounds like not the best solution, but either normal declarative way, or adding view programmatically and modifying it does not work for me.

    ReplyDelete
  4. hi Ivan,
    as far as I understand you are talking about provisioning of the whole list view web part programmatically. In the post above I described how to modify list view (not list view web part), which was created in the list during declarative provisioning of list view web part. I.e. in example above lvwp was provisioned still declaratively, while only list view was modified programmatically.

    Problem with toolbar when lvwp is provisioned programmatically is classic already I think. It exists at least from SP2007 version. One way to try is described here: http://sadomovalex.blogspot.com/2010/08/set-summary-view-for-listviewwebpart.html,
    but as far as I remember it may not work in all cases. Another way which worked for me in all cases is to change toolbar programmatically via reflection e.g. like shown here: https://www.altamiracorp.com/blog/employee-posts/changing-the-toolbar-on-a-shar.

    ReplyDelete
  5. This guy reverse engineered original obfuscated code of the method which sets toolbar type from IL code, which worth respect.

    ReplyDelete
  6. This comment has been removed by the author.

    ReplyDelete
  7. This comment has been removed by the author.

    ReplyDelete
  8. Deleted the previous comment, there were few typos: Thanks, yes, i have noticed the problem is old as universe, i was struggling with it in 2010 projects at least. But, now i got a bit confused. Yes, your code is about modifying the list view, not about xsltviewwebpart. At the same time, view tag in the file element exactly provisions the view webpart to the page. Or i am mixing some not related stuff? I thought that is exactly the declarative way how to provision the view wp on the page, as well as programmatical, but there are pros and cons in both approaches. For example declarative way might not work, if the list is created a bit slower than the list view is provisioned. On the other hand, i have the code snippet for adding the xsltview wp by code, which worked perfectly in 2010, and in 2013, the wp is added, but, when you try to edit the webpart, it just breaks, and showing only title bar and no contents. There is no way how to fix it back after first modification, only by removing/adding the new one from the UI, which is not a solution of course

    ReplyDelete
  9. > At the same time, view tag in the file element exactly provisions the view webpart to the page. Or i am mixing some not related stuff?

    you are not mixing it. Yes View tag provisions the web part. But also it adds hidden list view to the list and the post was about modifying of this list view only. Try the second link from my previous comment. I used this solution in several projects (for sure I used it in SP2007, don't remember about SP2010, and not used it in SP2013 by myself) and it worked.

    ReplyDelete
  10. Are you using the combination of declaration/code because you have encountered similar problem to this - https://social.msdn.microsoft.com/Forums/en-US/26720926-619c-4d7d-aaa8-c427f87e65a6/xsltlistviewwebpart-ignores-xmldefinition-property - when you go entirely for the declaration approach (i.e. AllUsersWebPart -> XsltListViewWebPart)

    ReplyDelete
  11. Hristo,
    yes, like shown in the post above, it firsts provisions list view web part on the page declaratively (note that it is simpler to provision list view web part using View element instead of AllUsersWebPart like you try to do) and then in feature receiver modifies the created view via code.

    ReplyDelete
  12. Hi,
    But do you confirm the issue that XsltListViewWebPart ignores the XmlDefinition property?

    ReplyDelete
  13. I didn't faced with exactly this problem but easily accept that this property may be ignored.

    ReplyDelete
  14. What do you think about the following alternative to your approach:
    1. Create List Definitions (based/inheriting from existing definitions, e.g. Document Library) as Feature (not in the onet.xml, or we get other kinds of problems)
    2. In the custom list definition create a new view which has all those queries, row limits and etc
    3. In the View you are describing, specify the custom view in your custom list definition

    That would be a 100% declarative approach, no backend coding.

    ReplyDelete
  15. if your goal is to get rid of the code, it is worth to try. However before to go this direction I recommend to try provision list view web part and specify view id of some existing list using BaseViewID attribute just in order to be sure that with View element existing predefined views can be used.

    ReplyDelete