Question

How to add a document library XsltListViewWebPart (or list view web part) on page using CSOM with SP2013 when we do not want to use the in-built view with "Name, Modified, Modified By" fields?

Custom lists get imported correctly via CSOM with this xml, and this also works in import via UI directly, but does not work via CSOM for Document Libraries or Linked Lists for example:

<?xml version="1.0" encoding="utf-8" ?>
<webParts>
  <webPart xmlns="http://schemas.microsoft.com/WebPart/v3">
    <metaData>
      <type name="Microsoft.SharePoint.WebPartPages.XsltListViewWebPart, Microsoft.SharePoint, Version=15.0.0.0, Culture=neutral, PublicKeyToken=71e9bce111e9429c" />
      <importErrorMessage>Cannot import this Web Part.</importErrorMessage>
    </metaData>
    <data>
      <properties>
        <property name="ListUrl" type="string">Lists/MyList</property>
        <property name="XmlDefinition" type="string">&lt;View Name="{ABCDEFGH-ABCD-ABCD-ABCD-ABCDEFGHIJK}" MobileView="TRUE" Type="HTML" Hidden="TRUE" OrderedView="TRUE" DisplayName="" Url="/sites/site/Pages/default.aspx" Level="255" BaseViewID="1" ContentTypeID="0x" ImageUrl="/_layouts/15/images/links.png?rev=23" &gt;&lt;Query&gt;&lt;OrderBy&gt;&lt;FieldRef Name="Order" Ascending="TRUE"/&gt;&lt;/OrderBy&gt;&lt;/Query&gt;&lt;ViewFields&gt;&lt;FieldRef Name="DocIcon"/&gt;&lt;FieldRef Name="Edit"/&gt;&lt;FieldRef Name="URLwMenu"/&gt;&lt;/ViewFields&gt;&lt;RowLimit Paged="TRUE"&gt;99&lt;/RowLimit&gt;&lt;JSLink&gt;clienttemplates.js&lt;/JSLink&gt;&lt;XslLink Default="TRUE"&gt;main.xsl&lt;/XslLink&gt;&lt;Toolbar Type="Standard"/&gt;&lt;/View&gt;</property>
        <property name="MissingAssembly" type="string">Cannot import this Web Part.</property>
      </properties>
    </data>
  </webPart>
</webParts>

EDIT / Solution:

As Anthony suggested we should first add the XsltListViewWebPart without specifying the view. Adding the web part will automatically create a hidden view in the list and link the web part to it. Updating this view will also update the web part.

Example solution below.

Web part xml:

<?xml version="1.0" encoding="utf-8" ?>
    <webParts>
      <webPart xmlns="http://schemas.microsoft.com/WebPart/v3">
        <metaData>
          <type name="Microsoft.SharePoint.WebPartPages.XsltListViewWebPart, Microsoft.SharePoint, Version=15.0.0.0, Culture=neutral, PublicKeyToken=71e9bce111e9429c" />
          <importErrorMessage>Cannot import this Web Part.</importErrorMessage>
        </metaData>
        <data>
          <properties>
            <property name="ListUrl" type="string">MyLibrary</property>
            <property name="MissingAssembly" type="string">Cannot import this Web Part.</property>
          </properties>
        </data>
      </webPart>
    </webParts>

Code:

WebPart importingWebPart = mgr.ImportWebPart(webPartXml).WebPart; // take webPartXml from above
WebPartDefinition wpDefinition = mgr.AddWebPart(importingWebPart, "Top", 1);
mgr.Context.Load(wpDefinition,
    d => d.Id); // Id of the hidden view which gets automatically created
mgr.Context.ExecuteQuery();
var viewId = wpDefinition.Id;

List list = web.Lists.GetByTitle("MyLibrary");
View view = list.Views.GetById(viewId);
view.ViewFields.RemoveAll();
view.ViewFields.Add("Title");
view.ViewQuery = "<Where><Eq><FieldRef Name=\"Title\" /><Value Type=\"Text\">Something Here</Value></Eq></Where>";
view.RowLimit = 10;
web.Context.ExecuteQuery();
Was it helpful?

Solution

I came across this today and found another workaround.

When the web part is added to the page it creates a hidden view on the list it is bound to. You can get the hidden view and make the changes to it directly. This works for announcements, calendars and doc libraries from what I have tested so far.

Example:

 private static void AddWebPart(Web web, LimitedWebPartManager mgr, string xmlSchema, string listName, string zoneName, int zoneId)
    {
        WebPartDefinition def = mgr.ImportWebPart(xmlSchema);
        mgr.AddWebPart(def.WebPart, zoneName, zoneId);       
        mgr.Context.ExecuteQuery();

        List list = web.Lists.GetByTitle(listName);
        web.Context.Load(list.Views);
        web.Context.ExecuteQuery();

        foreach (View v in list.Views)
        {
          if (v.Hidden)
            {                       
               FixView(v);
            }
        }            
    }

  private static void FixView(View v)
    {
       v.ViewFields.RemoveAll();

       v.ViewFields.Add("LinkTitle");
       v.ViewFields.Add("Description");
       v.ViewFields.Add("Location");

       v.ViewQuery = "<Where><Eq><FieldRef Name=\"Title\" /><Value Type=\"Text\">Something Here</Value></Eq></Where>";
       v.RowLimit = 4;

       v.Update();   
       v.Context.ExecuteQuery();
    }

You could probably read out the view fields from your schema and find a nice way to apply them rather than hard coded like the example.

Unfortunately I have not found away to apply a style to the view (e.g. Newspaper, boxed etc.) which is something I also need.

OTHER TIPS

I tested your code and I can confirm the behavior you described.

There seems to be a bug where, no matter what XmlDefinition you specify, a webpart attached to a document library always ends up showing the default OOB view (Name, Modified, Modified By).

If it were server object model, then it would be possible to cast the webpart to the underlying object of type XsltListViewWebPart and change its ViewGuid property (or viewFields using reflection).

Unfortunately, CSOM doesn't expose the underlying types, so AFAIK there's no workaround to overcome this strange behavior.

Since XsltListWebPart.ViewGuid and XsltListWebPart.XmlDefinition properties are ignored when adding web part via CSOM it makes it impossible to specify custom view for XsltListWebPart web part.

Solution

Since the default view is used when web part provisioned via CSOM, the proposed solution would be to modify default view during web part provisioning:

  • modify default view properties before adding web part on page
  • add web part on page
  • restore default view original state

Example

 using (var ctx = new ClientContext("http://intranet.contoso.com/"))
 {
      var web = ctx.Web;

      var view = GetDefaultView(web,listTitle);
      var originalViewFields = view.ViewFields.ToArray();
      UpdateViewFields(view,new []{ "LinkTitle","Editor"}); 

      //Add web part on page ()
      AddWebPart(ctx.Web, pageUrl, "TopColumnZone", 1, webPartSchemaXml);

      //Restore default view
      UpdateViewFields(view,originalViewFields);

}


/// <summary>
/// Get default view
/// </summary>
/// <param name="web"></param>
/// <param name="listTitle"></param>
/// <returns></returns>
private static View GetDefaultView(Web web,string listTitle)
{
    var list = web.Lists.GetByTitle(listTitle);
    var viewQuery = list.Context.LoadQuery(list.Views.Include(v => v.ViewFields, v => v.DefaultView)).Where(v => v.DefaultView);
    list.Context.ExecuteQuery();
    return viewQuery.FirstOrDefault();
}

/// <summary>
/// Update view fields
/// </summary>
/// <param name="view"></param>
/// <param name="viewFields"></param>
private static void UpdateViewFields(View view,string[] viewFields)
{
    view.ViewFields.RemoveAll();
    foreach (var viewField in viewFields)
    {
        view.ViewFields.Add(viewField);
    }
    view.Update();
    view.Context.ExecuteQuery();
}

public static WebPartDefinition AddWebPart(Web web, string pageUrl, string zoneId, int zoneIndex, string webPartXml)
{
     var file = web.GetFileByServerRelativeUrl(pageUrl);
     var webPartManager = file.GetLimitedWebPartManager(PersonalizationScope.Shared);
     var webPartImportedDef = webPartManager.ImportWebPart(webPartXml);
     var webPartDef = webPartManager.AddWebPart(webPartImportedDef.WebPart, zoneId, zoneIndex);
     web.Context.Load(webPartDef);
     web.Context.ExecuteQuery();
     return webPartDef;
 }

If anyone stumbles upon this. I have done a post on this : ADD DOCUMENT LIBRARY WEBPART IN SHAREPOINT 2013 PAGE

As requested by Phil, I putting all step here too.

Steps:

  1. First add document library in a page and save it.

  2. Open Page and edit it with SharePoint Designer, if you do not have SharePoint Designer, you can download the page on your computer and Find document library webpart markup, and set exportMode=”All” and save it.

  3. Now open page on SharePoint Online and click edit. On Document Library Webpart on right corner, click the icon and you will see export option now. Export the XML and save it. This is XML that you will use to add webpart on a page via CSOM. Note the Field ListID, You need to replace the list ID with new or actual library. If you do not replace you will create same library everywhere when you add this webpart with CSOM.

  4. And here is the code to get List ID for a List (Document Library).

  5. Code to add webpart or App Part on a SharePoint page. This works for both depending upon XML supplied.

[WebMethod]

public void AddWebpartSharepointOnline(string siteURL, string pageUrl,string webPartXml,string Title)
{
    using (ClientContext clientContext = new ClientContext(siteURL))
    {
        SecureString passWord = new SecureString();
        foreach (char c in sharepointPass.ToCharArray()) passWord.AppendChar(c);
        clientContext.Credentials = new SharePointOnlineCredentials(sharepointUser, passWord);
        Microsoft.SharePoint.Client.File page = clientContext.Web.GetFileByServerRelativeUrl(pageUrl); /* /NucleonSite/SitePages/Home.aspx */
        LimitedWebPartManager wpm = page.GetLimitedWebPartManager(PersonalizationScope.Shared);
        WebPartDefinition wpd = wpm.ImportWebPart(webPartXml);
        wpd.WebPart.Title = Title;
        wpm.AddWebPart(wpd.WebPart, “Right”, 1);
        try
        {
            clientContext.ExecuteQuery();
        }
        catch { throw; }
    }
}

I made it to work on a Document Library using this blog post's hints.

Basically, you just need to get an instance of your XsltListViewWebPart after adding it, using a new WebPartManager, and then use Reflection to get the internal ContextView property:

GetWebPartManagerForPage(web, "SitePages/default.aspx", manager =>
{
    var xvl = (from System.Web.UI.WebControls.WebParts.WebPart wp in manager.WebParts
               where wp.GetType() == typeof(XsltListViewWebPart)
               select wp).FirstOrDefault();

    PropertyInfo pi = xvl.GetType().GetProperty("ContextView", BindingFlags.NonPublic | BindingFlags.Public | BindingFlags.Instance);
    SPView view = (SPView)(pi.GetValue(xvl, null));
    SetXsltListViewFields(view);
    manager.SaveChanges(xvl);
});

Then to update the view to match your needs (in my case, just to have doc name with link):

public static void SetXsltListViewFields(SPView view)
{
   string viewQuery = "<OrderBy><FieldRef Name=\"ID\" /></OrderBy>";

   view.Query = viewQuery;
   view.ViewFields.DeleteAll();
   view.ViewFields.Add("DocIcon");
   view.ViewFields.Add("LinkFilename");

   view.Paged = true;
   view.RowLimit = 10;
   view.DefaultView = true;

   view.Update();
}

As part of my provisioning process, it is now working well.

Licensed under: CC-BY-SA with attribution
Not affiliated with sharepoint.stackexchange
scroll top