BitterCoder's Wiki

Edit

Replacement 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.

Edit

The 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.

Edit

Usage

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.

ScrewTurn Wiki version 2.0.2. Some of the icons created by FamFamFam.