I'd almost forget about this problem, but accidently I'd found the way to solve it.
The main difference between SP2013 and SP2010 - is javascript rendering! In 2013 - javascript is everywhere, and XsltListViewWebParts (classic list view) by default are rendering on client too. That's why Xslt-transformations for header working, but not visible! To see it, go to the List View Page -> "Change Page" -> "Change XsltListViewWebPart properties" -> find checkBox "Render on server" -> Save. XsltListViewWebPart will render at 2010-style and you will see your header from Xslt.
But how to do my custom header in 2013-style, with client render? The answer is - Javascript. You'll have to override javascript header rendering. You can find it in clienttemplates.js
So, full solution is:
When you creating your custom field type, you have to override JS-link for your field:
public class MyCustomType: SPFieldLookup
{
private const string JSLinkUrl = "~site/_layouts /15/MyCustomType/MyCustomType.js";
public override string JSLink
{
get { return JSLinkUrl; }
set { base.JSLink = value; }
}
//constructors and other logic
}
In file /_layouts /15/MyCustomType/MyCustomType.js you will make all javascript transformations for your field.
Create MyCustomType/MyCustomType.js in the LAYOUTS folder. And there, override renderHeader field of spMgr object:
(function () {
spMgr.RenderHeader = function (renderCtx, field) {
//...
//you'll have to copy all SPMgr functions from clientTemplates.js
//if you want render other fields headers propertly.
//Simply find string 'function SPMgr() {' and grab this function fully
//then add you header rendering
//...
if (field.Name == 'SelectedFlag')
return RenderSelectedFlagHeader(renderCtx, field);
else if (field.Name == 'Checkmark')
return RenderCheckmarkHeader(renderCtx, field);
var fieldHeaderRenderMap = {
Attachments: RenderAttachmentsHeader,
Computed: RenderComputedHeader,
CrossProjectLink: RenderCrossProjectLinkHeader,
Recurrence: RenderRecurrenceHeader,
DateTime: RenderDateTimeHeader
};
var headerRenderer = fieldHeaderRenderMap[field.Type];
if (field.FieldType == 'MyCustomType')
return RenderMyCustomHeader(renderCtx, field); //this is our rendering!
else {
if (headerRenderer != null)
return headerRenderer(renderCtx, field);
return RenderDefaultHeader(renderCtx, field);
}
}
})();
function RenderMyCustomHeader(renderCtx, field) {
var iStr = '<th scope="col" onmouseover="OnChildColumn(this)" style="max-width: 500px;" class="ms-vh2" onmousedown="ListHeaderMenu_OnMouseDown(this);">';
iStr += "My field -my header from js";
iStr += '</th>';
return iStr;
}
Unfortunately, I hadn't find the solution without full overriding SPMgr function from clienttemplates.js. If someone will find it - please add it here!
Update:
And here is the correct XSLT-rendering (fldtypes_MyCustomType.xsl) for Server-side rendering in SP2010-style:
<?xml version="1.0" encoding="utf-8"?>
<xsl:stylesheet xmlns:x="http://www.w3.org/2001/XMLSchema" version="1.0" exclude-result-prefixes="xsl msxsl ddwrt" xmlns:ddwrt="http://schemas.microsoft.com/WebParts/v2/DataView/runtime" xmlns:xsl="http://www.w3.org/1999/XSL/Transform" xmlns:msxsl="urn:schemas-microsoft-com:xslt" xmlns:SharePoint="Microsoft.SharePoint.WebControls" >
<xsl:output method="html" indent="no"/>
<xsl:template name="FieldRef_MyCustomType_header"
match="FieldRef[@FieldType='MyCustomType']"
ddwrt:dvt_mode="header" mode="header" priority="9">
<th class="ms-vh-icon" nowrap="nowrap" scope="col" onmouseover="OnChildColumn(this)">
<xsl:attribute name="class">ms-vh2</xsl:attribute>
<xsl:call-template name="dvt_headerfield">
<xsl:with-param name="fieldname"><xsl:value-of select="@Name"/>
</xsl:with-param>
<xsl:with-param name="fieldtitle">
MyCustomType from XSL
</xsl:with-param>
<xsl:with-param name="displayname"><xsl:value-of select="@DisplayName"/></xsl:with-param>
<xsl:with-param name="fieldtype">x:string</xsl:with-param>
</xsl:call-template>
</th>
</xsl:template>