博客地址:http://blog.csdn.net/FoxDave
页面转换。解决方案的核心用于转换的模型,模型会告诉引擎哪些web部件属性是重要的,允许我们处理这些属性,并动态为我们的web部件选择映射。页面转换模型用XML表达,并通过结构清单文件来验证模型的正确性。
页面转换模型结构
我们打开页面转换模型时,会看到如下三个部分:
- BaseWebPart
该元素包含应用到所有web部件的配置。例如:它描述了所有web部件都会获取的”标题“属性。它也是定义默认web部件映射的地方,如果一个web部件没有定义映射,引擎就会访问该默认映射去在客户端页面上展示web部件。 - AddOns
作为页面转换的用户,我们可能需要应用自定义的逻辑来实现我们的需求。例如我们需要将一个既有的属性转换为能被我们的客户端web部件接受的形式。框架支持这种做法,它允许我们添加我们自己的包含函数和选择器的程序集,通过简单地在AddOn部分中定义,之后通过在指定的名字上加前缀来引用它们以使页面转换使用我们自定义的代码。 - WebParts
该元素包含我们想要转换的每个web部件的信息。对于每个web部件,我们可以找到要使用的属性集的定义,在这些属性上要执行的函数,可能的定义转换目标的映射和我们动态选择所需映射的选择器。
转换模型中Web部件的定义
接下来我们分析一下web部件是如何在页面转换模型中定义的,用下面的例子说明:
<!-- XsltListView web part -->
<WebPart Type="Microsoft.SharePoint.WebPartPages.XsltListViewWebPart, Microsoft.SharePoint, Version=16.0.0.0, Culture=neutral, PublicKeyToken=71e9bce111e9429c">
<Properties>
<Property Name="XmlDefinitionLink" Type="string" />
<Property Name="ListUrl" Type="string" />
<Property Name="ListId" Type="guid" Functions="{ListWebRelativeUrl} = ListAddWebRelativeUrl({ListId}); {ListServerRelativeUrl} = ListAddServerRelativeUrl({ListId})"/>
<Property Name="Direction" Type="string"/>
<Property Name="GhostedXslLink" Type="string" />
<Property Name="DisableViewSelectorMenu" Type="bool"/>
<Property Name="XmlDefinition" Type="string" Functions="{ListViewId} = ListDetectUsedView({ListId},{XmlDefinition})"/>
<Property Name="SelectParameters" Type="string"/>
</Properties>
<!-- This selector outputs: Library, List -->
<Mappings Selector="ListSelectorListLibrary({ListId})">
<Mapping Name="List" Default="true">
<ClientSideText Text="You can map a source web part ({Title}) to a combination of client side controls :-)" Order="10" />
<ClientSideWebPart Type="List" Order="20" JsonControlData="{"serverProcessedContent":{"htmlStrings":{},"searchablePlainTexts":{},"imageSources":{},"links":{}},"dataVersion":"1.0","properties":{"isDocumentLibrary":false,"selectedListId":"{ListId}","listTitle":"{Title}","selectedListUrl":"{ListServerRelativeUrl}","webRelativeListUrl":"{ListWebRelativeUrl}","webpartHeightKey":4,"selectedViewId":"{ListViewId}"}}" />
</Mapping>
<Mapping Name="Library" Default="false">
<ClientSideWebPart Type="List" Order="10" JsonControlData="{"serverProcessedContent":{"htmlStrings":{},"searchablePlainTexts":{},"imageSources":{},"links":{}},"dataVersion":"1.0","properties":{"isDocumentLibrary":true,"selectedListId":"{ListId}","listTitle":"{Title}","selectedListUrl":"{ListServerRelativeUrl}","webRelativeListUrl":"{ListWebRelativeUrl}","webpartHeightKey":4,"selectedViewId":"{ListViewId}"}}" />
</Mapping>
</Mappings>
</WebPart>
属性元素
对于每个web部件,模型定义了对配置目标客户端web部件可能有用的属性。如果我们遗漏了某个属性,我们可以在模型中轻松地扩展出来。在一些属性中可以看到函数类型的属性,包含一到多个函数,用;分隔,会在源web部件映射到目的端客户端web部件时执行。函数的解析如下:
{Output} = FunctionName({Input1}, {Input2})
函数可以具有一到多个输入值,可以是:
- web部件上定义的属性,如{ListId}
- 基础web部件上定义的属性,如{Title}
- 之前函数执行产生的输出属性,如{ListWebRelativeUrl}
- 默认的网站范围的属性,如{Host}、{Web}、{Site}、{WebId}、{SiteId}
函数在执行时它的输出可能时:
- 单一字符串值:该值会以Output命名被添加到web部件的属性列表中,属性的值为执行函数后的返回值
- 键值对的列表:此情况下键值对中的每一个元素都被添加到web部件的属性列表中
如果函数没有定义输出参数,定义函数的web部件属性的值将会被函数运行结果覆盖。
让我们用一个例子说明:
<Property Name="ListId" Type="guid" Functions="FormatGuid({ListId})"/>
现在让我们假定web部件属性原先包含一个guid格式的值,如{AAFAD7D0-D57A-4BB1-8706-969A608C686B}。在执行完FormatGuid函数后,该值会被设置为执行格式化GUID函数后得到的结果值,如AAFAD7D0-D57A-4BB1-8706-969A608C686B。
如果函数返回了多个值,每一个返回的键值对会覆盖已存在于web部件中的属性。
映射集合元素
该元素定义一到多个对于给定源web部件的可能的目的端配置。既然我们可以定义多个目的端,我们就需要有一套机制去决定使用哪个映射:
- 如果映射元素包含填充的选项属性,选择器执行的输出被用来通过名称找到正确的映射,如ListSelectorListLibrary返回”Library”字符串表明使用名为”Library”的映射。选择器是一对一的函数,返回单一的值,所以我们可以指定任意的web部件属性作为输入。
- 如果仅有一个映射,在没有选择器结果时就选择它。
- 如果选择器没有结果且定义了多个映射,则使用默认的映射。
接下来我们讲解映射元素本身。
映射元素
在映射元素中我们可以有一到多个ClientSideText或ClientSideWebPart元素,如下所示:
<Mapping Name="List" Default="true">
<ClientSideText Text="You can map a source web part ({Title}) to a combination of client side controls :-)" Order="10" />
<ClientSideWebPart Type="List" Order="20" JsonControlData="{"serverProcessedContent":{"htmlStrings":{},"searchablePlainTexts":{},"imageSources":{},"links":{}},"dataVersion":"1.0","properties":{"isDocumentLibrary":false,"selectedListId":"{ListId}","listTitle":"{Title}","selectedListUrl":"{ListServerRelativeUrl}","webRelativeListUrl":"{ListWebRelativeUrl}","webpartHeightKey":4,"selectedViewId":"{ListViewId}"}}" />
</Mapping>
上面的示例中,源web部件XSLTListViewweb被映射到目的端文本部件ClientSideText和一个目的端web部件ClientSideWebPart:
- 使用Order属性来指定哪个优先
- 对于ClientSideWebPart我们需要指定web部件的Type,如果我们选择Custom作为类型,我们还需要指定ControlId属性来标识需要的自定义web部件。
- Text、JsonControlData和ControlId属性可以包含表单中的口令{Token},在运行时会被替换为真实的值。每个定义的口令需要能够作为web部件属性或函数的结果。
页面转换模型中AddOns的定义
Add-ons允许我们以下面的两个步骤插入自己的逻辑到映射模型中:
- 创建自定义的程序集来承载我们自定义的函数/选择器
- 在AddOns元素中声明自定义程序集
创建我们自定义的函数/选择器程序集
创建我们自己的函数需要我们在项目中引用SharePoint.Modernization.Framework程序集,然后创建一个类继承类
SharePointPnP.Modernization.Framework.Functions.FunctionsBase
:
示例:
using Microsoft.SharePoint.Client;
using SharePointPnP.Modernization.Framework.Functions;
using System;
namespace Contoso.Modernization
{
public class MyCustomFunctions: FunctionsBase
{
public MyCustomFunctions(ClientContext clientContext) : base(clientContext)
{
}
public string MyListAddServerRelativeUrl(Guid listId)
{
if (listId == Guid.Empty)
{
return "";
}
else
{
var list = this.clientContext.Web.GetListById(listId);
list.EnsureProperties(p => p.RootFolder.ServerRelativeUrl);
return list.RootFolder.ServerRelativeUrl;
}
}
}
}
声明我们自定义的程序集
在自定义函数可以被使用之前,它们需要在模型中通过在AddOns元素中对每个库/类添加一个引用的方式进行声明:
<AddOn Name="Custom" Type="Contoso.Modernization.MyCustomFunctions" Assembly="Contoso.Modernization.dll" />
或(完整路径)
<AddOn Name="Custom" Type="Contoso.Modernization.MyCustomFunctions" Assembly="c:\transform\Contoso.Modernization.dll" />
注意看每一种声明都有一个名称,在上例中叫做”Custom”。
使用我们自定义的函数/选择器
现在程序集已经定义完毕,我们可以使用我们的函数和选择器了,通过自定义的名称前缀来引用如:
<Property Name="ListId" Type="guid" Functions="{ListServerRelativeUrl} = Custom.MyListAddServerRelativeUrl({ListId})"/>