EditReplacement the ListForm webpart
This provides a quick and dirty replacement web part which can be used in conjunction with suppressing/replacing the default ListForm web part.
What it does is render a replica form, with some methods which can be overridden to control:
- Which fields are visible.
- Which fields are writable (editable).
Which can done in a fine-grained context-sensitive manor.
Additionally, unlike performing customizations using the "Custom List Form" method in SharePoint designer, this doesn't break attachment support.
This is just a starting point however - generally I create an xml configuration document which I use to drive the order, visibility and accessibility of fields based on the current user and the roles they have - often this saves time too as new fields are added, and requirements evolve.
EditThe Code
<nowiki>
public class BaseFilteredListFormWebPart : WebPart
{
private bool _buttonsAtTop;
private bool _descriptionsUnderLabels;
private SPControlMode _controlMode = SPControlMode.New;
public BaseFilteredListFormWebPart()
{
ExportMode = WebPartExportMode.All;
}
[WebBrowsable(true), Personalizable(true), Browsable(true), Category("List"), WebPartStorage(Storage.Personal),
FriendlyName("Control Mode"),
Description("The display mode for this command (Only Edit and New currently supported)")]
public SPControlMode ControlMode
{
get { return _controlMode; }
set { _controlMode = value; }
}
[WebBrowsable(true), Personalizable(true), Browsable(true), Category("List"), WebPartStorage(Storage.Personal),
FriendlyName("Buttons At Top?"), Description("Display OK & Cancel Buttons at top if web part?")]
public bool ButtonsAtTop
{
get { return _buttonsAtTop; }
set { _buttonsAtTop = value; }
}
[WebBrowsable(true), Personalizable(true), Browsable(true), Category("List"), WebPartStorage(Storage.Personal),
FriendlyName("Descriptions under labels?"), Description("Display descriptions for fields under the labels, instead of the controls?")]
public bool DescriptionsUnderLabels
{
get { return _descriptionsUnderLabels; }
set { _descriptionsUnderLabels = value; }
}
protected virtual SPList List
{
get
{
return SPContext.Current.List;
}
}
protected virtual bool CanAddAttachments
{
get { return !IsReadOnly; }
}
protected virtual bool IsReadOnly
{
get { return false; }
}
private static void AddRow(Table table, Control control)
{
TableCell cell = AddRowAndCell(table);
cell.Controls.Add(control);
}
protected Control CreateInputTable(SPList list)
{
CustomTable table = new CustomTable();
table.CellPadding = 0;
table.CellSpacing = 0;
table.ID = "part1";
table.Width = new Unit(100, UnitType.Percentage);
bool hasWriteableFields;
Control fieldsTable = CreateFieldsTable(list, out hasWriteableFields);
if (ButtonsAtTop) AddRow(table, CreateTopFormButtonBar(list, hasWriteableFields));
AddRow(table, CreateEmptyImage(1, 7));
if (CanAddAttachments)
{
AddRow(table, CreateToolbar(list));
AddRow(table, CreateEmptyImage(1, 7));
}
AddRow(table, fieldsTable);
AddRow(table, CreateEmptyImage(1, 7));
AddRow(table, CreateBottomFormButtonBar(list, hasWriteableFields));
return table;
}
private static TableCell AddRowAndCell(Table table)
{
TableRow row = new TableRow();
TableCell cell = new TableCell();
row.Cells.Add(cell);
table.Rows.Add(row);
return cell;
}
protected override void CreateChildControls()
{
base.CreateChildControls();
try
{
SPList list = List;
if (list == null) return;
Table table = new Table();
if (CanAddAttachments) AddRow(table, CreateAttachmentsTable(list));
AddRow(table, CreateInputTable(list));
Controls.Add(table);
if (CanAddAttachments) Controls.Add(GenerateHideEmptyAttachmentsScriptControl());
if (DesignMode)
{
Controls.Clear();
Label label = new Label();
label.Text = "Output of controls disabled in design mode";
Controls.Add(label);
}
}
catch (Exception ex)
{
Controls.Clear();
Label label = new Label();
label.Text = ex.ToString();
Controls.Add(label);
}
}
private static TableRow CreateEmptyRow()
{
TableRow row = new TableRow();
row.Height = 1;
row.Width = new Unit(100, UnitType.Percentage);
row.VerticalAlign = VerticalAlign.Top;
TableCell cell1 = new TableCell();
cell1.CssClass = "ms-formline";
row.Cells.Add(cell1);
TableCell cell2 = new TableCell();
cell2.CssClass = "ms-formline";
row.Cells.Add(cell2);
Image image = CreateEmptyImage();
cell1.Controls.Add(image);
cell2.Controls.Add(image);
return row;
}
private static Image CreateEmptyImage()
{
return CreateEmptyImage(1, 1);
}
private static Image CreateEmptyImage(int width, int height)
{
Image image = new Image();
image.ImageUrl = "/_layouts/images/blank.gif";
image.Width = width;
image.Height = height;
return image;
}
private FormToolBar CreateToolbar(SPList list)
{
FormToolBar toolBar = new FormToolBar();
toolBar.ControlMode = ControlMode;
toolBar.ListId = list.ID;
return toolBar;
}
private Table CreateAttachmentsTable(SPList list)
{
AttachmentUpload attachmentsUpload = new AttachmentUpload();
attachmentsUpload.ID = "fileupload";
attachmentsUpload.ListId = list.ID;
attachmentsUpload.ControlMode = ControlMode;
TableCell cell = new TableCell();
cell.Controls.Add(attachmentsUpload);
TableRow row = new TableRow();
row.Cells.Add(cell);
Table table = new Table();
table.Rows.Add(row);
return table;
}
private static SPField GetAttachmentsField(SPList list)
{
foreach (SPField field in list.Fields)
{
if (field.Type == SPFieldType.Attachments)
{
return field;
}
}
return null;
}
private TableRow CreateAttachmentsRow(SPList list)
{
SPField attachmentsField = GetAttachmentsField(list);
CustomTableRow row = new CustomTableRow();
row.ID = "idAttachmentsRow";
AttachmentsField attachmentsControl = new AttachmentsField();
attachmentsControl.ListId = list.ID;
attachmentsControl.ControlMode = ControlMode;
attachmentsControl.FieldName = attachmentsField.InternalName;
if (IsReadOnly)
{
attachmentsControl.ControlMode = SPControlMode.Display;
}
TableCell labelCell = new TableCell();
labelCell.Controls.Add(CreateFieldLabelControl(list, attachmentsField));
labelCell.CssClass = "ms-formlabel";
TableCell controlCell = new TableCell();
controlCell.Controls.Add(attachmentsControl);
controlCell.CssClass = "ms-formbody";
row.Cells.Add(labelCell);
row.Cells.Add(controlCell);
return row;
}
private static string GetHideEmptyAttachmentsRowScript()
{
return
@"
<_script>
var elm = document.getElementById(""idAttachmentsTable"");
if (elm == null || elm.rows.length == 0)
document.getElementById(""idAttachmentsRow"").style.display='none';
</_script>
";
}
private static Control GenerateHideEmptyAttachmentsScriptControl()
{
Literal literal = new Literal();
literal.Text = GetHideEmptyAttachmentsRowScript();
return literal;
}
private Table CreateFieldsTable(SPList list, out bool hasWriteableFields)
{
Table table = new Table();
table.CellPadding = 0;
table.CellSpacing = 0;
IEnumerable<FieldToRender> fieldsToRender = EnumerateFields(list);
fieldsToRender = StripNonVisibleFields(fieldsToRender);
List<FieldToRender> fields = new List<FieldToRender>(fieldsToRender);
hasWriteableFields = HasWriteableFields(fields);
IEnumerable<TableRow> renderedRows = CreateFieldControlRows(list, fields);
foreach (TableRow row in renderedRows) table.Rows.Add(row);
table.Rows.Add(CreateAttachmentsRow(list));
table.Rows.Add(CreateEmptyRow());
return table;
}
protected static bool HasWriteableFields(IEnumerable<FieldToRender> fields)
{
foreach (FieldToRender field in fields)
{
if (field.IsWriteable) return true;
}
return false;
}
protected virtual IEnumerable<FieldToRender> EnumerateFields(SPList list)
{
foreach (SPField field in list.Fields)
{
bool isVisible = true;
bool isWriteable = true;
// TODO: add logic to set visible/writeable for each field based on roles etc.
yield return new FieldToRender(field, isVisible, isWriteable);
}
}
private static IEnumerable<FieldToRender> StripNonVisibleFields(IEnumerable<FieldToRender> fields)
{
foreach (FieldToRender field in fields)
{
if (field.IsVisible) yield return field;
}
}
private Table CreateTopFormButtonBar(SPList list, bool hasWriteableFields)
{
return CreateFormButtonBar(list, "toolBarTbltop", hasWriteableFields);
}
private Table CreateBottomFormButtonBar(SPList list, bool hasWriteableFields)
{
return CreateFormButtonBar(list, "toolBarTblbottom", hasWriteableFields);
}
private Table CreateFormButtonBar(SPList list, string tableId, bool hasWriteableFields)
{
List<Control> buttons = new List<Control>();
if (hasWriteableFields)
{
SaveButton saveButton = new SaveButton();
saveButton.ControlMode = ControlMode;
saveButton.ListId = list.ID;
buttons.Add(saveButton);
}
GoBackButton cancelButton = new GoBackButton();
cancelButton.ControlMode = ControlMode;
cancelButton.ListId = list.ID;
buttons.Add(cancelButton);
Table table = CreateFormButtonBar(buttons.ToArray());
table.ID = tableId;
return table;
}
private static Table CreateFormButtonBar(params Control[] controls)
{
Table table = new Table();
table.CssClass = "ms-formtoolbar";
table.CellPadding = 2;
table.CellSpacing = 2;
table.BorderWidth = 0;
table.Width = new Unit(100, UnitType.Percentage);
TableRow row = new TableRow();
TableCell cell1 = new TableCell();
cell1.Width = new Unit(99, UnitType.Percentage);
cell1.CssClass = "ms-toolbar";
cell1.Attributes.Add("nowrap", "true");
row.Cells.Add(cell1);
Image image = new Image();
image.ImageUrl = "/_layouts/images/blank.gif";
image.Width = new Unit(1);
image.Height = new Unit(18);
cell1.Controls.Add(image);
table.Rows.Add(row);
foreach (Control control in controls)
{
TableCell containerCell = new TableCell();
containerCell.CssClass = "ms-toolbar";
containerCell.Attributes.Add("nowrap", "true");
row.Cells.Add(containerCell);
Table containerTable = new Table();
containerTable.CellPadding = 0;
containerTable.CellSpacing = 0;
containerTable.Width = new Unit(100, UnitType.Percentage);
TableRow row1 = new TableRow();
TableCell cell = new TableCell();
cell.HorizontalAlign = HorizontalAlign.Right;
cell.Width = new Unit(100, UnitType.Percentage);
cell.Attributes.Add("nowrap", "true");
cell.Controls.Add(control);
row1.Cells.Add(cell);
containerTable.Rows.Add(row1);
containerCell.Controls.Add(containerTable);
}
return table;
}
private IEnumerable<TableRow> CreateFieldControlRows(SPList list, IEnumerable<FieldToRender> fields)
{
foreach (FieldToRender fieldToRender in fields)
{
if (fieldToRender.IsWriteable)
{
yield return CreateLabelAndControlRowForField(list, fieldToRender.Field);
}
else
{
yield return CreateLabelAndReadOnlyControlForField(list, fieldToRender.Field);
}
}
}
private TableRow CreateLabelAndControlRowForField(SPList list, SPField field)
{
Control label = CreateFieldLabelControl(list, field);
Control formField = CreateFieldFormControl(list, field);
string fieldName = field.StaticName.Replace(" ", "_");
CustomTableRow row = new CustomTableRow();
row.ID = list.Title + "_" + fieldName + "_Row";
CustomTableCell cellLabel = new CustomTableCell();
row.Cells.Add(cellLabel);
cellLabel.ID = list.Title + "_" + fieldName + "_LabelCell";
CustomTableCell cellControl = new CustomTableCell();
row.Cells.Add(cellControl);
cellControl.ID = list.Title + "_" + fieldName + "_ControlCell";
cellLabel.Controls.Add(label);
cellControl.Controls.Add(formField);
if (!string.IsNullOrEmpty(field.Description))
{
Literal descriptionText = new Literal();
descriptionText.Text = "<span style=\"font-weight: normal;\">" +
HttpUtility.HtmlEncode(field.Description) + "</span>";
if (DescriptionsUnderLabels)
{
descriptionText.Text = "<br/>" + descriptionText.Text;
cellLabel.Controls.Add(descriptionText);
}
else
{
cellControl.Controls.Add(descriptionText);
}
}
cellLabel.CssClass = "ms-formlabel";
cellControl.CssClass = "ms-formbody";
return row;
}
private TableRow CreateLabelAndReadOnlyControlForField(SPList list, SPField field)
{
Control label = CreateFieldLabelControl(list, field);
Control formField = CreateReadOnlyFieldFormControl(list, field);
TableRow row = new TableRow();
TableCell cellLabel = new TableCell();
row.Cells.Add(cellLabel);
TableCell cellControl = new TableCell();
row.Cells.Add(cellControl);
cellLabel.Controls.Add(label);
cellControl.Controls.Add(formField);
cellLabel.CssClass = "ms-formlabel";
cellControl.CssClass = "ms-formbody";
return row;
}
private static Control CreateReadOnlyFieldFormControl(SPList list, SPField field)
{
FormField formField = new FormField();
formField.ControlMode = SPControlMode.Display;
formField.ListId = list.ID;
formField.FieldName = field.InternalName;
return formField;
}
private Control CreateFieldFormControl(SPList list, SPField field)
{
FormField formField = new FormField();
formField.ControlMode = ControlMode;
formField.ListId = list.ID;
formField.FieldName = field.InternalName;
if ((ControlMode == SPControlMode.New) && (field.Type == SPFieldType.DateTime))
{
}
return formField;
}
private FieldLabel CreateFieldLabelControl(SPList list, SPField field)
{
FieldLabel label = new FieldLabel();
label.ControlMode = ControlMode;
label.ListId = list.ID;
label.FieldName = field.InternalName;
return label;
}
}
</nowiki>
There is also a small support class:
<nowiki>
public class FieldToRender
{
private readonly SPField _field;
private bool _isVisible;
private bool _isWriteable;
public FieldToRender(SPField field, bool isVisible, bool isWriteable)
{
_field = field;
_isVisible = isVisible;
_isWriteable = isWriteable;
}
public bool IsVisible
{
get { return _isVisible; }
set { _isVisible = value; }
}
public bool IsWriteable
{
get { return _isWriteable; }
set { _isWriteable = value; }
}
public SPField Field
{
get { return _field; }
}
}
</nowiki>
Which is the payload yielded by the various field enumerators, to control which fields are visible/writeable.
EditUsage
When developing a site definition which gets deployed as a WSP, I've found that removing the original ListForm web part isn't all that easy (I'm sure there's a trick to it, but I haven't figured it out yet) - so beyond adding this web part to the appropriate WebPartZone, you have to suppress the existing contents of the ListForm web part - to do this I add an XmlDocuments section to the associated ContentType i.e.
<nowiki>
<XmlDocuments>
<XmlDocument NamespaceURI="http://schemas.microsoft.com/sharepoint/v3/contenttype/forms">
<FormTemplates xmlns="http://schemas.microsoft.com/sharepoint/v3/contenttype/forms">
<Edit>CustomListForm</Edit>
<New>CustomListForm</New>
</FormTemplates>
</XmlDocument>
</XmlDocuments>
</nowiki>
And then create a new file in the "\12\TEMPLATE\CONTROLTEMPLATES" folder i.e.
CustomListForm.ascx with the following content:
<nowiki>
<%@ Control Language="C#" AutoEventWireup="false" %>
<%@Assembly Name="Microsoft.SharePoint, Version=12.0.0.0, Culture=neutral, PublicKeyToken=71e9bce111e9429c" %>
<%@Register TagPrefix="SharePoint" Assembly="Microsoft.SharePoint, Version=12.0.0.0, Culture=neutral, PublicKeyToken=71e9bce111e9429c" namespace="Microsoft.SharePoint.WebControls"%>
<%@Register TagPrefix="SPHttpUtility" Assembly="Microsoft.SharePoint, Version=12.0.0.0, Culture=neutral, PublicKeyToken=71e9bce111e9429c" namespace="Microsoft.SharePoint.Utilities"%>
<%@ Register TagPrefix="wssuc" TagName="ToolBar" src="~/_controltemplates/ToolBar.ascx" %>
<%@ Register TagPrefix="wssuc" TagName="ToolBarButton" src="~/_controltemplates/ToolBarButton.ascx" %>
<SharePoint:RenderingTemplate ID="CustomListForm" runat="server">
<Template></Template>
</SharePoint:RenderingTemplate>
</nowiki>
Which stops the original ListForm from rendering any output.