This server control will help you hide html elements which shouldn’t display in ‘display mode’ if a certain SharePoint field is empty. This must be a common requirement amongst most SharePoint projects so I’m surprised that I haven’t covered this earlier as its not commonly documented elsewhere.
Over the last few months, I’ve been working on a project which has required building a lot of page layouts. Some of these page layouts have required building forms which required conditionally hiding and showing elements depending on a number of conditions. Dealing with empty fields is just one of these conditions.
The technique that we will be using will require the following:
- Ability to add (C#) code behind to the SharePoint web application.
- Control over the page layout or user control which exists on the page which requires this functionality. (There is way to add this functionality to a page by wrapping the control inside a web part but this post does not cover this scenario.)
- Understanding of how CSS works and how to uniquely identify a html element.
Goals:
Here is a list of functionality that this control offers:
- It can hide a html element containing a unique class name depending on whether a corresponding field is empty or null.
- You can define multiple pairings of fields to classes in one instance of the control.
- Each pairing definition can define multiple fields to map the the same class.
- When associating multiple fields to a class, you can define whether the ‘OR’ or the ‘AND’ operator be used, each of which uses a different algorithm to decide whether the elements of the class should be hidden.
Real world examples:
Suppose you have a heading called ‘Cost’ under which sits a table containing three fields (Euro € , GBP £ and US $). It may typically look like the following:
Cost
Euro € | GBP £ | US $ |
100 | 86 | 135 |
Suppose, the Dollar field is empty, and you would like to hide the Dollar column only. You can achieve this by using our server control and it would look like the following:
Cost
Euro € | GBP £ | US $ |
100 | 86 | 135 |
Now suppose if all three fields are empty, the Cost heading would still show. You only want to hide the ‘Cost’ heading when all three fields are empty, this control also allows you to achieve this.
Front end code
To achieve the functionality described in the example. After deploying the ‘MultipleEmptyFieldHider' server control (defined at the bottom) to your SharePoint web application. You will need create a reference to it’s namespace and use it like the following:
<%@ Register Tagprefix="myServerControls" Namespace="MyCompany.MyClient.MyProject.ServerControls" Assembly="MyCompany.MyClient.MyProject, Version=1.0.0.0, Culture=neutral, PublicKeyToken=abcd..." %>
<h2 class="my-specific-CostTitle">Cost</h2>
<table cellpadding="0" cellspacing="1" border="0" width="100%">
<colgroup>
<col width="33" class="my-specific-prodEuro"/>
<col width="33" class="my-specific-prodGBP"/>
<col width="34" class="my-specific-prodUS" />
</colgroup>
<thead>
<th >Euro €</th>
<th >GBP £</th>
<th >US $</th>
</thead>
<tr>
<td >
<SharePoint:TextField DisplaySize="30" id="EuroField" InputFieldLabel="Cost 1" FieldName="EuroField" runat="server"/></td>
<td >
<SharePoint:TextField DisplaySize="30" id="PoundField" InputFieldLabel="Cost 2" FieldName="PoundField" runat="server"/></td>
<td >
<SharePoint:TextField DisplaySize="30" id="DollarField" InputFieldLabel="Cost 3" FieldName="DollarField" runat="server"/></td>
</tr>
</table>
<myServerControls:MultipleEmptyFieldHider id="cusMultiEmptyContentArea1"
FieldPairings="DollarField:my-specific-prodUS;PoundField:my-specific-prodGBP;EuroField:my-specific-prodEuro;EuroField+PoundField+DollarField:my-specific-CostTitle"
runat="server" />
The key points of the code above are:
- All three columns have a unique class applied to them.
- The cost title has a unique column applied to it.
- The MultipleEmptyFieldHider control defines the rules of how the html elements should behave in one place.
Usage:
Property |
Default |
Description |
FieldPairings |
[none] |
This defines the pairing of fields and classes. Each pairing is defined by two sections:
[Field Definition]:[Class Name]
Each pairing is separated by a ‘;’ delimiter:
[Pair1];[Pair2];[Pair3];[Pair4]
e.g.
FieldName1:ClassName1;FieldName1:ClassName1;
In plain English:
Hide ClassName1 if FieldName1 is empty or null. Hide ClassName2 if FieldName2 is empty or null.
Available operators in [Field Definition].
’|’ – OR – Indicates that the class should hide if any of the fields separated by the ‘|’ delimiter, are empty or null.
[[Field1]|[Field2]]:[Class Name]
e.g.
FieldName1|FieldName2:ClassName1
In plain English:
Hide ClassName1 if either FieldName1 or FieldName2 are empty or null.
’+’ – AND – Indicates that the class should hide if all of the fields separated by the ‘+’ delimiter, are empty or null.
[[Field1]+[Field2]]:[Class Name]
e.g.
FieldName1+FieldName2:ClassName1
In plain English:
Hide ClassName1 if both FieldName1 and FieldName2 are empty or null.
You can have as many field name separation operations as you want e.g.
[[Field1]|[Field2]|[Field3]]:[Class Name]
or
[[Field1]+[Field2]+[Field3]]:[Class Name] |
ModifyPropertyName |
‘Display’ |
You can override the default CSS property name by defining your own. e.g.
ModifyPropertyName=”visibility”. If you do this, you will also need to ensure that you have set a valid CSS value on the ModifyPropertyValue property. |
ModifyPropertyValue |
‘None’ |
You can override the default CSS property value . e.g.
ModifyPropertyValue =”hidden”. This property must correspond to a CSS property defined in the ModifyPropertyName property. |
Code behind:
using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.Linq;
using System.Text;
using System.Web;
using System.Web.UI;
using System.Web.UI.WebControls;
using Microsoft.SharePoint;
using Microsoft.SharePoint.WebControls;
namespace MyCompany.MyClient.MyProject.ServerControls
{
public class MultipleEmptyFieldHider : WebControl
{
public string FieldPairings { get; set; }
public string ModifyPropertyName { get; set; }
public string ModifyPropertyValue { get; set; }
public MultipleEmptyFieldHider()
{
ModifyPropertyName = "display";
ModifyPropertyValue = "none";
}
protected override void Render(HtmlTextWriter writer)
{
try
{
string[] fieldPairingList = FieldPairings.Split(';');
StringBuilder sb = new StringBuilder();
foreach (var fieldPairing in fieldPairingList)
{
string[] pairgingSet = fieldPairing.Split(':');
if(pairgingSet[0] != null && pairgingSet[1] != null)
{
string _fieldName = pairgingSet[0];
string _className = pairgingSet[1];
if ((SPContext.Current.FormContext.FormMode == SPControlMode.Display)
&& (!string.IsNullOrEmpty(_fieldName))
&& (!string.IsNullOrEmpty(_className)))
{
string[] fieldsToCheck;
// Default to OR operator
FieldDelimeterOperatorType operatorType = FieldDelimeterOperatorType.OR;
if(_fieldName.Contains('+'))
{
operatorType = FieldDelimeterOperatorType.AND;
fieldsToCheck = _fieldName.Split('+');
}
else
{
fieldsToCheck = _fieldName.Split('|');
}
int emptyFieldCount = 0;
foreach(var fieldToCheckName in fieldsToCheck)
{
bool isEmptyField = false;
var fieldOject = SPContext.Current.Item[fieldToCheckName];
if (fieldOject != null)
{
string fieldValue = fieldOject.ToString();
if (string.IsNullOrEmpty(fieldValue))
{
isEmptyField = true;
}
else
{
// Do some logging!
}
}
else
{
isEmptyField = true;
}
if(isEmptyField)
{
emptyFieldCount++;
// In an OR situation, we want to terminate the loop once we've found
// the first empty field as the condition has been met
if(operatorType == FieldDelimeterOperatorType.OR)
{
break;
}
}
else
{
// In an AND situation, we want to terminate the loop once we've found
// the first field that has value
if (operatorType == FieldDelimeterOperatorType.AND)
{
break;
}
}
}
switch (operatorType)
{
case FieldDelimeterOperatorType.AND:
if (emptyFieldCount == fieldsToCheck.Count())
{
ApplyStyleToClass(sb, _className);
}
break;
default:
if (emptyFieldCount > 0)
{
ApplyStyleToClass(sb, _className);
}
break;
}
}
}
else
{
// Log as a problem
}
}
writer.Write(string.Format("\n",sb.ToString()));
}
catch(Exception ex)
{
// Log the error
}
base.Render(writer);
}
protected void ApplyStyleToClass(StringBuilder sb, string className)
{
sb.Append(string.Format("\n .{0}{{{1}:{2};}} \n", className,ModifyPropertyName,ModifyPropertyValue));
}
}
public enum FieldDelimeterOperatorType
{
OR = 0,
AND = 1
} ;
}
Additional notes:
- I have purposefully removed the tracing code. This needs to be added by yourself.
- This code has been evolved over a few iterations to do different things so I’m aware that it could be tweaked to be more efficient.
- You need to do something with the catch statements. Perhaps trace the errors!